diff options
| author | Michael Williamson <mike@zwobble.org> | 2016-01-02 15:17:50 +0000 |
|---|---|---|
| committer | Michael Williamson <mike@zwobble.org> | 2016-01-02 15:18:22 +0000 |
| commit | d5005305b90afe47e639fc0fa1430242556f0385 (patch) | |
| tree | 387c8e888b925917da79e1ad829e14665c7b8cc6 /src/test/java | |
| parent | 70edd90a3077fc63a5890c4d7d55561ba0274902 (diff) | |
Add possibly ill-advised DeepReflectionMatcher
Diffstat (limited to 'src/test/java')
| -rw-r--r-- | src/test/java/org/zwobble/mammoth/tests/DeepReflectionMatcher.java | 136 | ||||
| -rw-r--r-- | src/test/java/org/zwobble/mammoth/tests/docx/BodyXmlTests.java | 39 |
2 files changed, 159 insertions, 16 deletions
diff --git a/src/test/java/org/zwobble/mammoth/tests/DeepReflectionMatcher.java b/src/test/java/org/zwobble/mammoth/tests/DeepReflectionMatcher.java new file mode 100644 index 0000000..8ee0d8f --- /dev/null +++ b/src/test/java/org/zwobble/mammoth/tests/DeepReflectionMatcher.java @@ -0,0 +1,136 @@ +package org.zwobble.mammoth.tests; + +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.Matchers; +import org.hamcrest.TypeSafeDiagnosingMatcher; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.List; + +import static com.google.common.collect.Iterables.filter; +import static com.google.common.collect.Iterables.transform; +import static java.util.Arrays.asList; + +public class DeepReflectionMatcher<T> extends TypeSafeDiagnosingMatcher<T> { + private final T expected; + + public DeepReflectionMatcher(T expected) { + this.expected = expected; + } + + @Override + protected boolean matchesSafely(T item, Description mismatchDescription) { + return matchesSafely("", expected, item, mismatchDescription); + } + + private static <T> boolean matchesSafely(String path, T expected, T actual, Description mismatchDescription) { + if (expected instanceof List && actual instanceof List) { + return matchesList(path, (List)expected, (List)actual, mismatchDescription); + } + + if (!expected.getClass().equals(actual.getClass())) { + mismatchDescription.appendText("was " + actual.getClass().getName()); + return false; + } + + if (expected instanceof String) { + return matchesString(path, expected, actual, mismatchDescription); + } + + for (Field field : fields(expected.getClass())) { + if (!matchesSafely(path + "." + field.getName(), readField(expected, field), readField(actual, field), mismatchDescription)) { + return false; + } + } + + return true; + } + + private static <T> boolean matchesList(String path, List<?> expected, List<?> actual, Description mismatchDescription) { + if (actual.size() > expected.size()) { + appendPath(mismatchDescription, path); + mismatchDescription.appendText("extra elements:" + + indentedList(transform(Iterables.skip(actual, expected.size()), DeepReflectionMatcher::generateDescriptionOfValue))); + return false; + } + + if (actual.size() < expected.size()) { + appendPath(mismatchDescription, path); + mismatchDescription.appendText("missing elements:" + + indentedList(transform(Iterables.skip(expected, actual.size()), DeepReflectionMatcher::generateDescriptionOfValue))); + return false; + } + + for (int index = 0; index < expected.size(); index++) { + if (!matchesSafely(path + "[" + index + "]", expected.get(index), actual.get(index), mismatchDescription)) { + return false; + } + } + + return true; + } + + private static <T> boolean matchesString(String path, T expected, T actual, Description mismatchDescription) { + Matcher<Object> matcher = Matchers.equalTo(expected); + if (!matcher.matches(actual)) { + appendPath(mismatchDescription, path); + matcher.describeMismatch(actual, mismatchDescription); + return false; + } else { + return true; + } + } + + private static void appendPath(Description mismatchDescription, String path) { + mismatchDescription.appendText(path + ": "); + } + + @Override + public void describeTo(Description description) { + description.appendText(generateDescriptionOfValue(expected)); + } + + private static String generateDescriptionOfValue(Object value) { + if (value instanceof String) { + return value.toString(); + } else if (value instanceof List) { + List<?> list = (List)value; + return "[" + indentedList(transform(list, DeepReflectionMatcher::generateDescriptionOfValue)) + "]"; + } else { + Class<?> clazz = value.getClass(); + List<Field> fields = fields(clazz); + Iterable<String> fieldStrings = transform( + fields, + field -> field.getName() + "=" + generateDescriptionOfValue(readField(value, field))); + return String.format("%s(%s)", clazz.getSimpleName(), indentedList(fieldStrings)); + } + } + + private static String indentedList(Iterable<String> values) { + return Joiner.on(",").join(transform(values, value -> "\n " + indent(value))); + } + + private static String indent(String value) { + return value.replaceAll("\n", "\n "); + } + + private static ImmutableList<Field> fields(Class<?> clazz) { + return ImmutableList.copyOf(filter( + asList(clazz.getDeclaredFields()), + field -> !Modifier.isStatic(field.getModifiers()))); + } + + private static Object readField(Object obj, Field field) { + try { + field.setAccessible(true); + return field.get(obj); + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } + } +} diff --git a/src/test/java/org/zwobble/mammoth/tests/docx/BodyXmlTests.java b/src/test/java/org/zwobble/mammoth/tests/docx/BodyXmlTests.java index 8ab2dd8..e2ee5a9 100644 --- a/src/test/java/org/zwobble/mammoth/tests/docx/BodyXmlTests.java +++ b/src/test/java/org/zwobble/mammoth/tests/docx/BodyXmlTests.java @@ -7,16 +7,17 @@ import org.zwobble.mammoth.documents.DocumentElement; import org.zwobble.mammoth.documents.ParagraphElement; import org.zwobble.mammoth.documents.RunElement; import org.zwobble.mammoth.documents.TextElement; +import org.zwobble.mammoth.tests.DeepReflectionMatcher; import org.zwobble.mammoth.xml.XmlElement; import org.zwobble.mammoth.xml.XmlNode; +import org.zwobble.mammoth.xml.XmlNodes; import java.util.List; +import static java.util.Arrays.asList; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; import static org.zwobble.mammoth.docx.BodyXml.readBodyXmlElement; import static org.zwobble.mammoth.xml.XmlNodes.element; -import static org.zwobble.mammoth.xml.XmlNodes.text; public class BodyXmlTests { @Test @@ -30,7 +31,7 @@ public class BodyXmlTests { XmlElement element = runXml(ImmutableList.of(textXml("Hello!"))); assertThat( readBodyXmlElement(element), - isRunElement(isTextElement("Hello!"))); + isRun(run(text("Hello!")))); } @Test @@ -38,7 +39,7 @@ public class BodyXmlTests { XmlElement element = paragraphXml(ImmutableList.of(runXml(ImmutableList.of(textXml("Hello!"))))); assertThat( readBodyXmlElement(element), - isParagraphElement(isRunElement(isTextElement("Hello!")))); + isParagraph(paragraph(run(text("Hello!"))))); } private XmlElement paragraphXml(List<XmlNode> children) { @@ -50,24 +51,30 @@ public class BodyXmlTests { } private XmlElement textXml(String value) { - return element("w:t", ImmutableList.of(text(value))); + return element("w:t", ImmutableList.of(XmlNodes.text(value))); } - private Matcher<DocumentElement> isParagraphElement(Matcher<DocumentElement> child) { - return allOf( - instanceOf(ParagraphElement.class), - hasProperty("children", contains(child))); + private Matcher<DocumentElement> isParagraph(ParagraphElement expected) { + return new DeepReflectionMatcher<>(expected); } - private Matcher<DocumentElement> isRunElement(Matcher<DocumentElement> child) { - return allOf( - instanceOf(RunElement.class), - hasProperty("children", contains(child))); + private Matcher<DocumentElement> isRun(RunElement expected) { + return new DeepReflectionMatcher<>(expected); } private Matcher<DocumentElement> isTextElement(String value) { - return allOf( - instanceOf(TextElement.class), - hasProperty("value", equalTo(value))); + return new DeepReflectionMatcher<>(new TextElement(value)); + } + + private ParagraphElement paragraph(DocumentElement... children) { + return new ParagraphElement(asList(children)); + } + + private RunElement run(DocumentElement... children) { + return new RunElement(asList(children)); + } + + private TextElement text(String value) { + return new TextElement(value); } } |
