Fix failing tests

This commit is contained in:
Dimitry Ivanov 2019-01-04 01:32:06 +03:00
parent 81c1c16a86
commit ec3d596d8a
16 changed files with 384 additions and 64 deletions

View File

@ -25,22 +25,9 @@ public interface MarkwonSpansFactory {
@Nullable
<N extends Node> SpanFactory get(@NonNull Class<N> node);
/**
* @see #get(Class)
* @see #require(Node)
*/
@Nullable
<N extends Node> SpanFactory get(@NonNull N node);
@NonNull
<N extends Node> SpanFactory require(@NonNull Class<N> node);
/**
* @see #require(Class)
*/
@NonNull
<N extends Node> SpanFactory require(@NonNull N node);
interface Builder {

View File

@ -16,7 +16,7 @@ class MarkwonSpansFactoryImpl implements MarkwonSpansFactory {
private final Map<Class<? extends Node>, SpanFactory> factories;
private MarkwonSpansFactoryImpl(@NonNull Map<Class<? extends Node>, SpanFactory> factories) {
MarkwonSpansFactoryImpl(@NonNull Map<Class<? extends Node>, SpanFactory> factories) {
this.factories = factories;
}
@ -26,28 +26,12 @@ class MarkwonSpansFactoryImpl implements MarkwonSpansFactory {
return factories.get(node);
}
@Nullable
@Override
public <N extends Node> SpanFactory get(@NonNull N node) {
return get(node.getClass());
}
@NonNull
@Override
public <N extends Node> SpanFactory require(@NonNull Class<N> node) {
final SpanFactory f = get(node);
if (f == null) {
throw new NullPointerException();
}
return f;
}
@NonNull
@Override
public <N extends Node> SpanFactory require(@NonNull N node) {
final SpanFactory f = get(node);
if (f == null) {
throw new NullPointerException();
throw new NullPointerException(node.getName());
}
return f;
}

View File

@ -95,7 +95,7 @@ public interface MarkwonVisitor extends Visitor {
/**
* Helper method to obtain and apply spans for supplied Node. Internally queries {@link SpanFactory}
* for the node (via {@link MarkwonSpansFactory#require(Node)} thus throwing an exception
* for the node (via {@link MarkwonSpansFactory#require(Class)} thus throwing an exception
* if there is no {@link SpanFactory} registered for the node).
*
* @param node to retrieve {@link SpanFactory} for
@ -119,7 +119,7 @@ public interface MarkwonVisitor extends Visitor {
* {@link MarkwonSpansFactory} instance. Otherwise ignores this call (no spans will be applied).
* If there is a need to ensure that specified <code>node</code> has a {@link SpanFactory} registered,
* then {@link #setSpansForNode(Node, int)} can be used. {@link #setSpansForNode(Node, int)} internally
* uses {@link MarkwonSpansFactory#require(Node)}. This method uses {@link MarkwonSpansFactory#get(Node)}.
* uses {@link MarkwonSpansFactory#require(Class)}. This method uses {@link MarkwonSpansFactory#get(Class)}.
*
* @see #setSpansForNode(Node, int)
*/

View File

@ -241,7 +241,7 @@ class MarkwonVisitorImpl implements MarkwonVisitor {
@Override
public <N extends Node> void setSpansForNode(@NonNull N node, int start) {
setSpans(start, configuration.spansFactory().require(node).getSpans(configuration, renderProps));
setSpansForNode(node.getClass(), start);
}
@Override
@ -251,10 +251,7 @@ class MarkwonVisitorImpl implements MarkwonVisitor {
@Override
public <N extends Node> void setSpansForNodeOptional(@NonNull N node, int start) {
final SpanFactory factory = configuration.spansFactory().get(node);
if (factory != null) {
setSpans(start, factory.getSpans(configuration, renderProps));
}
setSpansForNodeOptional(node.getClass(), start);
}
@Override

View File

@ -36,30 +36,30 @@ public class Prop<T> {
}
@Nullable
public T get(@NonNull RenderProps context) {
return context.get(this);
public T get(@NonNull RenderProps props) {
return props.get(this);
}
@NonNull
public T get(@NonNull RenderProps context, @NonNull T defValue) {
return context.get(this, defValue);
public T get(@NonNull RenderProps props, @NonNull T defValue) {
return props.get(this, defValue);
}
@NonNull
public T require(@NonNull RenderProps context) {
final T t = get(context);
public T require(@NonNull RenderProps props) {
final T t = get(props);
if (t == null) {
throw new NullPointerException();
throw new NullPointerException(name);
}
return t;
}
public void set(@NonNull RenderProps context, @Nullable T value) {
context.set(this, value);
public void set(@NonNull RenderProps props, @Nullable T value) {
props.set(this, value);
}
public void clear(@NonNull RenderProps context) {
context.clear(this);
public void clear(@NonNull RenderProps props) {
props.clear(this);
}
@Override

View File

@ -42,6 +42,7 @@ import ru.noties.markwon.core.spans.OrderedListItemSpan;
import ru.noties.markwon.priority.Priority;
/**
* @see CoreProps
* @since 3.0.0
*/
public class CorePlugin extends AbstractMarkwonPlugin {

View File

@ -2,6 +2,9 @@ package ru.noties.markwon.core;
import ru.noties.markwon.Prop;
/**
* @since 3.0.0
*/
public abstract class CoreProps {
public static final Prop<ListItemType> LIST_ITEM_TYPE = Prop.of("list-item-type");

View File

@ -33,6 +33,7 @@ public class ImagesPlugin extends AbstractMarkwonPlugin {
/**
* Special scheme that is used {@code file:///android_asset/}
*
* @param context
* @return
*/
@ -79,7 +80,7 @@ public class ImagesPlugin extends AbstractMarkwonPlugin {
public void visit(@NonNull MarkwonVisitor visitor, @NonNull Image image) {
// if there is no image spanFactory, ignore
final SpanFactory spanFactory = visitor.configuration().spansFactory().get(image);
final SpanFactory spanFactory = visitor.configuration().spansFactory().get(Image.class);
if (spanFactory == null) {
visitor.visitChildren(image);
return;

View File

@ -10,7 +10,7 @@ import ru.noties.markwon.image.ImageItem;
import ru.noties.markwon.image.SchemeHandler;
/**
* @since 3.0.0
* @since 2.0.0
*/
public class DataUriSchemeHandler extends SchemeHandler {

View File

@ -5,6 +5,10 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
/**
* Processor that will <em>assume</em> that an URL without scheme points to android assets folder.
* URL with a scheme will be processed by {@link #processor} (if it is specified) or returned `as-is`.
*/
@SuppressWarnings({"unused", "WeakerAccess"})
public class UrlProcessorAndroidAssets implements UrlProcessor {

View File

@ -5,7 +5,12 @@ import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import static org.junit.Assert.fail;
import java.util.List;
import ru.noties.markwon.core.CorePlugin;
import ru.noties.markwon.priority.Priority;
import static org.junit.Assert.assertEquals;
@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE)
@ -15,13 +20,25 @@ public class AbstractMarkwonPluginTest {
public void priority() {
// returns CorePlugin dependency
fail();
final Priority priority = new AbstractMarkwonPlugin() {
}.priority();
final List<Class<? extends MarkwonPlugin>> after = priority.after();
assertEquals(1, after.size());
assertEquals(CorePlugin.class, after.get(0));
}
@Test
public void process_markdown() {
// returns supplied argument (no-op)
fail();
final String[] input = {
"hello",
"!\nworld___-976"
};
for (String s : input) {
assertEquals(s, new AbstractMarkwonPlugin() {
}.processMarkdown(s));
}
}
}

View File

@ -1,18 +1,90 @@
package ru.noties.markwon;
import org.commonmark.node.Block;
import org.commonmark.node.Emphasis;
import org.commonmark.node.Image;
import org.commonmark.node.Link;
import org.commonmark.node.ListItem;
import org.commonmark.node.Node;
import org.commonmark.node.Paragraph;
import org.commonmark.node.Text;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import java.util.Collections;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class MarkwonSpansFactoryImplTest {
@Test
public void test() {
fail();
public void get_class() {
// register one TextNode
final MarkwonSpansFactoryImpl impl = new MarkwonSpansFactoryImpl(
Collections.<Class<? extends Node>, SpanFactory>singletonMap(Text.class, mock(SpanFactory.class)));
// text must be present
assertNotNull(impl.get(Text.class));
// we haven't registered ListItem, so null here
assertNull(impl.get(ListItem.class));
}
@Test
public void require_class() {
// register one TextNode
final MarkwonSpansFactoryImpl impl = new MarkwonSpansFactoryImpl(
Collections.<Class<? extends Node>, SpanFactory>singletonMap(Text.class, mock(SpanFactory.class)));
// text must be present
assertNotNull(impl.require(Text.class));
// we haven't registered ListItem, so null here
try {
impl.require(ListItem.class);
fail();
} catch (NullPointerException e) {
assertTrue(true);
}
}
@Test
public void builder() {
// all passed to builder will be in factory
final SpanFactory text = mock(SpanFactory.class);
final SpanFactory link = mock(SpanFactory.class);
final MarkwonSpansFactory factory = new MarkwonSpansFactoryImpl.BuilderImpl()
.setFactory(Text.class, text)
.setFactory(Link.class, link)
.build();
assertNotNull(factory.get(Text.class));
assertNotNull(factory.get(Link.class));
// a bunch of non-present factories
//noinspection unchecked
final Class<? extends Node>[] types = new Class[]{
Image.class,
Block.class,
Emphasis.class,
Paragraph.class
};
for (Class<? extends Node> type : types) {
assertNull(factory.get(type));
}
}
}

View File

@ -1,6 +1,8 @@
package ru.noties.markwon;
import org.commonmark.node.BlockQuote;
import org.commonmark.node.Node;
import org.commonmark.node.Text;
import org.commonmark.node.Visitor;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -18,10 +20,13 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.RETURNS_MOCKS;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE)
@ -132,7 +137,23 @@ public class MarkwonVisitorImplTest {
@Test
public void non_registered_nodes_children_visited() {
fail();
// if a node is encountered, but we have no registered visitor -> just visit children
// (node.firstChild.accept)
final MarkwonVisitorImpl impl = new MarkwonVisitorImpl(
mock(MarkwonConfiguration.class),
mock(RenderProps.class),
mock(SpannableBuilder.class),
Collections.<Class<? extends Node>, MarkwonVisitor.NodeVisitor<? extends Node>>emptyMap());
final BlockQuote node = mock(BlockQuote.class);
final Node child = mock(Node.class);
when(node.getFirstChild()).thenReturn(child);
impl.visit(node);
verify(node, times(1)).getFirstChild();
verify(child, times(1)).accept(eq(impl));
}
@Test
@ -185,11 +206,61 @@ public class MarkwonVisitorImplTest {
@Test
public void set_spans_for_node() {
fail();
// internally requests spanFactory via `require` call (thus throwing exception)
// configuration.spansFactory().require(node).getSpans(configuration, renderProps)
final MarkwonConfiguration configuration = mock(MarkwonConfiguration.class);
final MarkwonSpansFactory spansFactory = mock(MarkwonSpansFactory.class);
final SpanFactory factory = mock(SpanFactory.class);
when(configuration.spansFactory()).thenReturn(spansFactory);
when(spansFactory.require(eq(Node.class))).thenReturn(factory);
when(spansFactory.require(eq(Text.class))).thenThrow(new NullPointerException());
final MarkwonVisitorImpl impl = new MarkwonVisitorImpl(
configuration,
mock(RenderProps.class),
mock(SpannableBuilder.class),
Collections.<Class<? extends Node>, MarkwonVisitor.NodeVisitor<? extends Node>>emptyMap());
impl.setSpansForNode(Node.class, 0);
verify(configuration, times(1)).spansFactory();
verify(spansFactory, times(1)).require(eq(Node.class));
verify(factory, times(1)).getSpans(eq(configuration), any(RenderProps.class));
try {
impl.setSpansForNode(Text.class, 0);
fail();
} catch (NullPointerException e) {
assertTrue(true);
}
}
@Test
public void set_spans_for_node_optional() {
fail();
// if spanFactory is not found -> nothing will happen (no spans will be applied)
final MarkwonConfiguration configuration = mock(MarkwonConfiguration.class);
final MarkwonSpansFactory spansFactory = mock(MarkwonSpansFactory.class);
when(configuration.spansFactory()).thenReturn(spansFactory);
final SpannableBuilder builder = new SpannableBuilder();
final MarkwonVisitorImpl impl = new MarkwonVisitorImpl(
configuration,
mock(RenderProps.class),
builder,
Collections.<Class<? extends Node>, MarkwonVisitor.NodeVisitor<? extends Node>>emptyMap());
// append something
builder.append("no-spans-test");
assertEquals(0, builder.getSpans(0, builder.length()).size());
impl.setSpansForNodeOptional(Node.class, 0);
assertEquals(0, builder.getSpans(0, builder.length()).size());
}
}

View File

@ -1,18 +1,100 @@
package ru.noties.markwon;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class PropTest {
// get
// get with default
// require
// set
// clear
private RenderProps props;
private Prop<Boolean> prop;
@Before
public void before() {
props = mock(RenderProps.class);
prop = new Prop<>("a prop");
}
@Test
public void methods_redirected_get() {
prop.get(props);
verify(props, times(1)).get(eq(prop));
}
@Test
public void methods_redirected_get_with_default() {
prop.get(props, false);
verify(props, times(1)).get(eq(prop), eq(false));
}
@Test
public void methods_redirected_require() {
// require is a bit different as `require` has no place in renderProps
// instead a Prop will throw an exception if requested prop is not in props
when(props.get(eq(prop))).thenReturn(true);
prop.require(props);
verify(props, times(1)).get(eq(prop));
}
@Test
public void methods_redirected_set() {
prop.set(props, true);
verify(props, times(1)).set(eq(prop), eq(true));
}
@Test
public void methods_redirected_clear() {
prop.clear(props);
verify(props, times(1)).clear(eq(prop));
}
@Test
public void require() {
fail();
try {
prop.require(props);
fail();
} catch (NullPointerException e) {
assertTrue(true);
}
}
@Test
public void has_hashcode_and_equals() {
try {
Prop.class.getDeclaredMethod("hashCode");
Prop.class.getDeclaredMethod("equals", Object.class);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
}

View File

@ -1,18 +1,119 @@
package ru.noties.markwon;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import static org.junit.Assert.fail;
import java.util.Arrays;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class RenderPropsImplTest {
private RenderPropsImpl props;
private Prop<String> prop;
@Before
public void before() {
props = new RenderPropsImpl();
prop = Prop.of("a prop of byte");
}
@Test
public void test() {
fail();
public void get() {
// initial
assertNull(props.get(prop));
// update value
props.set(prop, "get-value");
assertEquals("get-value", props.get(prop));
}
@Test
public void get_with_default() {
// validate that it's null
assertNull(props.get(prop));
assertEquals("a-default", props.get(prop, "a-default"));
// update value (so, no default will be returned)
props.set(prop, "get-with-default-value");
assertEquals("get-with-default-value", props.get(prop, "not-used"));
}
@Test
public void set() {
assertNull(props.get(prop));
props.set(prop, "set-value");
assertEquals("set-value", props.get(prop));
// update (aka delete) with null value
props.set(prop, null);
assertNull(props.get(prop));
// multiple set's (last one will be used, each one replaces previous)
props.set(prop, "value-1");
props.set(prop, "value-2");
props.set(prop, "value-3");
assertEquals("value-3", props.get(prop));
}
@Test
public void clear() {
props.set(prop, "clear-value");
assertEquals("clear-value", props.get(prop));
props.clear(prop);
assertNull(props.get(prop));
}
@Test
public void clear_all() {
final List<Prop<String>> list = Arrays.asList(
Prop.<String>of("#1"),
Prop.<String>of("#2"),
Prop.<String>of("#3"),
Prop.<String>of("#4"),
Prop.<String>of("#5"));
// validate that all nulls
for (Prop<String> prop : list) {
assertNull(props.get(prop));
}
// set each
for (Prop<String> prop : list) {
props.set(prop, prop.name());
}
// validate that all are not-null
for (Prop<String> prop : list) {
assertNotNull(props.get(prop));
}
props.clearAll();
// validate that all are nulls
for (Prop<String> prop : list) {
assertNull(props.get(prop));
}
}
}

View File

@ -74,7 +74,7 @@ public class SyntaxHighlightTest {
};
final MarkwonSpansFactory spansFactory = mock(MarkwonSpansFactory.class);
when(spansFactory.get(any(FencedCodeBlock.class))).thenReturn(new SpanFactory() {
when(spansFactory.get(any(Class.class))).thenReturn(new SpanFactory() {
@Override
public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps props) {
return codeSpan;