package fr.openwide.core.jpa.more.junit.difference; import java.util.Collection; import java.util.Map; import java.util.Map.Entry; import javax.annotation.Nonnull; import org.hamcrest.Description; import org.hamcrest.TypeSafeMatcher; import com.google.common.base.Function; import com.google.common.base.Objects; import com.google.common.collect.Iterables; import com.google.common.collect.MapDifference; import com.google.common.collect.MapDifference.ValueDifference; import fr.openwide.core.jpa.more.business.history.model.AbstractHistoryDifference; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; public class TestHistoryDifferenceCollectionMatcher<D extends AbstractHistoryDifference<?, ?>> extends TypeSafeMatcher<Collection<D>> { private static final Function<AbstractHistoryDifference<?, ?>, TestHistoryDifferenceKey> HISTORY_DIFFERENCE_TO_DIFFERENCE_KEY = new Function<AbstractHistoryDifference<?, ?>, TestHistoryDifferenceKey>() { @Override public TestHistoryDifferenceKey apply(@Nonnull AbstractHistoryDifference<?, ?> input) { return new TestHistoryDifferenceKey(input); } }; private static final Function<AbstractHistoryDifference<?, ?>, TestHistoryDifferenceDescription> HISTORY_DIFFERENCE_TO_DESCRIPTION = new Function<AbstractHistoryDifference<?, ?>, TestHistoryDifferenceDescription>() { @Override public TestHistoryDifferenceDescription apply(@Nonnull AbstractHistoryDifference<?, ?> input) { return new TestHistoryDifferenceDescription( input.getEventType(), Multimaps.transformValues( Multimaps.index(input.getDifferences(), HISTORY_DIFFERENCE_TO_DIFFERENCE_KEY), HISTORY_DIFFERENCE_TO_DESCRIPTION // Recursion. On suppose qu'il n'y a pas de cycle. ) ); } }; private final Multimap<TestHistoryDifferenceKey, TestHistoryDifferenceDescription> expected; public TestHistoryDifferenceCollectionMatcher(Multimap<TestHistoryDifferenceKey, TestHistoryDifferenceDescription> expected) { super(); this.expected = expected; } @Override public void describeTo(Description description) { description.appendValue(expected); } private Multimap<TestHistoryDifferenceKey, TestHistoryDifferenceDescription> description(Collection<D> collection) { return Multimaps.transformValues( Multimaps.index(collection, HISTORY_DIFFERENCE_TO_DIFFERENCE_KEY), HISTORY_DIFFERENCE_TO_DESCRIPTION ); } private final MapDifference<TestHistoryDifferenceKey, Collection<TestHistoryDifferenceDescription>> difference(Collection<D> item) { Multimap<TestHistoryDifferenceKey, TestHistoryDifferenceDescription> actual = description(item); return Maps.difference(expected.asMap(), actual.asMap()); } @Override protected boolean matchesSafely(Collection<D> item) { return difference(item).areEqual(); } @Override protected void describeMismatchSafely(Collection<D> item, Description mismatchDescription) { describeMismatchSafely("\n\t\t", difference(item), mismatchDescription); } protected void describeMismatchSafely(String prefix, MapDifference<TestHistoryDifferenceKey, Collection<TestHistoryDifferenceDescription>> difference, Description mismatchDescription) { appendIfNonEmpty(mismatchDescription, prefix, "missing: ", difference.entriesOnlyOnLeft()); appendIfNonEmpty(mismatchDescription, prefix, "unexpected: ", difference.entriesOnlyOnRight()); for (Entry<TestHistoryDifferenceKey, ValueDifference<Collection<TestHistoryDifferenceDescription>>> entryDiffering : difference.entriesDiffering().entrySet()) { mismatchDescription.appendText(prefix).appendText("differing from expected: ") .appendValue(entryDiffering.getKey()); ValueDifference<Collection<TestHistoryDifferenceDescription>> valueDifference = entryDiffering.getValue(); Collection<TestHistoryDifferenceDescription> expectedCollection = valueDifference.leftValue(); Collection<TestHistoryDifferenceDescription> actualCollection = valueDifference.rightValue(); String newPrefix = prefix + "\t\t"; if (expectedCollection.size() == 1 && actualCollection.size() == 1) { TestHistoryDifferenceDescription expected = Iterables.getOnlyElement(expectedCollection); TestHistoryDifferenceDescription actual = Iterables.getOnlyElement(actualCollection); describeMismatchSafely(newPrefix, expected, actual, mismatchDescription); } else { mismatchDescription.appendText(newPrefix).appendText("expected: ").appendValue(valueDifference.leftValue()); mismatchDescription.appendText(newPrefix).appendText("actual: ").appendValue(valueDifference.rightValue()); } } } protected void describeMismatchSafely(String prefix, TestHistoryDifferenceDescription expected, TestHistoryDifferenceDescription actual, Description mismatchDescription) { if (!Objects.equal(expected.getAction(), actual.getAction())) { mismatchDescription.appendText(prefix) .appendText("action differs: expected ").appendValue(expected.getAction()) .appendText(", actual is ").appendValue(actual.getAction()); } describeMismatchSafely(prefix, Maps.difference(expected.getDifferences().asMap(), actual.getDifferences().asMap()), mismatchDescription); } private static void appendIfNonEmpty(Description mismatchDescription, String prefix1, String prefix2, Map<?, ?> map) { for (Map.Entry<?, ?> entry : map.entrySet()) { mismatchDescription.appendText(prefix1).appendText(prefix2).appendValue(entry); } } }