Make SpannedSubject more fluent

I decided the flags bit was a bit unclear so I played around with this

It's also needed for more 'complex' assertions like colors - I didn't
want to just chuck in a fourth int parameter to create:
hasForegroundColorSpan(int start, int end, int flags, int color)

PiperOrigin-RevId: 287989424
This commit is contained in:
ibaker 2020-01-03 15:32:06 +00:00 committed by Ian Baker
parent 1c0e69789f
commit 0587180f14
4 changed files with 478 additions and 54 deletions

View File

@ -34,8 +34,7 @@ public final class WebvttCueParserTest {
+ "This <u.style1.style2 some stuff>is</u> text with <b.foo><i.bar>html</i></b> tags");
assertThat(text.toString()).isEqualTo("This is text with html tags");
assertThat(text)
.hasUnderlineSpan("This ".length(), "This is".length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
assertThat(text).hasUnderlineSpanBetween("This ".length(), "This is".length());
assertThat(text)
.hasBoldItalicSpan(
"This is text with ".length(),
@ -59,10 +58,7 @@ public final class WebvttCueParserTest {
assertThat(text.toString()).isEqualTo("An unclosed u tag with italic inside");
assertThat(text)
.hasUnderlineSpan(
"An ".length(),
"An unclosed u tag with italic inside".length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
.hasUnderlineSpanBetween("An ".length(), "An unclosed u tag with italic inside".length());
assertThat(text)
.hasItalicSpan(
"An unclosed u tag with ".length(),
@ -81,10 +77,9 @@ public final class WebvttCueParserTest {
"An italic tag with unclosed underline".length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
assertThat(text)
.hasUnderlineSpan(
.hasUnderlineSpanBetween(
"An italic tag with unclosed ".length(),
"An italic tag with unclosed underline".length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
"An italic tag with unclosed underline".length());
}
@Test
@ -95,8 +90,7 @@ public final class WebvttCueParserTest {
assertThat(text.toString()).isEqualTo(expectedText);
assertThat(text).hasBoldSpan(0, expectedText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
// Text between the <u> tags is underlined.
assertThat(text)
.hasUnderlineSpan(0, "Overlapping u and".length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
assertThat(text).hasUnderlineSpanBetween(0, "Overlapping u and".length());
// Only text from <i> to <\\u> is italic (unexpected - but simplifies the parsing).
assertThat(text)
.hasItalicSpan(

View File

@ -15,23 +15,23 @@
*/
package com.google.android.exoplayer2.text.webvtt;
import static com.google.android.exoplayer2.testutil.truth.SpannedSubject.assertThat;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import android.graphics.Typeface;
import android.text.Layout.Alignment;
import android.text.Spanned;
import android.text.style.BackgroundColorSpan;
import android.text.style.ForegroundColorSpan;
import android.text.style.StyleSpan;
import android.text.style.TypefaceSpan;
import android.text.style.UnderlineSpan;
import androidx.annotation.Nullable;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.testutil.TestUtil;
import com.google.android.exoplayer2.text.Cue;
import com.google.android.exoplayer2.text.SubtitleDecoderException;
import com.google.android.exoplayer2.util.ColorParser;
import com.google.common.truth.Expect;
import java.io.IOException;
import java.util.List;
@ -403,14 +403,25 @@ public class WebvttDecoderTest {
Spanned s2 = getUniqueSpanTextAt(subtitle, /* timeUs= */ 2345000);
Spanned s3 = getUniqueSpanTextAt(subtitle, /* timeUs= */ 20000000);
Spanned s4 = getUniqueSpanTextAt(subtitle, /* timeUs= */ 25000000);
assertThat(s1.getSpans(/* start= */ 0, s1.length(), ForegroundColorSpan.class)).hasLength(1);
assertThat(s1.getSpans(/* start= */ 0, s1.length(), BackgroundColorSpan.class)).hasLength(1);
assertThat(s2.getSpans(/* start= */ 0, s2.length(), ForegroundColorSpan.class)).hasLength(1);
assertThat(s3.getSpans(/* start= */ 10, s3.length(), UnderlineSpan.class)).hasLength(1);
assertThat(s4.getSpans(/* start= */ 0, /* end= */ 16, BackgroundColorSpan.class)).hasLength(2);
assertThat(s4.getSpans(/* start= */ 17, s4.length(), StyleSpan.class)).hasLength(1);
assertThat(s4.getSpans(/* start= */ 17, s4.length(), StyleSpan.class)[0].getStyle())
.isEqualTo(Typeface.BOLD);
assertThat(s1)
.hasForegroundColorSpanBetween(0, s1.length())
.withColor(ColorParser.parseCssColor("papayawhip"));
assertThat(s1)
.hasBackgroundColorSpanBetween(0, s1.length())
.withColor(ColorParser.parseCssColor("green"));
assertThat(s2)
.hasForegroundColorSpanBetween(0, s2.length())
.withColor(ColorParser.parseCssColor("peachpuff"));
assertThat(s3).hasUnderlineSpanBetween(10, s3.length());
assertThat(s4)
.hasBackgroundColorSpanBetween(0, 16)
.withColor(ColorParser.parseCssColor("lime"));
assertThat(s4)
.hasBoldSpan(
/* startIndex= */ 17,
/* endIndex= */ s4.length(),
/* flags= */ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
@Test

View File

@ -23,8 +23,12 @@ import static com.google.common.truth.Truth.assertAbout;
import android.graphics.Typeface;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.BackgroundColorSpan;
import android.text.style.ForegroundColorSpan;
import android.text.style.StyleSpan;
import android.text.style.UnderlineSpan;
import androidx.annotation.CheckResult;
import androidx.annotation.ColorInt;
import androidx.annotation.Nullable;
import com.google.common.truth.FailureMetadata;
import com.google.common.truth.Subject;
@ -64,7 +68,7 @@ public final class SpannedSubject extends Subject {
failWithoutActual(
simpleFact("Expected no spans"),
fact("in text", actual),
fact("but found", actualSpansString()));
fact("but found", getAllSpansAsStringWithoutFlags(actual)));
}
}
@ -76,6 +80,7 @@ public final class SpannedSubject extends Subject {
* @param flags The flags of the expected span. See constants on {@link Spanned} for more
* information.
*/
// TODO: swap this to fluent-style.
public void hasItalicSpan(int startIndex, int endIndex, int flags) {
hasStyleSpan(startIndex, endIndex, flags, Typeface.ITALIC);
}
@ -88,6 +93,7 @@ public final class SpannedSubject extends Subject {
* @param flags The flags of the expected span. See constants on {@link Spanned} for more
* information.
*/
// TODO: swap this to fluent-style.
public void hasBoldSpan(int startIndex, int endIndex, int flags) {
hasStyleSpan(startIndex, endIndex, flags, Typeface.BOLD);
}
@ -104,7 +110,7 @@ public final class SpannedSubject extends Subject {
}
}
failWithExpectedSpan(
failWithExpectedSpanWithFlags(
startIndex,
endIndex,
flags,
@ -129,6 +135,7 @@ public final class SpannedSubject extends Subject {
* @param flags The flags of the expected span. See constants on {@link Spanned} for more
* information.
*/
// TODO: swap this to fluent-style.
public void hasBoldItalicSpan(int startIndex, int endIndex, int flags) {
if (actual == null) {
failWithoutActual(simpleFact("Spanned must not be null"));
@ -149,11 +156,13 @@ public final class SpannedSubject extends Subject {
String spannedSubstring = actual.toString().substring(startIndex, endIndex);
String boldSpan =
spanToString(startIndex, endIndex, flags, new StyleSpan(Typeface.BOLD), spannedSubstring);
getSpanAsStringWithFlags(
startIndex, endIndex, flags, new StyleSpan(Typeface.BOLD), spannedSubstring);
String italicSpan =
spanToString(startIndex, endIndex, flags, new StyleSpan(Typeface.ITALIC), spannedSubstring);
getSpanAsStringWithFlags(
startIndex, endIndex, flags, new StyleSpan(Typeface.ITALIC), spannedSubstring);
String boldItalicSpan =
spanToString(
getSpanAsStringWithFlags(
startIndex, endIndex, flags, new StyleSpan(Typeface.BOLD_ITALIC), spannedSubstring);
failWithoutActual(
@ -161,34 +170,89 @@ public final class SpannedSubject extends Subject {
fact("in text", actual.toString()),
fact("expected either", boldItalicSpan),
fact("or both", boldSpan + "\n" + italicSpan),
fact("but found", actualSpansString()));
fact("but found", getAllSpansAsStringWithFlags(actual)));
}
/**
* Checks that the subject has an underline span from {@code startIndex} to {@code endIndex}.
* Checks that the subject has an {@link UnderlineSpan} from {@code start} to {@code end}.
*
* @param startIndex The start of the expected span.
* @param endIndex The end of the expected span.
* @param flags The flags of the expected span. See constants on {@link Spanned} for more
* information.
* @param start The start of the expected span.
* @param end The end of the expected span.
* @return A {@link WithSpanFlags} object for optional additional assertions on the flags.
*/
public void hasUnderlineSpan(int startIndex, int endIndex, int flags) {
public WithSpanFlags hasUnderlineSpanBetween(int start, int end) {
if (actual == null) {
failWithoutActual(simpleFact("Spanned must not be null"));
return;
return ALREADY_FAILED_WITH_FLAGS;
}
List<UnderlineSpan> underlineSpans =
findMatchingSpans(startIndex, endIndex, flags, UnderlineSpan.class);
if (underlineSpans.size() == 1) {
return;
List<UnderlineSpan> underlineSpans = findMatchingSpans(start, end, UnderlineSpan.class);
List<Integer> allFlags = new ArrayList<>();
for (UnderlineSpan span : underlineSpans) {
allFlags.add(actual.getSpanFlags(span));
}
failWithExpectedSpan(
startIndex,
endIndex,
flags,
new UnderlineSpan(),
actual.toString().substring(startIndex, endIndex));
if (underlineSpans.size() == 1) {
return check("UnderlineSpan (start=%s,end=%s)", start, end).about(spanFlags()).that(allFlags);
}
failWithExpectedSpanWithoutFlags(
start, end, UnderlineSpan.class, actual.toString().substring(start, end));
return ALREADY_FAILED_WITH_FLAGS;
}
/**
* Checks that the subject as a {@link ForegroundColorSpan} from {@code start} to {@code end}.
*
* <p>The color is asserted in a follow-up method call on the return {@link Colored} object.
*
* @param start The start of the expected span.
* @param end The end of the expected span.
* @return A {@link Colored} object to assert on the color of the matching spans.
*/
@CheckResult
public Colored hasForegroundColorSpanBetween(int start, int end) {
if (actual == null) {
failWithoutActual(simpleFact("Spanned must not be null"));
return ALREADY_FAILED_COLORED;
}
List<ForegroundColorSpan> foregroundColorSpans =
findMatchingSpans(start, end, ForegroundColorSpan.class);
if (foregroundColorSpans.isEmpty()) {
failWithExpectedSpanWithoutFlags(
start, end, ForegroundColorSpan.class, actual.toString().substring(start, end));
return ALREADY_FAILED_COLORED;
}
return check("ForegroundColorSpan (start=%s,end=%s)", start, end)
.about(foregroundColorSpans(actual))
.that(foregroundColorSpans);
}
/**
* Checks that the subject as a {@link ForegroundColorSpan} from {@code start} to {@code end}.
*
* <p>The color is asserted in a follow-up method call on the return {@link Colored} object.
*
* @param start The start of the expected span.
* @param end The end of the expected span.
* @return A {@link Colored} object to assert on the color of the matching spans.
*/
@CheckResult
public Colored hasBackgroundColorSpanBetween(int start, int end) {
if (actual == null) {
failWithoutActual(simpleFact("Spanned must not be null"));
return ALREADY_FAILED_COLORED;
}
List<BackgroundColorSpan> backgroundColorSpans =
findMatchingSpans(start, end, BackgroundColorSpan.class);
if (backgroundColorSpans.isEmpty()) {
failWithExpectedSpanWithoutFlags(
start, end, BackgroundColorSpan.class, actual.toString().substring(start, end));
return ALREADY_FAILED_COLORED;
}
return check("BackgroundColorSpan (start=%s,end=%s)", start, end)
.about(backgroundColorSpans(actual))
.that(backgroundColorSpans);
}
private <T> List<T> findMatchingSpans(
@ -204,27 +268,46 @@ public final class SpannedSubject extends Subject {
return spans;
}
private void failWithExpectedSpan(
private <T> List<T> findMatchingSpans(int startIndex, int endIndex, Class<T> spanClazz) {
List<T> spans = new ArrayList<>();
for (T span : actual.getSpans(startIndex, endIndex, spanClazz)) {
if (actual.getSpanStart(span) == startIndex && actual.getSpanEnd(span) == endIndex) {
spans.add(span);
}
}
return spans;
}
private void failWithExpectedSpanWithFlags(
int start, int end, int flags, Object span, String spannedSubstring) {
failWithoutActual(
simpleFact("No matching span found"),
fact("in text", actual),
fact("expected", spanToString(start, end, flags, span, spannedSubstring)),
fact("but found", actualSpansString()));
fact("expected", getSpanAsStringWithFlags(start, end, flags, span, spannedSubstring)),
fact("but found", getAllSpansAsStringWithFlags(actual)));
}
private String actualSpansString() {
private void failWithExpectedSpanWithoutFlags(
int start, int end, Class<?> spanType, String spannedSubstring) {
failWithoutActual(
simpleFact("No matching span found"),
fact("in text", actual),
fact("expected", getSpanAsStringWithoutFlags(start, end, spanType, spannedSubstring)),
fact("but found", getAllSpansAsStringWithoutFlags(actual)));
}
private static String getAllSpansAsStringWithFlags(Spanned spanned) {
List<String> actualSpanStrings = new ArrayList<>();
for (Object span : actual.getSpans(0, actual.length(), /* type= */ Object.class)) {
actualSpanStrings.add(spanToString(span, actual));
for (Object span : spanned.getSpans(0, spanned.length(), Object.class)) {
actualSpanStrings.add(getSpanAsStringWithFlags(span, spanned));
}
return TextUtils.join("\n", actualSpanStrings);
}
private static String spanToString(Object span, Spanned spanned) {
private static String getSpanAsStringWithFlags(Object span, Spanned spanned) {
int spanStart = spanned.getSpanStart(span);
int spanEnd = spanned.getSpanEnd(span);
return spanToString(
return getSpanAsStringWithFlags(
spanStart,
spanEnd,
spanned.getSpanFlags(span),
@ -232,7 +315,7 @@ public final class SpannedSubject extends Subject {
spanned.toString().substring(spanStart, spanEnd));
}
private static String spanToString(
private static String getSpanAsStringWithFlags(
int start, int end, int flags, Object span, String spannedSubstring) {
String suffix;
if (span instanceof StyleSpan) {
@ -244,4 +327,177 @@ public final class SpannedSubject extends Subject {
"start=%s\tend=%s\tflags=%s\ttype=%s\tsubstring='%s'%s",
start, end, flags, span.getClass().getSimpleName(), spannedSubstring, suffix);
}
private static String getAllSpansAsStringWithoutFlags(Spanned spanned) {
List<String> actualSpanStrings = new ArrayList<>();
for (Object span : spanned.getSpans(0, spanned.length(), Object.class)) {
actualSpanStrings.add(getSpanAsStringWithoutFlags(span, spanned));
}
return TextUtils.join("\n", actualSpanStrings);
}
private static String getSpanAsStringWithoutFlags(Object span, Spanned spanned) {
int spanStart = spanned.getSpanStart(span);
int spanEnd = spanned.getSpanEnd(span);
return getSpanAsStringWithoutFlags(
spanStart, spanEnd, span.getClass(), spanned.toString().substring(spanStart, spanEnd));
}
private static String getSpanAsStringWithoutFlags(
int start, int end, Class<?> span, String spannedSubstring) {
return String.format(
"start=%s\tend=%s\ttype=%s\tsubstring='%s'",
start, end, span.getSimpleName(), spannedSubstring);
}
/**
* Allows additional assertions to be made on the flags of matching spans.
*
* <p>Identical to {@link WithSpanFlags}, but this should be returned from {@code with...()}
* methods while {@link WithSpanFlags} should be returned from {@code has...()} methods.
*
* <p>See Flag constants on {@link Spanned} for possible values.
*/
public interface AndSpanFlags {
/**
* Checks that one of the matched spans has the expected {@code flags}.
*
* @param flags The expected flags. See SPAN_* constants on {@link Spanned} for possible values.
*/
void andFlags(int flags);
}
private static final AndSpanFlags ALREADY_FAILED_AND_FLAGS = flags -> {};
/**
* Allows additional assertions to be made on the flags of matching spans.
*
* <p>Identical to {@link AndSpanFlags}, but this should be returned from {@code has...()} methods
* while {@link AndSpanFlags} should be returned from {@code with...()} methods.
*/
public interface WithSpanFlags {
/**
* Checks that one of the matched spans has the expected {@code flags}.
*
* @param flags The expected flags. See SPAN_* constants on {@link Spanned} for possible values.
*/
void withFlags(int flags);
}
private static final WithSpanFlags ALREADY_FAILED_WITH_FLAGS = flags -> {};
private static Factory<SpanFlagsSubject, List<Integer>> spanFlags() {
return SpanFlagsSubject::new;
}
private static final class SpanFlagsSubject extends Subject
implements AndSpanFlags, WithSpanFlags {
private final List<Integer> flags;
private SpanFlagsSubject(FailureMetadata metadata, List<Integer> flags) {
super(metadata, flags);
this.flags = flags;
}
@Override
public void andFlags(int flags) {
check("contains()").that(this.flags).contains(flags);
}
@Override
public void withFlags(int flags) {
andFlags(flags);
}
}
/** Allows assertions about the color of a span. */
public interface Colored {
/**
* Checks that at least one of the matched spans has the expected {@code color}.
*
* @param color The expected color.
* @return A {@link WithSpanFlags} object for optional additional assertions on the flags.
*/
AndSpanFlags withColor(@ColorInt int color);
}
private static final Colored ALREADY_FAILED_COLORED = color -> ALREADY_FAILED_AND_FLAGS;
private Factory<ForegroundColorSpansSubject, List<ForegroundColorSpan>> foregroundColorSpans(
Spanned actualSpanned) {
return (FailureMetadata metadata, List<ForegroundColorSpan> spans) ->
new ForegroundColorSpansSubject(metadata, spans, actualSpanned);
}
private static final class ForegroundColorSpansSubject extends Subject implements Colored {
private final List<ForegroundColorSpan> actualSpans;
private final Spanned actualSpanned;
private ForegroundColorSpansSubject(
FailureMetadata metadata, List<ForegroundColorSpan> actualSpans, Spanned actualSpanned) {
super(metadata, actualSpans);
this.actualSpans = actualSpans;
this.actualSpanned = actualSpanned;
}
@Override
public AndSpanFlags withColor(@ColorInt int color) {
List<Integer> matchingSpanFlags = new ArrayList<>();
// Use hex strings for comparison so the values in error messages are more human readable.
List<String> spanColors = new ArrayList<>();
for (ForegroundColorSpan span : actualSpans) {
spanColors.add(String.format("0x%08X", span.getForegroundColor()));
if (span.getForegroundColor() == color) {
matchingSpanFlags.add(actualSpanned.getSpanFlags(span));
}
}
String expectedColorString = String.format("0x%08X", color);
check("foregroundColor").that(spanColors).containsExactly(expectedColorString);
return check("flags").about(spanFlags()).that(matchingSpanFlags);
}
}
private Factory<BackgroundColorSpansSubject, List<BackgroundColorSpan>> backgroundColorSpans(
Spanned actualSpanned) {
return (FailureMetadata metadata, List<BackgroundColorSpan> spans) ->
new BackgroundColorSpansSubject(metadata, spans, actualSpanned);
}
private static final class BackgroundColorSpansSubject extends Subject implements Colored {
private final List<BackgroundColorSpan> actualSpans;
private final Spanned actualSpanned;
private BackgroundColorSpansSubject(
FailureMetadata metadata, List<BackgroundColorSpan> actualSpans, Spanned actualSpanned) {
super(metadata, actualSpans);
this.actualSpans = actualSpans;
this.actualSpanned = actualSpanned;
}
@Override
public AndSpanFlags withColor(@ColorInt int color) {
List<Integer> matchingSpanFlags = new ArrayList<>();
// Use hex strings for comparison so the values in error messages are more human readable.
List<String> spanColors = new ArrayList<>();
for (BackgroundColorSpan span : actualSpans) {
spanColors.add(String.format("0x%08X", span.getBackgroundColor()));
if (span.getBackgroundColor() == color) {
matchingSpanFlags.add(actualSpanned.getSpanFlags(span));
}
}
String expectedColorString = String.format("0x%08X", color);
check("backgroundColor").that(spanColors).containsExactly(expectedColorString);
return check("flags").about(spanFlags()).that(matchingSpanFlags);
}
}
}

View File

@ -21,9 +21,12 @@ import static com.google.android.exoplayer2.testutil.truth.SpannedSubject.spanne
import static com.google.common.truth.ExpectFailure.assertThat;
import static com.google.common.truth.ExpectFailure.expectFailureAbout;
import android.graphics.Color;
import android.graphics.Typeface;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.style.BackgroundColorSpan;
import android.text.style.ForegroundColorSpan;
import android.text.style.StyleSpan;
import android.text.style.UnderlineSpan;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@ -153,7 +156,167 @@ public class SpannedSubjectTest {
int end = start + "underlined".length();
spannable.setSpan(new UnderlineSpan(), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
assertThat(spannable).hasUnderlineSpan(start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
assertThat(spannable)
.hasUnderlineSpanBetween(start, end)
.withFlags(Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
}
@Test
public void foregroundColorSpan_success() {
SpannableString spannable = SpannableString.valueOf("test with cyan section");
int start = "test with ".length();
int end = start + "cyan".length();
spannable.setSpan(
new ForegroundColorSpan(Color.CYAN), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
assertThat(spannable)
.hasForegroundColorSpanBetween(start, end)
.withColor(Color.CYAN)
.andFlags(Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
}
@Test
public void foregroundColorSpan_wrongEndIndex() {
SpannableString spannable = SpannableString.valueOf("test with cyan section");
int start = "test with ".length();
int end = start + "cyan".length();
spannable.setSpan(
new ForegroundColorSpan(Color.CYAN), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
int incorrectEnd = end + 2;
AssertionError expected =
expectFailure(
whenTesting ->
whenTesting
.that(spannable)
.hasForegroundColorSpanBetween(start, incorrectEnd)
.withColor(Color.CYAN));
assertThat(expected).factValue("expected").contains("end=" + incorrectEnd);
assertThat(expected).factValue("but found").contains("end=" + end);
}
@Test
public void foregroundColorSpan_wrongColor() {
SpannableString spannable = SpannableString.valueOf("test with cyan section");
int start = "test with ".length();
int end = start + "cyan".length();
spannable.setSpan(
new ForegroundColorSpan(Color.CYAN), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
AssertionError expected =
expectFailure(
whenTesting ->
whenTesting
.that(spannable)
.hasForegroundColorSpanBetween(start, end)
.withColor(Color.BLUE));
assertThat(expected).factValue("value of").contains("foregroundColor");
assertThat(expected).factValue("expected").contains("0xFF0000FF"); // Color.BLUE
assertThat(expected).factValue("but was").contains("0xFF00FFFF"); // Color.CYAN
}
@Test
public void foregroundColorSpan_wrongFlags() {
SpannableString spannable = SpannableString.valueOf("test with cyan section");
int start = "test with ".length();
int end = start + "cyan".length();
spannable.setSpan(
new ForegroundColorSpan(Color.CYAN), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
AssertionError expected =
expectFailure(
whenTesting ->
whenTesting
.that(spannable)
.hasForegroundColorSpanBetween(start, end)
.withColor(Color.CYAN)
.andFlags(Spanned.SPAN_EXCLUSIVE_EXCLUSIVE));
assertThat(expected).factValue("value of").contains("flags");
assertThat(expected)
.factValue("expected to contain")
.contains(String.valueOf(Spanned.SPAN_EXCLUSIVE_EXCLUSIVE));
assertThat(expected)
.factValue("but was")
.contains(String.valueOf(Spanned.SPAN_INCLUSIVE_EXCLUSIVE));
}
@Test
public void backgroundColorSpan_success() {
SpannableString spannable = SpannableString.valueOf("test with cyan section");
int start = "test with ".length();
int end = start + "cyan".length();
spannable.setSpan(
new BackgroundColorSpan(Color.CYAN), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
assertThat(spannable)
.hasBackgroundColorSpanBetween(start, end)
.withColor(Color.CYAN)
.andFlags(Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
}
@Test
public void backgroundColorSpan_wrongEndIndex() {
SpannableString spannable = SpannableString.valueOf("test with cyan section");
int start = "test with ".length();
int end = start + "cyan".length();
spannable.setSpan(
new BackgroundColorSpan(Color.CYAN), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
int incorrectEnd = end + 2;
AssertionError expected =
expectFailure(
whenTesting ->
whenTesting
.that(spannable)
.hasBackgroundColorSpanBetween(start, incorrectEnd)
.withColor(Color.CYAN));
assertThat(expected).factValue("expected").contains("end=" + incorrectEnd);
assertThat(expected).factValue("but found").contains("end=" + end);
}
@Test
public void backgroundColorSpan_wrongColor() {
SpannableString spannable = SpannableString.valueOf("test with cyan section");
int start = "test with ".length();
int end = start + "cyan".length();
spannable.setSpan(
new BackgroundColorSpan(Color.CYAN), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
AssertionError expected =
expectFailure(
whenTesting ->
whenTesting
.that(spannable)
.hasBackgroundColorSpanBetween(start, end)
.withColor(Color.BLUE));
assertThat(expected).factValue("value of").contains("backgroundColor");
assertThat(expected).factValue("expected").contains("0xFF0000FF"); // Color.BLUE
assertThat(expected).factValue("but was").contains("0xFF00FFFF"); // Color.CYAN
}
@Test
public void backgroundColorSpan_wrongFlags() {
SpannableString spannable = SpannableString.valueOf("test with cyan section");
int start = "test with ".length();
int end = start + "cyan".length();
spannable.setSpan(
new BackgroundColorSpan(Color.CYAN), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
AssertionError expected =
expectFailure(
whenTesting ->
whenTesting
.that(spannable)
.hasBackgroundColorSpanBetween(start, end)
.withColor(Color.CYAN)
.andFlags(Spanned.SPAN_EXCLUSIVE_EXCLUSIVE));
assertThat(expected).factValue("value of").contains("flags");
assertThat(expected)
.factValue("expected to contain")
.contains(String.valueOf(Spanned.SPAN_EXCLUSIVE_EXCLUSIVE));
assertThat(expected)
.factValue("but was")
.contains(String.valueOf(Spanned.SPAN_INCLUSIVE_EXCLUSIVE));
}
private static AssertionError expectFailure(