Adding tests for SpannableBuilder

This commit is contained in:
Dimitry Ivanov 2018-11-05 12:14:15 +03:00
parent 7a1b76af66
commit cb917e7391
2 changed files with 215 additions and 16 deletions

View File

@ -163,11 +163,46 @@ public class SpannableBuilder implements Appendable, CharSequence {
*/
@Override
public CharSequence subSequence(int start, int end) {
// todo: NB, we do not copy spans here... we should I think
// the thing to deal with: implement own `getSpans` method to mimic _native_ SpannableStringBuilder
// behaviour. For example originally it will return all spans that at least _overlap_ with specified
// range... which can be confusing
return builder.subSequence(start, end);
final CharSequence out;
// @since 2.0.1 we copy spans to resulting subSequence
final List<Span> spans = getSpans(start, end);
if (spans.isEmpty()) {
out = builder.subSequence(start, end);
} else {
// we should not be SpannableStringBuilderReversed here
final SpannableStringBuilder builder = new SpannableStringBuilder(this.builder.subSequence(start, end));
final int length = builder.length();
int s;
int e;
for (Span span : spans) {
// we should limit start/end to resulting subSequence length
//
// for example, originally it was 5-7 and range 5-7 requested
// span should have 0-2
//
// if a span was fully including resulting subSequence it's start and
// end must be within 0..length bounds
s = Math.max(0, span.start - start);
e = Math.max(length, s + (span.end - span.start));
builder.setSpan(
span.what,
s,
e,
span.flags
);
}
out = builder;
}
return out;
}
/**
@ -263,7 +298,7 @@ public class SpannableBuilder implements Appendable, CharSequence {
/**
* Simple method to create a SpannableStringBuilder, which is created anyway. Unlike {@link #text()}
* method which returns the same SpannableStringBuilder there is no need to cast the resulting
* CharSequence
* CharSequence and makes the thing more explicit
*
* @since 2.0.0
*/
@ -338,10 +373,10 @@ public class SpannableBuilder implements Appendable, CharSequence {
*/
public static class Span {
final Object what;
int start;
int end;
final int flags;
public final Object what;
public int start;
public int end;
public final int flags;
Span(@NonNull Object what, int start, int end, int flags) {
this.what = what;
@ -355,7 +390,6 @@ public class SpannableBuilder implements Appendable, CharSequence {
* @since 2.0.1 made inner class of {@link SpannableBuilder}, initially added in 1.0.1
*/
static class SpannableStringBuilderReversed extends SpannableStringBuilder {
SpannableStringBuilderReversed(CharSequence text) {
super(text);
}

View File

@ -1,6 +1,8 @@
package ru.noties.markwon;
import android.support.annotation.NonNull;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import org.junit.Before;
import org.junit.Test;
@ -18,6 +20,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static ru.noties.markwon.SpannableBuilder.isPositionValid;
import static ru.noties.markwon.SpannableBuilder.setSpans;
@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE)
@ -65,11 +68,6 @@ public class SpannableBuilderTest {
}
}
// @Test
// public void set_spans_position_invalid() {
// // will be silently ignored
// }
@Test
public void get_spans() {
@ -165,6 +163,173 @@ public class SpannableBuilderTest {
.toList();
}
@Test
public void set_spans_position_invalid() {
// if supplied position is invalid, no spans should be added
builder.append('0');
assertTrue(builder.getSpans(0, builder.length()).isEmpty());
setSpans(builder, new Object(), -1, -1);
assertTrue(builder.getSpans(0, builder.length()).isEmpty());
}
@Test
public void set_spans_single() {
// single span as `spans` argument correctly added
builder.append('0');
assertTrue(builder.getSpans(0, builder.length()).isEmpty());
final Object span = new Object();
setSpans(builder, span, 0, 1);
final List<SpannableBuilder.Span> spans = builder.getSpans(0, builder.length());
assertEquals(1, spans.size());
assertEquals(span, spans.get(0).what);
}
@Test
public void set_spans_array_detected() {
// if supplied `spans` argument is an array -> it should be expanded
builder.append('0');
assertTrue(builder.getSpans(0, builder.length()).isEmpty());
final Object[] spans = {
new Object(),
new Object(),
new Object()
};
setSpans(builder, spans, 0, 1);
final List<SpannableBuilder.Span> actual = builder.getSpans(0, builder.length());
assertEquals(spans.length, actual.size());
for (int i = 0, length = spans.length; i < length; i++) {
assertEquals(spans[i], actual.get(i).what);
}
}
@Test
public void set_spans_array_of_arrays() {
// if array of arrays is supplied -> it won't be expanded to single elements
builder.append('0');
assertTrue(builder.getSpans(0, builder.length()).isEmpty());
final Object[] spans = {
new Object[]{
new Object(), new Object()
},
new Object[]{
new Object(), new Object(), new Object()
}
};
setSpans(builder, spans, 0, 1);
final List<SpannableBuilder.Span> actual = builder.getSpans(0, builder.length());
assertEquals(2, actual.size());
for (int i = 0, length = spans.length; i < length; i++) {
assertEquals(spans[i], actual.get(i).what);
}
}
@Test
public void set_spans_null() {
// if `spans` argument is null, then nothing will be added
builder.append('0');
assertTrue(builder.getSpans(0, builder.length()).isEmpty());
setSpans(builder, null, 0, builder.length());
assertTrue(builder.getSpans(0, builder.length()).isEmpty());
}
@Test
public void spans_reversed() {
// resulting SpannableStringBuilder should have spans reversed
final Object[] spans = {
0,
1,
2
};
for (Object span : spans) {
builder.append(span.toString(), span);
}
final SpannableStringBuilder spannableStringBuilder = builder.spannableStringBuilder();
final Object[] actual = spannableStringBuilder.getSpans(0, builder.length(), Object.class);
for (int start = 0, length = spans.length, end = length - 1; start < length; start++, end--) {
assertEquals(spans[start], actual[end]);
}
}
@Test
public void append_spanned_normal() {
// #append is called with regular Spanned content -> spans should be added in reverse
final SpannableStringBuilder ssb = new SpannableStringBuilder();
for (int i = 0; i < 3; i++) {
ssb.append(String.valueOf(i));
ssb.setSpan(i, i, i + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
assertTrue(builder.getSpans(0, builder.length()).isEmpty());
builder.append(ssb);
assertEquals("012", builder.toString());
// this one would return normal order as spans are reversed here
// final List<SpannableBuilder.Span> spans = builder.getSpans(0, builder.length());
final SpannableStringBuilder spannableStringBuilder = builder.spannableStringBuilder();
final Object[] spans = spannableStringBuilder.getSpans(0, builder.length(), Object.class);
assertEquals(3, spans.length);
for (int i = 0, length = spans.length; i < length; i++) {
assertEquals(length - 1 - i, spans[i]);
}
}
@Test
public void append_spanned_reversed() {
// #append is called with reversed spanned content -> spans should be added as-are
final SpannableBuilder spannableBuilder = new SpannableBuilder();
for (int i = 0; i < 3; i++) {
spannableBuilder.append(String.valueOf(i), i);
}
assertTrue(builder.getSpans(0, builder.length()).isEmpty());
builder.append(spannableBuilder.spannableStringBuilder());
final SpannableStringBuilder spannableStringBuilder = builder.spannableStringBuilder();
final Object[] spans = spannableStringBuilder.getSpans(0, spannableStringBuilder.length(), Object.class);
assertEquals(3, spans.length);
for (int i = 0, length = spans.length; i < length; i++) {
// in the end order should be as we expect in order to properly render it
// (no matter if reversed is used or not)
assertEquals(length - 1 - i, spans[i]);
}
}
private static class Position {
@NonNull