From 7caa376bcb02cb28d596e1c1e532695b100b8981 Mon Sep 17 00:00:00 2001 From: Dimitry Ivanov Date: Sat, 25 Aug 2018 21:54:44 +0300 Subject: [PATCH] Add more tests --- .../visitor/SpannableMarkdownVisitorTest.java | 149 ++------------ .../renderer/visitor/TestValidator.java | 191 ++++++++++++++++++ .../src/test/resources/tests/bold-italic.yaml | 5 + .../tests/html-allow-non-closed-tags.yaml | 19 ++ .../tests/html-non-closed-ignore.yaml | 12 ++ .../test/resources/tests/no-paragraphs.yaml | 12 ++ .../src/test/resources/tests/ol-2-spaces.yaml | 16 ++ .../resources/tests/ol-starts-with-5.yaml | 14 ++ markwon/src/test/resources/tests/ol.yaml | 14 ++ .../src/test/resources/tests/paragraph.yaml | 12 ++ .../tests/soft-break-adds-new-line.yaml | 10 + .../src/test/resources/tests/soft-break.yaml | 7 + .../src/test/resources/tests/ul-levels.yaml | 20 ++ markwon/src/test/resources/tests/ul.yaml | 14 ++ 14 files changed, 358 insertions(+), 137 deletions(-) create mode 100644 markwon/src/test/java/ru/noties/markwon/renderer/visitor/TestValidator.java create mode 100644 markwon/src/test/resources/tests/bold-italic.yaml create mode 100644 markwon/src/test/resources/tests/html-allow-non-closed-tags.yaml create mode 100644 markwon/src/test/resources/tests/html-non-closed-ignore.yaml create mode 100644 markwon/src/test/resources/tests/no-paragraphs.yaml create mode 100644 markwon/src/test/resources/tests/ol-2-spaces.yaml create mode 100644 markwon/src/test/resources/tests/ol-starts-with-5.yaml create mode 100644 markwon/src/test/resources/tests/ol.yaml create mode 100644 markwon/src/test/resources/tests/paragraph.yaml create mode 100644 markwon/src/test/resources/tests/soft-break-adds-new-line.yaml create mode 100644 markwon/src/test/resources/tests/soft-break.yaml create mode 100644 markwon/src/test/resources/tests/ul-levels.yaml create mode 100644 markwon/src/test/resources/tests/ul.yaml diff --git a/markwon/src/test/java/ru/noties/markwon/renderer/visitor/SpannableMarkdownVisitorTest.java b/markwon/src/test/java/ru/noties/markwon/renderer/visitor/SpannableMarkdownVisitorTest.java index 1d2f7e73..9f123307 100644 --- a/markwon/src/test/java/ru/noties/markwon/renderer/visitor/SpannableMarkdownVisitorTest.java +++ b/markwon/src/test/java/ru/noties/markwon/renderer/visitor/SpannableMarkdownVisitorTest.java @@ -1,11 +1,9 @@ package ru.noties.markwon.renderer.visitor; import android.support.annotation.NonNull; -import android.support.annotation.Nullable; import android.text.SpannableStringBuilder; import org.commonmark.node.Node; -import org.junit.ComparisonFailure; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.ParameterizedRobolectricTestRunner; @@ -13,11 +11,7 @@ import org.robolectric.annotation.Config; import java.util.Arrays; import java.util.Collection; -import java.util.Comparator; -import java.util.Map; -import ix.Ix; -import ix.IxPredicate; import ru.noties.markwon.LinkResolverDef; import ru.noties.markwon.Markwon; import ru.noties.markwon.SpannableBuilder; @@ -28,8 +22,6 @@ import ru.noties.markwon.renderer.SpannableMarkdownVisitor; import ru.noties.markwon.spans.SpannableTheme; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; @RunWith(ParameterizedRobolectricTestRunner.class) @@ -60,99 +52,23 @@ public class SpannableMarkdownVisitorTest { final SpannableStringBuilder stringBuilder = builder.spannableStringBuilder(); -// System.out.printf("%s: %s%n", file, Arrays.toString(stringBuilder.getSpans(0, stringBuilder.length(), Object.class))); + final TestValidator validator = TestValidator.create(file); int index = 0; for (TestNode testNode : data.output()) { - index = validate(stringBuilder, index, testNode); + index = validator.validate(stringBuilder, index, testNode); } // assert that the whole thing is processed assertEquals(stringBuilder.length(), index); - } - private int validate(@NonNull final SpannableStringBuilder builder, final int index, @NonNull TestNode node) { + final Object[] spans = stringBuilder.getSpans(0, stringBuilder.length(), Object.class); + final int length = spans != null + ? spans.length + : 0; - if (node.isText()) { - - final String text; - { - final String content = node.getAsText().text(); - - // code is a special case as we wrap it around non-breakable spaces - final TestNode parent = node.parent(); - if (parent != null) { - final TestNode.Span span = parent.getAsSpan(); - if (TestSpan.CODE.equals(span.name())) { - text = "\u00a0" + content + "\u00a0"; - } else if (TestSpan.CODE_BLOCK.equals(span.name())) { - text = "\u00a0\n" + content + "\n\u00a0"; - } else { - text = content; - } - } else { - text = content; - } - } - - assertEquals(text, builder.subSequence(index, index + text.length()).toString()); - - return index + text.length(); - } - - final TestNode.Span span = node.getAsSpan(); - - int out = index; - - for (TestNode child : span.children()) { - out = validate(builder, out, child); - } - - final int end = out; - - final String info = node.toString(); - -// System.out.printf("%s: %s%n", file, builder.subSequence(index, out)); - - // we can possibly have parent spans here, should filter them - final Object[] spans = builder.getSpans(index, out, Object.class); - assertTrue(info, spans != null); - - final TestSpan testSpan = Ix.fromArray(spans) - .filter(new IxPredicate() { - @Override - public boolean test(Object o) { - return o instanceof TestSpan; - } - }) - .cast(TestSpan.class) - .filter(new IxPredicate() { - @Override - public boolean test(TestSpan testSpan) { - return span.name().equals(testSpan.name()) - && index == builder.getSpanStart(testSpan) - && end == builder.getSpanEnd(testSpan); - } - }) - .first(null); - - assertNotNull( - format("info: %s, spans: %s", info, Arrays.toString(spans)), - testSpan - ); - - assertEquals(info, span.name(), testSpan.name()); - - // for correct tracking of nested blocks we must validate expected start/end - assertEquals(info, index, builder.getSpanStart(testSpan)); - assertEquals(info, out, builder.getSpanEnd(testSpan)); - - System.out.printf("%s: expected: %s, actual: %s%n", file, span.attributes(), testSpan.attributes()); - - assertMapEquals(info, span.attributes(), testSpan.attributes()); - - return out; + assertEquals(Arrays.toString(spans), validator.processedSpanNodesCount(), length); } @SuppressWarnings("ConstantConditions") @@ -164,57 +80,16 @@ public class SpannableMarkdownVisitorTest { ? null : MarkwonHtmlParser.noOp(); - // todo: rest omitted for now + final boolean softBreakAddsNewLine = config.hasOption(TestConfig.SOFT_BREAK_ADDS_NEW_LINE); + final boolean htmlAllowNonClosedTags = config.hasOption(TestConfig.HTML_ALLOW_NON_CLOSED_TAGS); + return SpannableConfiguration.builder(null) .theme(mock(SpannableTheme.class)) .linkResolver(mock(LinkResolverDef.class)) .htmlParser(htmlParser) .factory(factory) + .softBreakAddsNewLine(softBreakAddsNewLine) + .htmlAllowNonClosedTags(htmlAllowNonClosedTags) .build(); } - - private static void assertMapEquals( - @NonNull String message, - @NonNull Map expected, - @NonNull Map actual) { - boolean result = expected.size() == actual.size(); - if (result) { - for (Map.Entry entry : expected.entrySet()) { - if (!actual.containsKey(entry.getKey()) - || !equals(entry.getValue(), actual.get(entry.getKey()))) { - result = false; - break; - } - } - } - if (!result) { - final Comparator> comparator = new Comparator>() { - @Override - public int compare(Map.Entry o1, Map.Entry o2) { - return o1.getKey().compareTo(o2.getKey()); - } - }; - final String e = Ix.from(expected.entrySet()) - .orderBy(comparator) - .toList() - .toString(); - final String a = Ix.from(actual.entrySet()) - .orderBy(comparator) - .toList() - .toString(); - throw new ComparisonFailure(message, e, a); - } - } - - private static boolean equals(@Nullable Object o1, @Nullable Object o2) { - return o1 != null - ? o1.equals(o2) - : o2 == null; - - } - - @NonNull - private static String format(@NonNull String message, Object... args) { - return String.format(message, args); - } } \ No newline at end of file diff --git a/markwon/src/test/java/ru/noties/markwon/renderer/visitor/TestValidator.java b/markwon/src/test/java/ru/noties/markwon/renderer/visitor/TestValidator.java new file mode 100644 index 00000000..f6ad246e --- /dev/null +++ b/markwon/src/test/java/ru/noties/markwon/renderer/visitor/TestValidator.java @@ -0,0 +1,191 @@ +package ru.noties.markwon.renderer.visitor; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.text.SpannableStringBuilder; +import android.text.Spanned; + +import java.util.Arrays; +import java.util.Map; + +import ix.Ix; +import ix.IxPredicate; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +abstract class TestValidator { + + abstract int validate( + @NonNull SpannableStringBuilder builder, + int index, + @NonNull TestNode node); + + abstract int processedSpanNodesCount(); + + + @NonNull + static TestValidator create(@NonNull String id) { + return new Impl(id); + } + + static class Impl extends TestValidator { + + private final String id; + + private int processedCount; + + Impl(@NonNull String id) { + this.id = id; + } + + @Override + int validate( + @NonNull final SpannableStringBuilder builder, + final int index, + @NonNull TestNode node) { + + if (node.isText()) { + + final String text; + { + final String content = node.getAsText().text(); + + // code is a special case as we wrap it around non-breakable spaces + final TestNode parent = node.parent(); + if (parent != null) { + final TestNode.Span span = parent.getAsSpan(); + if (TestSpan.CODE.equals(span.name())) { + text = "\u00a0" + content + "\u00a0"; + } else if (TestSpan.CODE_BLOCK.equals(span.name())) { + text = "\u00a0\n" + content + "\n\u00a0"; + } else { + text = content; + } + } else { + text = content; + } + } + + assertEquals(text, builder.subSequence(index, index + text.length()).toString()); + + return index + text.length(); + } + + final TestNode.Span span = node.getAsSpan(); + processedCount += 1; + + int out = index; + + for (TestNode child : span.children()) { + out = validate(builder, out, child); + } + + final int end = out; + + // we can possibly have parent spans here, should filter them + final Object[] spans = builder.getSpans(index, out, Object.class); + + // expected span{name, attributes} at position{start-end}, with text: `%s`, spans: [] + + + assertTrue( + message(span, index, end, builder, spans), + spans != null + ); + + final TestSpan testSpan = Ix.fromArray(spans) + .filter(new IxPredicate() { + @Override + public boolean test(Object o) { + return o instanceof TestSpan; + } + }) + .cast(TestSpan.class) + .filter(new IxPredicate() { + @Override + public boolean test(TestSpan testSpan) { + // in case of nested spans with the same name (lists) + // we also must validate attributes + // and thus we are moving most of assertions to this filter method + return span.name().equals(testSpan.name()) + && index == builder.getSpanStart(testSpan) + && end == builder.getSpanEnd(testSpan) + && mapEquals(span.attributes(), testSpan.attributes()); + } + }) + .first(null); + + assertNotNull( + message(span, index, end, builder, spans), + testSpan + ); + + return out; + } + + @Override + int processedSpanNodesCount() { + return processedCount; + } + + private static boolean mapEquals( + @NonNull Map expected, + @NonNull Map actual) { + + if (expected.size() != actual.size()) { + return false; + } + + boolean result = true; + + for (Map.Entry entry : expected.entrySet()) { + if (!actual.containsKey(entry.getKey()) + || !equals(entry.getValue(), actual.get(entry.getKey()))) { + result = false; + break; + } + } + + return result; + } + + private static boolean equals(@Nullable Object o1, @Nullable Object o2) { + return o1 != null + ? o1.equals(o2) + : o2 == null; + } + + @NonNull + private static String message( + @NonNull TestNode.Span span, + int start, + int end, + @NonNull Spanned text, + @Nullable Object[] spans) { + final String spansText; + if (spans == null + || spans.length == 0) { + spansText = "[]"; + } else { + final StringBuilder builder = new StringBuilder(); + for (Object o: spans) { + final TestSpan testSpan = (TestSpan) o; + if (builder.length() > 0) { + builder.append(", "); + } + builder + .append("{name: '").append(testSpan.name()).append('\'') + .append(", position{").append(start).append(", ").append(end).append('}') + .append(", attributes: ").append(testSpan.attributes()) + .append('}'); + } + spansText = builder.toString(); + } + return String.format("Expected span: %s at position{%d-%d} with text `%s`, spans: %s", + span, start, end, text.subSequence(start, end), spansText + ); + } + } +} diff --git a/markwon/src/test/resources/tests/bold-italic.yaml b/markwon/src/test/resources/tests/bold-italic.yaml new file mode 100644 index 00000000..d7d24682 --- /dev/null +++ b/markwon/src/test/resources/tests/bold-italic.yaml @@ -0,0 +1,5 @@ +input: "**_bold italic_**" + +output: + - b: + - i: "bold italic" \ No newline at end of file diff --git a/markwon/src/test/resources/tests/html-allow-non-closed-tags.yaml b/markwon/src/test/resources/tests/html-allow-non-closed-tags.yaml new file mode 100644 index 00000000..42578b6b --- /dev/null +++ b/markwon/src/test/resources/tests/html-allow-non-closed-tags.yaml @@ -0,0 +1,19 @@ +input: |- + italic + bold italic + underline bold italic + strike underline bold italic + +config: + use-html: true + html-allow-non-closed-tags: true + +output: + - i: + - text: "italic " + - b: + - text: "bold italic " + - u: + - text: "underline bold italic " + - s: + - text: "strike underline bold italic" \ No newline at end of file diff --git a/markwon/src/test/resources/tests/html-non-closed-ignore.yaml b/markwon/src/test/resources/tests/html-non-closed-ignore.yaml new file mode 100644 index 00000000..73c41ae4 --- /dev/null +++ b/markwon/src/test/resources/tests/html-non-closed-ignore.yaml @@ -0,0 +1,12 @@ +input: |- + no italic here + bold yeah + no underline + +config: + use-html: true + +output: + - text: "no italic here " + - b: "bold yeah" + - text: " no underline" \ No newline at end of file diff --git a/markwon/src/test/resources/tests/no-paragraphs.yaml b/markwon/src/test/resources/tests/no-paragraphs.yaml new file mode 100644 index 00000000..048f3ef4 --- /dev/null +++ b/markwon/src/test/resources/tests/no-paragraphs.yaml @@ -0,0 +1,12 @@ +input: |- + This could be a paragraph + + But it is not and this one is not also + +config: + use-paragraphs: false + +output: + - text: "This could be a paragraph" + - text: "\n\n" + - text: "But it is not and this one is not also" \ No newline at end of file diff --git a/markwon/src/test/resources/tests/ol-2-spaces.yaml b/markwon/src/test/resources/tests/ol-2-spaces.yaml new file mode 100644 index 00000000..0ffd7adb --- /dev/null +++ b/markwon/src/test/resources/tests/ol-2-spaces.yaml @@ -0,0 +1,16 @@ +description: "Will be rendered as simple flat list" + +input: |- + 1. First + 2. Second + 3. Third + +output: + - ol: "First" + start: 1 + - text: "\n" + - ol: "Second" + start: 2 + - text: "\n" + - ol: "Third" + start: 3 \ No newline at end of file diff --git a/markwon/src/test/resources/tests/ol-starts-with-5.yaml b/markwon/src/test/resources/tests/ol-starts-with-5.yaml new file mode 100644 index 00000000..eaabc7bb --- /dev/null +++ b/markwon/src/test/resources/tests/ol-starts-with-5.yaml @@ -0,0 +1,14 @@ +input: |- + 5. Five + 6. Six + 7. Seven + +output: + - ol: "Five" + start: 5 + - text: "\n" + - ol: "Six" + start: 6 + - text: "\n" + - ol: "Seven" + start: 7 \ No newline at end of file diff --git a/markwon/src/test/resources/tests/ol.yaml b/markwon/src/test/resources/tests/ol.yaml new file mode 100644 index 00000000..246b84ba --- /dev/null +++ b/markwon/src/test/resources/tests/ol.yaml @@ -0,0 +1,14 @@ +input: |- + 1. First + 1. Second + 1. Third + +output: + - ol: + - text: "First\n" + - ol: + - text: "Second\n" + - ol: "Third" + start: 1 + start: 1 + start: 1 \ No newline at end of file diff --git a/markwon/src/test/resources/tests/paragraph.yaml b/markwon/src/test/resources/tests/paragraph.yaml new file mode 100644 index 00000000..862bbc06 --- /dev/null +++ b/markwon/src/test/resources/tests/paragraph.yaml @@ -0,0 +1,12 @@ +input: |- + So, this is a paragraph + + And this one is another + +config: + use-paragraphs: true + +output: + - p: "So, this is a paragraph" + - text: "\n\n" + - p: "And this one is another" \ No newline at end of file diff --git a/markwon/src/test/resources/tests/soft-break-adds-new-line.yaml b/markwon/src/test/resources/tests/soft-break-adds-new-line.yaml new file mode 100644 index 00000000..4fbb7d0a --- /dev/null +++ b/markwon/src/test/resources/tests/soft-break-adds-new-line.yaml @@ -0,0 +1,10 @@ +input: |- + hello there! + this one is on the next line + hard break to the full extend + +config: + soft-break-adds-new-line: true + +output: + - text: "hello there!\nthis one is on the next line\nhard break to the full extend" \ No newline at end of file diff --git a/markwon/src/test/resources/tests/soft-break.yaml b/markwon/src/test/resources/tests/soft-break.yaml new file mode 100644 index 00000000..4406a5c7 --- /dev/null +++ b/markwon/src/test/resources/tests/soft-break.yaml @@ -0,0 +1,7 @@ +input: |- + First line + same line but with space between + this is also the first line + +output: + - text: "First line same line but with space between this is also the first line" \ No newline at end of file diff --git a/markwon/src/test/resources/tests/ul-levels.yaml b/markwon/src/test/resources/tests/ul-levels.yaml new file mode 100644 index 00000000..00708e17 --- /dev/null +++ b/markwon/src/test/resources/tests/ul-levels.yaml @@ -0,0 +1,20 @@ +input: |- + * First + * * Second + * * * Third + +output: + - ul: "First" + level: 0 + - text: "\n" + - ul: + - ul: "Second" + level: 1 + - text: "\n" + level: 0 + - ul: + - ul: + - ul: "Third" + level: 2 + level: 1 + level: 0 \ No newline at end of file diff --git a/markwon/src/test/resources/tests/ul.yaml b/markwon/src/test/resources/tests/ul.yaml new file mode 100644 index 00000000..171145da --- /dev/null +++ b/markwon/src/test/resources/tests/ul.yaml @@ -0,0 +1,14 @@ +input: |- + * First + * Second + * Third + +output: + - ul: + - text: "First\n" + - ul: + - text: "Second\n" + - ul: "Third" + level: 2 + level: 1 + level: 0 \ No newline at end of file