Html parser impl tests

This commit is contained in:
Dimitry Ivanov 2018-08-18 14:55:07 +03:00
parent 76fdbabad0
commit 203f5fae52
8 changed files with 560 additions and 66 deletions

View File

@ -9,9 +9,12 @@ import java.util.Map;
/**
* @see Inline
* @see Block
* @since 2.0.0
*/
public interface HtmlTag {
int NO_END = -1;
/**
* @return normalized tag name (lower-case)
*/
@ -33,6 +36,12 @@ public interface HtmlTag {
*/
boolean isEmpty();
/**
* @return flag indicating if this tag is closed (has valid start and end)
* @see #NO_END
*/
boolean isClosed();
@NonNull
Map<String, String> attributes();
@ -59,5 +68,11 @@ public interface HtmlTag {
*/
@NonNull
List<Block> children();
/**
* @return a flag indicating if this {@link Block} is at the root level (shortcut to calling:
* {@code parent() == null}
*/
boolean isRoot();
}
}

View File

@ -4,8 +4,14 @@ import android.support.annotation.NonNull;
import java.util.List;
/**
* @since 2.0.0
*/
public abstract class MarkwonHtmlParser {
/**
* Factory method to create a `no-op` implementation (no parsing)
*/
@NonNull
public static MarkwonHtmlParser noOp() {
return new MarkwonHtmlParserNoOp();
@ -19,14 +25,32 @@ public abstract class MarkwonHtmlParser {
@NonNull T output,
@NonNull String htmlFragment);
// clear all pending tags (if any)
// todo: we also can do this: if supplied value is -1 (for example) we ignore tags that are not closed
/**
* After this method exists a {@link MarkwonHtmlParser} will clear internal state for stored tags.
* If you wish to process them further after this method exists create own copy of supplied
* collection.
*
* @param documentLength known document length. This value is used to close all non-closed tags.
* If you wish to keep them open (do not force close at the end of a
* document pass here {@link HtmlTag#NO_END}. Later non-closed tags
* can be detected by calling {@link HtmlTag#isClosed()}
* @param action {@link FlushAction} to be called with resulting tags ({@link ru.noties.markwon.html.api.HtmlTag.Inline})
*/
public abstract void flushInlineTags(
int documentLength,
@NonNull FlushAction<HtmlTag.Inline> action);
// clear all pending blocks if any
// todo: we also can do this: if supplied value is -1 (for example) we ignore tags that are not closed
/**
* After this method exists a {@link MarkwonHtmlParser} will clear internal state for stored tags.
* If you wish to process them further after this method exists create own copy of supplied
* collection.
*
* @param documentLength known document length. This value is used to close all non-closed tags.
* If you wish to keep them open (do not force close at the end of a
* document pass here {@link HtmlTag#NO_END}. Later non-closed tags
* can be detected by calling {@link HtmlTag#isClosed()}
* @param action {@link FlushAction} to be called with resulting tags ({@link ru.noties.markwon.html.api.HtmlTag.Block})
*/
public abstract void flushBlockTags(
int documentLength,
@NonNull FlushAction<HtmlTag.Block> action);

View File

@ -11,6 +11,8 @@ import ru.noties.markwon.html.impl.jsoup.parser.Token;
* _void_ tags and tags that are self-closed (even if HTML spec doesn\'t specify
* a tag as self-closed). This is due to the fact that underlying parser does not
* validate context and does not check if a tag is correctly used.
*
* @since 2.0.0
*/
public class HtmlEmptyTagReplacement {

View File

@ -11,12 +11,10 @@ import ru.noties.markwon.html.api.HtmlTag;
abstract class HtmlTagImpl implements HtmlTag {
private static final int NO_VALUE = -1;
final String name;
final int start;
final Map<String, String> attributes;
int end = NO_VALUE;
int end = NO_END;
protected HtmlTagImpl(@NonNull String name, int start, @NonNull Map<String, String> attributes) {
this.name = name;
@ -51,8 +49,9 @@ abstract class HtmlTagImpl implements HtmlTag {
return attributes;
}
boolean isClosed() {
return end > NO_VALUE;
@Override
public boolean isClosed() {
return end > NO_END;
}
abstract void closeAt(int end);
@ -86,8 +85,7 @@ abstract class HtmlTagImpl implements HtmlTag {
@NonNull
static BlockImpl root() {
//noinspection ConstantConditions
return new BlockImpl("", 0, null, null);
return new BlockImpl("", 0, Collections.<String, String>emptyMap(), null);
}
@NonNull
@ -95,7 +93,7 @@ abstract class HtmlTagImpl implements HtmlTag {
@NonNull String name,
int start,
@NonNull Map<String, String> attributes,
@NonNull BlockImpl parent) {
@Nullable BlockImpl parent) {
return new BlockImpl(name, start, attributes, parent);
}
@ -107,7 +105,7 @@ abstract class HtmlTagImpl implements HtmlTag {
@NonNull String name,
int start,
@NonNull Map<String, String> attributes,
@NonNull BlockImpl parent) {
@Nullable BlockImpl parent) {
super(name, start, attributes);
this.parent = parent;
}
@ -120,42 +118,36 @@ abstract class HtmlTagImpl implements HtmlTag {
for (BlockImpl child : children) {
child.closeAt(end);
}
children = Collections.unmodifiableList(children);
} else {
children = Collections.emptyList();
}
}
}
boolean isRoot() {
@Override
public boolean isRoot() {
return parent == null;
}
@Nullable
@Override
public Block parent() {
if (parent == null) {
throw new IllegalStateException("#parent() getter was called on the root node " +
"which should not be exposed outside internal usage");
}
return parent;
}
@NonNull
@Override
public List<Block> children() {
//noinspection unchecked
return (List<Block>) (List<? extends Block>) children;
final List<Block> list;
if (children == null) {
list = Collections.emptyList();
} else {
list = Collections.unmodifiableList((List<? extends Block>) children);
}
return list;
}
@NonNull
@Override
public Map<String, String> attributes() {
//noinspection ConstantConditions
if (attributes == null) {
throw new IllegalStateException("#attributes() getter was called on the root node " +
"which should not be exposed outside internal usage");
}
return attributes;
}

View File

@ -14,6 +14,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.Set;
import ru.noties.markwon.html.api.HtmlTag;
import ru.noties.markwon.html.api.HtmlTag.Block;
import ru.noties.markwon.html.api.HtmlTag.Inline;
import ru.noties.markwon.html.api.MarkwonHtmlParser;
@ -24,6 +25,9 @@ import ru.noties.markwon.html.impl.jsoup.parser.ParseErrorList;
import ru.noties.markwon.html.impl.jsoup.parser.Token;
import ru.noties.markwon.html.impl.jsoup.parser.Tokeniser;
/**
* @since 2.0.0
*/
public class MarkwonHtmlParserImpl extends MarkwonHtmlParser {
@NonNull
@ -173,12 +177,18 @@ public class MarkwonHtmlParserImpl extends MarkwonHtmlParser {
@Override
public void flushInlineTags(int documentLength, @NonNull FlushAction<Inline> action) {
if (inlineTags.size() > 0) {
if (documentLength > HtmlTag.NO_END) {
for (HtmlTagImpl.InlineImpl inline : inlineTags) {
inline.closeAt(documentLength);
}
}
//noinspection unchecked
action.apply(Collections.unmodifiableList((List<? extends Inline>) inlineTags));
inlineTags.clear();
} else {
action.apply(Collections.<Inline>emptyList());
}
}
@ -186,15 +196,19 @@ public class MarkwonHtmlParserImpl extends MarkwonHtmlParser {
public void flushBlockTags(int documentLength, @NonNull FlushAction<Block> action) {
HtmlTagImpl.BlockImpl block = currentBlock;
while (!block.isRoot()) {
while (block.parent != null) {
block = block.parent;
}
if (documentLength > HtmlTag.NO_END) {
block.closeAt(documentLength);
}
final List<Block> children = block.children();
if (children.size() > 0) {
action.apply(children);
} else {
action.apply(Collections.<Block>emptyList());
}
currentBlock = HtmlTagImpl.BlockImpl.root();

View File

@ -222,7 +222,7 @@ public abstract class Token {
}
public final static class StartTag extends Tag {
StartTag() {
public StartTag() {
super(TokenType.StartTag);
attributes = new Attributes();
}

View File

@ -0,0 +1,42 @@
package ru.noties.markwon.html.impl;
import org.junit.Before;
import org.junit.Test;
import ru.noties.markwon.html.impl.jsoup.nodes.Attributes;
import ru.noties.markwon.html.impl.jsoup.parser.Token;
import static org.junit.Assert.assertEquals;
public class HtmlEmptyTagReplacementTest {
private HtmlEmptyTagReplacement replacement;
@Before
public void before() {
replacement = HtmlEmptyTagReplacement.create();
}
@Test
public void imageReplacementNoAlt() {
final Token.StartTag startTag = new Token.StartTag();
startTag.normalName = "img";
assertEquals("\uFFFC", replacement.replace(startTag));
}
@Test
public void imageReplacementAlt() {
final Token.StartTag startTag = new Token.StartTag();
startTag.normalName = "img";
startTag.attributes = new Attributes().put("alt", "alternative27");
assertEquals("alternative27", replacement.replace(startTag));
}
@Test
public void brAddsNewLine() {
final Token.StartTag startTag = new Token.StartTag();
startTag.normalName = "br";
startTag.selfClosing = true;
assertEquals("\n", replacement.replace(startTag));
}
}

View File

@ -12,15 +12,15 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import ru.noties.markwon.html.api.HtmlTag;
import ru.noties.markwon.html.api.MarkwonHtmlParser;
import ru.noties.markwon.html.impl.HtmlEmptyTagReplacement;
import ru.noties.markwon.html.impl.MarkwonHtmlParserImpl;
import ru.noties.markwon.html.impl.jsoup.parser.Token;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@RunWith(RobolectricTestRunner.class)
@ -257,11 +257,11 @@ public class MarkwonHtmlParserImplTest {
// tag names must be lower cased
final Set<String> set = new HashSet<>(tags.size());
for (String tag: tags) {
for (String tag : tags) {
set.add(tag.toLowerCase());
}
for (HtmlTag.Block block: blocks) {
for (HtmlTag.Block block : blocks) {
assertTrue(block.name(), block.isEmpty());
assertTrue(set.remove(block.name()));
}
@ -301,7 +301,7 @@ public class MarkwonHtmlParserImplTest {
});
final StringBuilder html = new StringBuilder();
for (String tag: tags) {
for (String tag : tags) {
html.append('<')
.append(tag)
.append('>')
@ -327,7 +327,7 @@ public class MarkwonHtmlParserImplTest {
final Set<String> set = new HashSet<>(tags);
boolean first = true;
for (HtmlTag.Block block: blocks) {
for (HtmlTag.Block block : blocks) {
assertEquals(block.name(), block.name(), output.substring(block.start(), block.end()));
if (first) {
first = false;
@ -342,58 +342,455 @@ public class MarkwonHtmlParserImplTest {
@Test
public void multipleFragmentsContinuation() {
throw new RuntimeException();
final MarkwonHtmlParserImpl impl = new MarkwonHtmlParserImpl(new HtmlEmptyTagReplacement());
final StringBuilder output = new StringBuilder();
impl.processFragment(output, "<i>");
output.append("italic ");
impl.processFragment(output, "</i>");
final CaptureInlineTagsAction action = new CaptureInlineTagsAction();
impl.flushInlineTags(output.length(), action);
assertTrue(action.called);
final List<HtmlTag.Inline> inlines = action.tags;
assertEquals(inlines.toString(), 1, inlines.size());
final HtmlTag.Inline inline = inlines.get(0);
assertEquals("i", inline.name());
assertEquals(0, inline.start());
assertEquals(output.length(), inline.end());
assertEquals("italic ", output.toString());
}
@Test
public void paragraphCannotContainAnythingButInlines() {
throw new RuntimeException();
}
// move to htmlInlineTagreplacement test class
@Test
public void imageReplacementNoAlt() {
throw new RuntimeException();
}
final MarkwonHtmlParserImpl impl = MarkwonHtmlParserImpl.create();
@Test
public void brAddsNewLine() {
throw new RuntimeException();
}
final StringBuilder output = new StringBuilder();
@Test
public void imageReplacementAlt() {
throw new RuntimeException();
impl.processFragment(output, "<p><i>italic <b>bold italic <div>in-div</div>");
final CaptureInlineTagsAction inlineTagsAction = new CaptureInlineTagsAction();
final CaptureBlockTagsAction blockTagsAction = new CaptureBlockTagsAction();
impl.flushInlineTags(output.length(), inlineTagsAction);
impl.flushBlockTags(output.length(), blockTagsAction);
assertTrue(inlineTagsAction.called);
assertTrue(blockTagsAction.called);
final List<HtmlTag.Inline> inlines = inlineTagsAction.tags;
final List<HtmlTag.Block> blocks = blockTagsAction.tags;
assertEquals(2, inlines.size());
assertEquals(2, blocks.size());
// inlines will be closed at the end of the document
// P will be closed right before <div>
with(inlines.get(0), new Action<HtmlTag.Inline>() {
@Override
public void apply(@NonNull HtmlTag.Inline inline) {
assertEquals("i", inline.name());
assertEquals(0, inline.start());
assertEquals(output.length(), inline.end());
}
});
with(inlines.get(1), new Action<HtmlTag.Inline>() {
@Override
public void apply(@NonNull HtmlTag.Inline inline) {
assertEquals("b", inline.name());
assertEquals("italic ".length(), inline.start());
assertEquals(output.length(), inline.end());
}
});
with(blocks.get(0), new Action<HtmlTag.Block>() {
@Override
public void apply(@NonNull HtmlTag.Block block) {
assertEquals("p", block.name());
assertEquals(0, block.start());
assertEquals(output.indexOf("in-div") - 1, block.end());
}
});
with(blocks.get(1), new Action<HtmlTag.Block>() {
@Override
public void apply(@NonNull HtmlTag.Block block) {
assertEquals("div", block.name());
assertEquals(output.indexOf("in-div"), block.start());
assertEquals(output.length(), block.end());
}
});
}
@Test
public void blockCloseClosesChildren() {
throw new RuntimeException();
}
@Test
public void allReturnedTagsAreClosed() {
throw new RuntimeException();
final MarkwonHtmlParserImpl impl = MarkwonHtmlParserImpl.create();
final StringBuilder output = new StringBuilder();
final String html = "<div-1>1<div-2>2<div-3>hello!</div-1>";
impl.processFragment(output, html);
assertEquals("12hello!", output.toString());
final CaptureBlockTagsAction action = new CaptureBlockTagsAction();
impl.flushBlockTags(output.length(), action);
assertTrue(action.called);
assertEquals(1, action.tags.size());
with(action.tags.get(0), new Action<HtmlTag.Block>() {
@Override
public void apply(@NonNull HtmlTag.Block block) {
final int end = output.length();
assertEquals("div-1", block.name());
assertEquals(0, block.start());
assertEquals(end, block.end());
assertEquals(1, block.children().size());
with(block.children().get(0), new Action<HtmlTag.Block>() {
@Override
public void apply(@NonNull HtmlTag.Block block) {
assertEquals("div-2", block.name());
assertEquals(1, block.start());
assertEquals(end, block.end());
assertEquals(1, block.children().size());
with(block.children().get(0), new Action<HtmlTag.Block>() {
@Override
public void apply(@NonNull HtmlTag.Block block) {
assertEquals("div-3", block.name());
assertEquals(2, block.start());
assertEquals(end, block.end());
assertEquals(0, block.children().size());
}
});
}
});
}
});
}
@Test
public void allTagsAreLowerCase() {
throw new RuntimeException();
final MarkwonHtmlParserImpl impl = MarkwonHtmlParserImpl.create();
final StringBuilder output = new StringBuilder();
impl.processFragment(output, "<DiV><I>italic <eM>emphasis</Em> italic</i></dIv>");
final CaptureInlineTagsAction inlineTagsAction = new CaptureInlineTagsAction();
final CaptureBlockTagsAction blockTagsAction = new CaptureBlockTagsAction();
impl.flushInlineTags(output.length(), inlineTagsAction);
impl.flushBlockTags(output.length(), blockTagsAction);
assertTrue(inlineTagsAction.called);
assertTrue(blockTagsAction.called);
with(inlineTagsAction.tags, new Action<List<HtmlTag.Inline>>() {
@Override
public void apply(@NonNull List<HtmlTag.Inline> inlines) {
assertEquals(2, inlines.size());
with(inlines.get(0), new Action<HtmlTag.Inline>() {
@Override
public void apply(@NonNull HtmlTag.Inline inline) {
assertEquals("i", inline.name());
assertEquals(0, inline.start());
assertEquals(output.length(), inline.end());
}
});
with(inlines.get(1), new Action<HtmlTag.Inline>() {
@Override
public void apply(@NonNull HtmlTag.Inline inline) {
assertEquals("em", inline.name());
final int start = "italic ".length();
assertEquals(start, inline.start());
assertEquals(start + ("emphasis".length()), inline.end());
}
});
}
});
assertEquals(1, blockTagsAction.tags.size());
with(blockTagsAction.tags.get(0), new Action<HtmlTag.Block>() {
@Override
public void apply(@NonNull HtmlTag.Block block) {
assertEquals("div", block.name());
assertEquals(0, block.start());
assertEquals(output.length(), block.end());
}
});
}
@Test
public void previousListItemClosed() {
throw new RuntimeException();
}
@Test
public void nestedBlocks() {
throw new RuntimeException();
final MarkwonHtmlParserImpl impl = MarkwonHtmlParserImpl.create();
final StringBuilder output = new StringBuilder();
final String html = "<ul><li>UL-First<li>UL-Second<ol><li>OL-First<li>OL-Second</ol><li>UL-Third";
impl.processFragment(output, html);
final CaptureBlockTagsAction action = new CaptureBlockTagsAction();
impl.flushBlockTags(output.length(), action);
assertTrue(action.called);
assertEquals(1, action.tags.size());
with(action.tags.get(0), new Action<HtmlTag.Block>() {
@Override
public void apply(@NonNull HtmlTag.Block block) {
assertEquals("ul", block.name());
assertEquals(3, block.children().size());
with(block.children().get(0), new Action<HtmlTag.Block>() {
@Override
public void apply(@NonNull HtmlTag.Block block) {
assertEquals("li", block.name());
assertEquals("UL-First", output.substring(block.start(), block.end()));
assertEquals(0, block.children().size());
}
});
with(block.children().get(1), new Action<HtmlTag.Block>() {
@Override
public void apply(@NonNull HtmlTag.Block block) {
assertEquals("li", block.name());
// this block will contain nested block text also
assertEquals("UL-Second\nOL-First\nOL-Second", output.substring(block.start(), block.end()));
assertEquals(1, block.children().size());
with(block.children().get(0), new Action<HtmlTag.Block>() {
@Override
public void apply(@NonNull HtmlTag.Block block) {
assertEquals("ol", block.name());
assertEquals(2, block.children().size());
with(block.children().get(0), new Action<HtmlTag.Block>() {
@Override
public void apply(@NonNull HtmlTag.Block block) {
assertEquals("li", block.name());
assertEquals("OL-First", output.substring(block.start(), block.end()));
assertEquals(0, block.children().size());
}
});
with(block.children().get(1), new Action<HtmlTag.Block>() {
@Override
public void apply(@NonNull HtmlTag.Block block) {
assertEquals("li", block.name());
assertEquals("OL-Second", output.substring(block.start(), block.end()));
assertEquals(0, block.children().size());
}
});
}
});
}
});
with(block.children().get(2), new Action<HtmlTag.Block>() {
@Override
public void apply(@NonNull HtmlTag.Block block) {
assertEquals("li", block.name());
assertEquals("UL-Third", output.substring(block.start(), block.end()));
assertEquals(0, block.children().size());
}
});
}
});
}
@Test
public void attributes() {
throw new RuntimeException();
final MarkwonHtmlParserImpl impl = MarkwonHtmlParserImpl.create();
final StringBuilder output = new StringBuilder();
impl.processFragment(output, "<my-tag " +
"name=no-name " +
":click='doSomething' " +
"@focus=\"focus\" " +
"@blur.native=\"blur\" " +
"android:id=\"@id/id\">my-content</my-tag>");
final CaptureBlockTagsAction action = new CaptureBlockTagsAction();
impl.flushBlockTags(output.length(), action);
assertTrue(action.called);
assertEquals(1, action.tags.size());
with(action.tags.get(0), new Action<HtmlTag.Block>() {
@Override
public void apply(@NonNull HtmlTag.Block block) {
assertEquals("my-tag", block.name());
with(block.attributes(), new Action<Map<String, String>>() {
@Override
public void apply(@NonNull Map<String, String> attributes) {
assertEquals(5, attributes.size());
assertEquals("no-name", attributes.get("name"));
assertEquals("doSomething", attributes.get(":click"));
assertEquals("focus", attributes.get("@focus"));
assertEquals("blur", attributes.get("@blur.native"));
assertEquals("@id/id", attributes.get("android:id"));
}
});
}
});
}
@Test
public void flushCloseTagsIfRequested() {
final MarkwonHtmlParserImpl impl = MarkwonHtmlParserImpl.create();
final StringBuilder output = new StringBuilder();
impl.processFragment(output, "<div><i><b><em><strong>divibemstrong");
final int end = output.length();
final CaptureInlineTagsAction inlineTagsAction = new CaptureInlineTagsAction();
final CaptureBlockTagsAction blockTagsAction = new CaptureBlockTagsAction();
impl.flushInlineTags(end, inlineTagsAction);
impl.flushBlockTags(end, blockTagsAction);
assertTrue(inlineTagsAction.called);
assertTrue(blockTagsAction.called);
with(inlineTagsAction.tags, new Action<List<HtmlTag.Inline>>() {
@Override
public void apply(@NonNull List<HtmlTag.Inline> inlines) {
assertEquals(4, inlines.size());
for (HtmlTag.Inline inline : inlines) {
assertTrue(inline.isClosed());
assertEquals(end, inline.end());
}
}
});
assertEquals(1, blockTagsAction.tags.size());
with(blockTagsAction.tags.get(0), new Action<HtmlTag.Block>() {
@Override
public void apply(@NonNull HtmlTag.Block block) {
assertTrue(block.isClosed());
assertEquals(end, block.end());
}
});
}
@Test
public void flushDoesNotCloseTagsIfNoEndRequested() {
final MarkwonHtmlParserImpl impl = MarkwonHtmlParserImpl.create();
final StringBuilder output = new StringBuilder();
impl.processFragment(output, "<div><i><b><em><strong>divibemstrong");
final CaptureInlineTagsAction inlineTagsAction = new CaptureInlineTagsAction();
final CaptureBlockTagsAction blockTagsAction = new CaptureBlockTagsAction();
impl.flushInlineTags(HtmlTag.NO_END, inlineTagsAction);
impl.flushBlockTags(HtmlTag.NO_END, blockTagsAction);
assertTrue(inlineTagsAction.called);
assertTrue(blockTagsAction.called);
with(inlineTagsAction.tags, new Action<List<HtmlTag.Inline>>() {
@Override
public void apply(@NonNull List<HtmlTag.Inline> inlines) {
assertEquals(4, inlines.size());
for (HtmlTag.Inline inline : inlines) {
assertFalse(inline.isClosed());
assertEquals(HtmlTag.NO_END, inline.end());
}
}
});
assertEquals(1, blockTagsAction.tags.size());
with(blockTagsAction.tags.get(0), new Action<HtmlTag.Block>() {
@Override
public void apply(@NonNull HtmlTag.Block block) {
assertFalse(block.isClosed());
assertEquals(HtmlTag.NO_END, block.end());
}
});
}
@Test
public void flushClearsInternalState() {
final MarkwonHtmlParserImpl impl = MarkwonHtmlParserImpl.create();
final StringBuilder output = new StringBuilder();
impl.processFragment(output, "<p><i>italic <b>bold italic</b></i></p><p>paragraph</p><div>and a div</div>");
final CaptureInlineTagsAction inlineTagsAction = new CaptureInlineTagsAction();
final CaptureBlockTagsAction blockTagsAction = new CaptureBlockTagsAction();
impl.flushInlineTags(output.length(), inlineTagsAction);
impl.flushBlockTags(output.length(), blockTagsAction);
assertTrue(inlineTagsAction.called);
assertTrue(blockTagsAction.called);
assertEquals(2, inlineTagsAction.tags.size());
assertEquals(3, blockTagsAction.tags.size());
final CaptureInlineTagsAction captureInlineTagsAction = new CaptureInlineTagsAction();
final CaptureBlockTagsAction captureBlockTagsAction = new CaptureBlockTagsAction();
impl.flushInlineTags(output.length(), captureInlineTagsAction);
impl.flushBlockTags(output.length(), captureBlockTagsAction);
assertTrue(captureInlineTagsAction.called);
assertTrue(captureBlockTagsAction.called);
assertEquals(0, captureInlineTagsAction.tags.size());
assertEquals(0, captureBlockTagsAction.tags.size());
}
@Test
public void resetClearsBothInlinesAndBlocks() {
final MarkwonHtmlParserImpl impl = MarkwonHtmlParserImpl.create();
final StringBuilder output = new StringBuilder();
impl.processFragment(output, "<p>paragraph <i>italic</i></p><div>div</div>");
impl.reset();
final CaptureInlineTagsAction inlineTagsAction = new CaptureInlineTagsAction();
final CaptureBlockTagsAction blockTagsAction = new CaptureBlockTagsAction();
impl.flushInlineTags(output.length(), inlineTagsAction);
impl.flushBlockTags(output.length(), blockTagsAction);
assertTrue(inlineTagsAction.called);
assertTrue(blockTagsAction.called);
assertEquals(0, inlineTagsAction.tags.size());
assertEquals(0, blockTagsAction.tags.size());
}
private static class CaptureTagsAction<T> implements MarkwonHtmlParser.FlushAction<T> {
@ -413,4 +810,12 @@ public class MarkwonHtmlParserImplTest {
private static class CaptureBlockTagsAction extends CaptureTagsAction<HtmlTag.Block> {
}
private interface Action<T> {
void apply(@NonNull T t);
}
private static <T> void with(@NonNull T t, @NonNull Action<T> action) {
action.apply(t);
}
}