Commit 448db894 by ibaker Committed by Oliver Woodman

Add TypefaceSpan and hasNoFooSpanBetween() support to SpannedSubject

Use these to migrate the last WebvttDecoderTest method to SpannedSubject

PiperOrigin-RevId: 288688620
parent ee091e6a
...@@ -20,7 +20,7 @@ STYLE ...@@ -20,7 +20,7 @@ STYLE
id id
00:00.000 --> 00:01.001 00:00.000 --> 00:01.001
This should be underlined and <lang.class1.class2> courier and violet. This should be underlined and <lang.class1.class2>courier and violet.
íd íd
00:02.000 --> 00:02.001 00:02.000 --> 00:02.001
...@@ -31,10 +31,10 @@ _id ...@@ -31,10 +31,10 @@ _id
This <lang.class.another>should be courier and bold. This <lang.class.another>should be courier and bold.
00:04.000 --> 00:04.001 00:04.000 --> 00:04.001
This <v Strider Trancos> shouldn't be bold.</v> This <v Strider Trancos>shouldn't be bold.</v>
This <v.class.clazz Strider Trancos> should be bold. This <v.class.clazz Strider Trancos>should be bold.
anId anId
00:05.000 --> 00:05.001 00:05.000 --> 00:05.001
This is <v.class1.class3.class2 Pipo> specific </v> This is <v.class1.class3.class2 Pipo>specific</v>
<v.class1.class3.class2 Robert> But this is more italic</v> <v.class1.class3.class2 Robert>But this is more italic</v>
...@@ -19,17 +19,14 @@ import static com.google.android.exoplayer2.testutil.truth.SpannedSubject.assert ...@@ -19,17 +19,14 @@ import static com.google.android.exoplayer2.testutil.truth.SpannedSubject.assert
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import android.graphics.Typeface;
import android.text.Layout.Alignment; import android.text.Layout.Alignment;
import android.text.Spanned; import android.text.Spanned;
import android.text.style.ForegroundColorSpan;
import android.text.style.StyleSpan;
import android.text.style.TypefaceSpan;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.testutil.TestUtil; import com.google.android.exoplayer2.testutil.TestUtil;
import com.google.android.exoplayer2.text.Cue; import com.google.android.exoplayer2.text.Cue;
import com.google.android.exoplayer2.text.SubtitleDecoderException; import com.google.android.exoplayer2.text.SubtitleDecoderException;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.ColorParser; import com.google.android.exoplayer2.util.ColorParser;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.truth.Expect; import com.google.common.truth.Expect;
...@@ -328,41 +325,39 @@ public class WebvttDecoderTest { ...@@ -328,41 +325,39 @@ public class WebvttDecoderTest {
@Test @Test
public void testWithComplexCssSelectors() throws Exception { public void testWithComplexCssSelectors() throws Exception {
WebvttSubtitle subtitle = getSubtitleForTestAsset(WITH_CSS_COMPLEX_SELECTORS); WebvttSubtitle subtitle = getSubtitleForTestAsset(WITH_CSS_COMPLEX_SELECTORS);
Spanned text = getUniqueSpanTextAt(subtitle, /* timeUs= */ 0); Spanned firstCueText = getUniqueSpanTextAt(subtitle, /* timeUs= */ 0);
assertThat(text.getSpans(/* start= */ 30, text.length(), ForegroundColorSpan.class)) assertThat(firstCueText)
.hasLength(1); .hasForegroundColorSpanBetween(
assertThat( "This should be underlined and ".length(), firstCueText.length())
text.getSpans(/* start= */ 30, text.length(), ForegroundColorSpan.class)[0] .withColor(ColorParser.parseCssColor("violet"));
.getForegroundColor()) assertThat(firstCueText)
.isEqualTo(0xFFEE82EE); .hasTypefaceSpanBetween("This should be underlined and ".length(), firstCueText.length())
assertThat(text.getSpans(/* start= */ 30, text.length(), TypefaceSpan.class)).hasLength(1); .withFamily("courier");
assertThat(text.getSpans(/* start= */ 30, text.length(), TypefaceSpan.class)[0].getFamily())
.isEqualTo("courier"); Spanned secondCueText = getUniqueSpanTextAt(subtitle, /* timeUs= */ 2_000_000);
assertThat(secondCueText)
text = getUniqueSpanTextAt(subtitle, /* timeUs= */ 2000000); .hasTypefaceSpanBetween("This ".length(), secondCueText.length())
assertThat(text.getSpans(/* start= */ 5, text.length(), TypefaceSpan.class)).hasLength(1); .withFamily("courier");
assertThat(text.getSpans(/* start= */ 5, text.length(), TypefaceSpan.class)[0].getFamily()) assertThat(secondCueText)
.isEqualTo("courier"); .hasNoForegroundColorSpanBetween("This ".length(), secondCueText.length());
text = getUniqueSpanTextAt(subtitle, /* timeUs= */ 2500000); Spanned thirdCueText = getUniqueSpanTextAt(subtitle, /* timeUs= */ 2_500_000);
assertThat(text.getSpans(/* start= */ 5, text.length(), StyleSpan.class)).hasLength(1); assertThat(thirdCueText).hasBoldSpanBetween("This ".length(), thirdCueText.length());
assertThat(text.getSpans(/* start= */ 5, text.length(), StyleSpan.class)[0].getStyle()) assertThat(thirdCueText)
.isEqualTo(Typeface.BOLD); .hasTypefaceSpanBetween("This ".length(), thirdCueText.length())
assertThat(text.getSpans(/* start= */ 5, text.length(), TypefaceSpan.class)).hasLength(1); .withFamily("courier");
assertThat(text.getSpans(/* start= */ 5, text.length(), TypefaceSpan.class)[0].getFamily())
.isEqualTo("courier"); Spanned fourthCueText = getUniqueSpanTextAt(subtitle, /* timeUs= */ 4_000_000);
assertThat(fourthCueText)
text = getUniqueSpanTextAt(subtitle, /* timeUs= */ 4000000); .hasNoStyleSpanBetween("This ".length(), "shouldn't be bold.".length());
assertThat(text.getSpans(/* start= */ 6, /* end= */ 22, StyleSpan.class)).hasLength(0); assertThat(fourthCueText)
assertThat(text.getSpans(/* start= */ 30, text.length(), StyleSpan.class)).hasLength(1); .hasBoldSpanBetween("This shouldn't be bold.\nThis ".length(), fourthCueText.length());
assertThat(text.getSpans(/* start= */ 30, text.length(), StyleSpan.class)[0].getStyle())
.isEqualTo(Typeface.BOLD); Spanned fifthCueText = getUniqueSpanTextAt(subtitle, /* timeUs= */ 5_000_000);
assertThat(fifthCueText)
text = getUniqueSpanTextAt(subtitle, /* timeUs= */ 5000000); .hasNoStyleSpanBetween("This is ".length(), "This is specific".length());
assertThat(text.getSpans(/* start= */ 9, /* end= */ 17, StyleSpan.class)).hasLength(0); assertThat(fifthCueText)
assertThat(text.getSpans(/* start= */ 19, text.length(), StyleSpan.class)).hasLength(1); .hasItalicSpanBetween("This is specific\n".length(), fifthCueText.length());
assertThat(text.getSpans(/* start= */ 19, text.length(), StyleSpan.class)[0].getStyle())
.isEqualTo(Typeface.ITALIC);
} }
@Test @Test
...@@ -387,6 +382,6 @@ public class WebvttDecoderTest { ...@@ -387,6 +382,6 @@ public class WebvttDecoderTest {
} }
private Spanned getUniqueSpanTextAt(WebvttSubtitle sub, long timeUs) { private Spanned getUniqueSpanTextAt(WebvttSubtitle sub, long timeUs) {
return (Spanned) sub.getCues(timeUs).get(0).text; return (Spanned) Assertions.checkNotNull(sub.getCues(timeUs).get(0).text);
} }
} }
...@@ -26,12 +26,14 @@ import android.text.TextUtils; ...@@ -26,12 +26,14 @@ import android.text.TextUtils;
import android.text.style.BackgroundColorSpan; import android.text.style.BackgroundColorSpan;
import android.text.style.ForegroundColorSpan; import android.text.style.ForegroundColorSpan;
import android.text.style.StyleSpan; import android.text.style.StyleSpan;
import android.text.style.TypefaceSpan;
import android.text.style.UnderlineSpan; import android.text.style.UnderlineSpan;
import androidx.annotation.CheckResult; import androidx.annotation.CheckResult;
import androidx.annotation.ColorInt; import androidx.annotation.ColorInt;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.text.span.HorizontalTextInVerticalContextSpan; import com.google.android.exoplayer2.text.span.HorizontalTextInVerticalContextSpan;
import com.google.android.exoplayer2.text.span.RubySpan; import com.google.android.exoplayer2.text.span.RubySpan;
import com.google.android.exoplayer2.util.Util;
import com.google.common.truth.FailureMetadata; import com.google.common.truth.FailureMetadata;
import com.google.common.truth.Subject; import com.google.common.truth.Subject;
import java.util.ArrayList; import java.util.ArrayList;
...@@ -172,6 +174,19 @@ public final class SpannedSubject extends Subject { ...@@ -172,6 +174,19 @@ public final class SpannedSubject extends Subject {
} }
/** /**
* Checks that the subject has no {@link StyleSpan}s on any of the text between {@code start} and
* {@code end}.
*
* <p>This fails even if the start and end indexes don't exactly match.
*
* @param start The start index to start searching for spans.
* @param end The end index to stop searching for spans.
*/
public void hasNoStyleSpanBetween(int start, int end) {
hasNoSpansOfTypeBetween(StyleSpan.class, start, end);
}
/**
* Checks that the subject has an {@link UnderlineSpan} from {@code start} to {@code end}. * Checks that the subject has an {@link UnderlineSpan} from {@code start} to {@code end}.
* *
* @param start The start of the expected span. * @param start The start of the expected span.
...@@ -195,6 +210,19 @@ public final class SpannedSubject extends Subject { ...@@ -195,6 +210,19 @@ public final class SpannedSubject extends Subject {
} }
/** /**
* Checks that the subject has no {@link UnderlineSpan}s on any of the text between {@code start}
* and {@code end}.
*
* <p>This fails even if the start and end indexes don't exactly match.
*
* @param start The start index to start searching for spans.
* @param end The end index to stop searching for spans.
*/
public void hasNoUnderlineSpanBetween(int start, int end) {
hasNoSpansOfTypeBetween(UnderlineSpan.class, start, end);
}
/**
* Checks that the subject has a {@link ForegroundColorSpan} from {@code start} to {@code end}. * Checks that the subject has 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. * <p>The color is asserted in a follow-up method call on the return {@link Colored} object.
...@@ -223,6 +251,19 @@ public final class SpannedSubject extends Subject { ...@@ -223,6 +251,19 @@ public final class SpannedSubject extends Subject {
} }
/** /**
* Checks that the subject has no {@link ForegroundColorSpan}s on any of the text between {@code
* start} and {@code end}.
*
* <p>This fails even if the start and end indexes don't exactly match.
*
* @param start The start index to start searching for spans.
* @param end The end index to stop searching for spans.
*/
public void hasNoForegroundColorSpanBetween(int start, int end) {
hasNoSpansOfTypeBetween(ForegroundColorSpan.class, start, end);
}
/**
* Checks that the subject has a {@link BackgroundColorSpan} from {@code start} to {@code end}. * Checks that the subject has a {@link BackgroundColorSpan} from {@code start} to {@code end}.
* *
* <p>The color is asserted in a follow-up method call on the return {@link Colored} object. * <p>The color is asserted in a follow-up method call on the return {@link Colored} object.
...@@ -251,6 +292,58 @@ public final class SpannedSubject extends Subject { ...@@ -251,6 +292,58 @@ public final class SpannedSubject extends Subject {
} }
/** /**
* Checks that the subject has no {@link BackgroundColorSpan}s on any of the text between {@code
* start} and {@code end}.
*
* <p>This fails even if the start and end indexes don't exactly match.
*
* @param start The start index to start searching for spans.
* @param end The end index to stop searching for spans.
*/
public void hasNoBackgroundColorSpanBetween(int start, int end) {
hasNoSpansOfTypeBetween(BackgroundColorSpan.class, start, end);
}
/**
* Checks that the subject has a {@link TypefaceSpan} from {@code start} to {@code end}.
*
* <p>The font is asserted in a follow-up method call on the return {@link Typefaced} object.
*
* @param start The start of the expected span.
* @param end The end of the expected span.
* @return A {@link Typefaced} object to assert on the font of the matching spans.
*/
@CheckResult
public Typefaced hasTypefaceSpanBetween(int start, int end) {
if (actual == null) {
failWithoutActual(simpleFact("Spanned must not be null"));
return ALREADY_FAILED_TYPEFACED;
}
List<TypefaceSpan> backgroundColorSpans = findMatchingSpans(start, end, TypefaceSpan.class);
if (backgroundColorSpans.isEmpty()) {
failWithExpectedSpan(start, end, TypefaceSpan.class, actual.toString().substring(start, end));
return ALREADY_FAILED_TYPEFACED;
}
return check("TypefaceSpan (start=%s,end=%s)", start, end)
.about(typefaceSpans(actual))
.that(backgroundColorSpans);
}
/**
* Checks that the subject has no {@link TypefaceSpan}s on any of the text between {@code start}
* and {@code end}.
*
* <p>This fails even if the start and end indexes don't exactly match.
*
* @param start The start index to start searching for spans.
* @param end The end index to stop searching for spans.
*/
public void hasNoTypefaceSpanBetween(int start, int end) {
hasNoSpansOfTypeBetween(TypefaceSpan.class, start, end);
}
/**
* Checks that the subject has a {@link RubySpan} from {@code start} to {@code end}. * Checks that the subject has a {@link RubySpan} from {@code start} to {@code end}.
* *
* <p>The ruby-text is asserted in a follow-up method call on the return {@link RubyText} object. * <p>The ruby-text is asserted in a follow-up method call on the return {@link RubyText} object.
...@@ -275,6 +368,19 @@ public final class SpannedSubject extends Subject { ...@@ -275,6 +368,19 @@ public final class SpannedSubject extends Subject {
} }
/** /**
* Checks that the subject has no {@link RubySpan}s on any of the text between {@code start} and
* {@code end}.
*
* <p>This fails even if the start and end indexes don't exactly match.
*
* @param start The start index to start searching for spans.
* @param end The end index to stop searching for spans.
*/
public void hasNoRubySpanBetween(int start, int end) {
hasNoSpansOfTypeBetween(RubySpan.class, start, end);
}
/**
* Checks that the subject has an {@link HorizontalTextInVerticalContextSpan} from {@code start} * Checks that the subject has an {@link HorizontalTextInVerticalContextSpan} from {@code start}
* to {@code end}. * to {@code end}.
* *
...@@ -303,6 +409,45 @@ public final class SpannedSubject extends Subject { ...@@ -303,6 +409,45 @@ public final class SpannedSubject extends Subject {
return ALREADY_FAILED_WITH_FLAGS; return ALREADY_FAILED_WITH_FLAGS;
} }
/**
* Checks that the subject has no {@link HorizontalTextInVerticalContextSpan}s on any of the text
* between {@code start} and {@code end}.
*
* <p>This fails even if the start and end indexes don't exactly match.
*
* @param start The start index to start searching for spans.
* @param end The end index to stop searching for spans.
*/
public void hasNoHorizontalTextInVerticalContextSpanBetween(int start, int end) {
hasNoSpansOfTypeBetween(HorizontalTextInVerticalContextSpan.class, start, end);
}
/**
* Checks that the subject has no spans of type {@code spanClazz} on any of the text between
* {@code start} and {@code end}.
*
* <p>This fails even if the start and end indexes don't exactly match.
*
* @param start The start index to start searching for spans.
* @param end The end index to stop searching for spans.
*/
private void hasNoSpansOfTypeBetween(Class<?> spanClazz, int start, int end) {
if (actual == null) {
failWithoutActual(simpleFact("Spanned must not be null"));
return;
}
Object[] matchingSpans = actual.getSpans(start, end, spanClazz);
if (matchingSpans.length != 0) {
failWithoutActual(
simpleFact(
String.format(
"Found unexpected %ss between start=%s,end=%s",
spanClazz.getSimpleName(), start, end)),
simpleFact("expected none"),
fact("but found", getAllSpansAsString(actual)));
}
}
private <T> List<T> findMatchingSpans(int startIndex, int endIndex, Class<T> spanClazz) { private <T> List<T> findMatchingSpans(int startIndex, int endIndex, Class<T> spanClazz) {
List<T> spans = new ArrayList<>(); List<T> spans = new ArrayList<>();
for (T span : actual.getSpans(startIndex, endIndex, spanClazz)) { for (T span : actual.getSpans(startIndex, endIndex, spanClazz)) {
...@@ -421,8 +566,8 @@ public final class SpannedSubject extends Subject { ...@@ -421,8 +566,8 @@ public final class SpannedSubject extends Subject {
private static final Colored ALREADY_FAILED_COLORED = color -> ALREADY_FAILED_AND_FLAGS; private static final Colored ALREADY_FAILED_COLORED = color -> ALREADY_FAILED_AND_FLAGS;
private Factory<ForegroundColorSpansSubject, List<ForegroundColorSpan>> foregroundColorSpans( private static Factory<ForegroundColorSpansSubject, List<ForegroundColorSpan>>
Spanned actualSpanned) { foregroundColorSpans(Spanned actualSpanned) {
return (FailureMetadata metadata, List<ForegroundColorSpan> spans) -> return (FailureMetadata metadata, List<ForegroundColorSpan> spans) ->
new ForegroundColorSpansSubject(metadata, spans, actualSpanned); new ForegroundColorSpansSubject(metadata, spans, actualSpanned);
} }
...@@ -458,8 +603,8 @@ public final class SpannedSubject extends Subject { ...@@ -458,8 +603,8 @@ public final class SpannedSubject extends Subject {
} }
} }
private Factory<BackgroundColorSpansSubject, List<BackgroundColorSpan>> backgroundColorSpans( private static Factory<BackgroundColorSpansSubject, List<BackgroundColorSpan>>
Spanned actualSpanned) { backgroundColorSpans(Spanned actualSpanned) {
return (FailureMetadata metadata, List<BackgroundColorSpan> spans) -> return (FailureMetadata metadata, List<BackgroundColorSpan> spans) ->
new BackgroundColorSpansSubject(metadata, spans, actualSpanned); new BackgroundColorSpansSubject(metadata, spans, actualSpanned);
} }
...@@ -495,6 +640,55 @@ public final class SpannedSubject extends Subject { ...@@ -495,6 +640,55 @@ public final class SpannedSubject extends Subject {
} }
} }
/** Allows assertions about the typeface of a span. */
public interface Typefaced {
/**
* Checks that at least one of the matched spans has the expected {@code fontFamily}.
*
* @param fontFamily The expected font family.
* @return A {@link WithSpanFlags} object for optional additional assertions on the flags.
*/
AndSpanFlags withFamily(String fontFamily);
}
private static final Typefaced ALREADY_FAILED_TYPEFACED = color -> ALREADY_FAILED_AND_FLAGS;
private static Factory<TypefaceSpansSubject, List<TypefaceSpan>> typefaceSpans(
Spanned actualSpanned) {
return (FailureMetadata metadata, List<TypefaceSpan> spans) ->
new TypefaceSpansSubject(metadata, spans, actualSpanned);
}
private static final class TypefaceSpansSubject extends Subject implements Typefaced {
private final List<TypefaceSpan> actualSpans;
private final Spanned actualSpanned;
private TypefaceSpansSubject(
FailureMetadata metadata, List<TypefaceSpan> actualSpans, Spanned actualSpanned) {
super(metadata, actualSpans);
this.actualSpans = actualSpans;
this.actualSpanned = actualSpanned;
}
@Override
public AndSpanFlags withFamily(String fontFamily) {
List<Integer> matchingSpanFlags = new ArrayList<>();
List<String> spanFontFamilies = new ArrayList<>();
for (TypefaceSpan span : actualSpans) {
spanFontFamilies.add(span.getFamily());
if (Util.areEqual(span.getFamily(), fontFamily)) {
matchingSpanFlags.add(actualSpanned.getSpanFlags(span));
}
}
check("family").that(spanFontFamilies).containsExactly(fontFamily);
return check("flags").about(spanFlags()).that(matchingSpanFlags);
}
}
/** Allows assertions about a span's ruby text and its position. */ /** Allows assertions about a span's ruby text and its position. */
public interface RubyText { public interface RubyText {
...@@ -511,7 +705,7 @@ public final class SpannedSubject extends Subject { ...@@ -511,7 +705,7 @@ public final class SpannedSubject extends Subject {
private static final RubyText ALREADY_FAILED_WITH_TEXT = private static final RubyText ALREADY_FAILED_WITH_TEXT =
(text, position) -> ALREADY_FAILED_AND_FLAGS; (text, position) -> ALREADY_FAILED_AND_FLAGS;
private Factory<RubySpansSubject, List<RubySpan>> rubySpans(Spanned actualSpanned) { private static Factory<RubySpansSubject, List<RubySpan>> rubySpans(Spanned actualSpanned) {
return (FailureMetadata metadata, List<RubySpan> spans) -> return (FailureMetadata metadata, List<RubySpan> spans) ->
new RubySpansSubject(metadata, spans, actualSpanned); new RubySpansSubject(metadata, spans, actualSpanned);
} }
...@@ -544,7 +738,7 @@ public final class SpannedSubject extends Subject { ...@@ -544,7 +738,7 @@ public final class SpannedSubject extends Subject {
return check("flags").about(spanFlags()).that(matchingSpanFlags); return check("flags").about(spanFlags()).that(matchingSpanFlags);
} }
private static class TextAndPosition { private static final class TextAndPosition {
private final String text; private final String text;
@RubySpan.Position private final int position; @RubySpan.Position private final int position;
......
...@@ -28,6 +28,7 @@ import android.text.Spanned; ...@@ -28,6 +28,7 @@ import android.text.Spanned;
import android.text.style.BackgroundColorSpan; import android.text.style.BackgroundColorSpan;
import android.text.style.ForegroundColorSpan; import android.text.style.ForegroundColorSpan;
import android.text.style.StyleSpan; import android.text.style.StyleSpan;
import android.text.style.TypefaceSpan;
import android.text.style.UnderlineSpan; import android.text.style.UnderlineSpan;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.text.span.HorizontalTextInVerticalContextSpan; import com.google.android.exoplayer2.text.span.HorizontalTextInVerticalContextSpan;
...@@ -171,6 +172,40 @@ public class SpannedSubjectTest { ...@@ -171,6 +172,40 @@ public class SpannedSubjectTest {
} }
@Test @Test
public void noStyleSpan_success() {
SpannableString spannable = SpannableString.valueOf("test with underline then italic spans");
spannable.setSpan(
new UnderlineSpan(),
"test with ".length(),
"test with underline".length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
spannable.setSpan(
new StyleSpan(Typeface.ITALIC),
"test with underline then ".length(),
"test with underline then italic".length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
assertThat(spannable).hasNoStyleSpanBetween(0, "test with underline then".length());
}
@Test
public void noStyleSpan_failure() {
SpannableString spannable = SpannableString.valueOf("test with italic section");
int start = "test with ".length();
int end = start + "italic".length();
spannable.setSpan(new StyleSpan(Typeface.ITALIC), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
AssertionError expected =
expectFailure(
whenTesting -> whenTesting.that(spannable).hasNoStyleSpanBetween(start + 1, end));
assertThat(expected)
.factKeys()
.contains("Found unexpected StyleSpans between start=" + (start + 1) + ",end=" + end);
assertThat(expected).factKeys().contains("expected none");
assertThat(expected).factValue("but found").contains("start=" + start);
}
@Test
public void underlineSpan_success() { public void underlineSpan_success() {
SpannableString spannable = SpannableString.valueOf("test with underlined section"); SpannableString spannable = SpannableString.valueOf("test with underlined section");
int start = "test with ".length(); int start = "test with ".length();
...@@ -183,6 +218,40 @@ public class SpannedSubjectTest { ...@@ -183,6 +218,40 @@ public class SpannedSubjectTest {
} }
@Test @Test
public void noUnderlineSpan_success() {
SpannableString spannable = SpannableString.valueOf("test with italic then underline spans");
spannable.setSpan(
new StyleSpan(Typeface.ITALIC),
"test with ".length(),
"test with italic".length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
spannable.setSpan(
new UnderlineSpan(),
"test with italic then ".length(),
"test with italic then underline".length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
assertThat(spannable).hasNoUnderlineSpanBetween(0, "test with italic then".length());
}
@Test
public void noUnderlineSpan_failure() {
SpannableString spannable = SpannableString.valueOf("test with underline section");
int start = "test with ".length();
int end = start + "underline".length();
spannable.setSpan(new UnderlineSpan(), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
AssertionError expected =
expectFailure(
whenTesting -> whenTesting.that(spannable).hasNoUnderlineSpanBetween(start + 1, end));
assertThat(expected)
.factKeys()
.contains("Found unexpected UnderlineSpans between start=" + (start + 1) + ",end=" + end);
assertThat(expected).factKeys().contains("expected none");
assertThat(expected).factValue("but found").contains("start=" + start);
}
@Test
public void foregroundColorSpan_success() { public void foregroundColorSpan_success() {
SpannableString spannable = SpannableString.valueOf("test with cyan section"); SpannableString spannable = SpannableString.valueOf("test with cyan section");
int start = "test with ".length(); int start = "test with ".length();
...@@ -262,6 +331,43 @@ public class SpannedSubjectTest { ...@@ -262,6 +331,43 @@ public class SpannedSubjectTest {
} }
@Test @Test
public void noForegroundColorSpan_success() {
SpannableString spannable = SpannableString.valueOf("test with underline then cyan spans");
spannable.setSpan(
new UnderlineSpan(),
"test with ".length(),
"test with underline".length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
spannable.setSpan(
new ForegroundColorSpan(Color.CYAN),
"test with underline then ".length(),
"test with underline then cyan".length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
assertThat(spannable).hasNoForegroundColorSpanBetween(0, "test with underline then".length());
}
@Test
public void noForegroundColorSpan_failure() {
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).hasNoForegroundColorSpanBetween(start + 1, end));
assertThat(expected)
.factKeys()
.contains(
"Found unexpected ForegroundColorSpans between start=" + (start + 1) + ",end=" + end);
assertThat(expected).factKeys().contains("expected none");
assertThat(expected).factValue("but found").contains("start=" + start);
}
@Test
public void backgroundColorSpan_success() { public void backgroundColorSpan_success() {
SpannableString spannable = SpannableString.valueOf("test with cyan section"); SpannableString spannable = SpannableString.valueOf("test with cyan section");
int start = "test with ".length(); int start = "test with ".length();
...@@ -341,6 +447,152 @@ public class SpannedSubjectTest { ...@@ -341,6 +447,152 @@ public class SpannedSubjectTest {
} }
@Test @Test
public void noBackgroundColorSpan_success() {
SpannableString spannable = SpannableString.valueOf("test with underline then cyan spans");
spannable.setSpan(
new UnderlineSpan(),
"test with ".length(),
"test with underline".length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
spannable.setSpan(
new BackgroundColorSpan(Color.CYAN),
"test with underline then ".length(),
"test with underline then cyan".length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
assertThat(spannable).hasNoBackgroundColorSpanBetween(0, "test with underline then".length());
}
@Test
public void noBackgroundColorSpan_failure() {
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).hasNoBackgroundColorSpanBetween(start + 1, end));
assertThat(expected)
.factKeys()
.contains(
"Found unexpected BackgroundColorSpans between start=" + (start + 1) + ",end=" + end);
assertThat(expected).factKeys().contains("expected none");
assertThat(expected).factValue("but found").contains("start=" + start);
}
@Test
public void typefaceSpan_success() {
SpannableString spannable = SpannableString.valueOf("test with courier section");
int start = "test with ".length();
int end = start + "courier".length();
spannable.setSpan(new TypefaceSpan("courier"), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
assertThat(spannable)
.hasTypefaceSpanBetween(start, end)
.withFamily("courier")
.andFlags(Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
}
@Test
public void typefaceSpan_wrongEndIndex() {
SpannableString spannable = SpannableString.valueOf("test with courier section");
int start = "test with ".length();
int end = start + "courier".length();
spannable.setSpan(new TypefaceSpan("courier"), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
int incorrectEnd = end + 2;
AssertionError expected =
expectFailure(
whenTesting ->
whenTesting
.that(spannable)
.hasTypefaceSpanBetween(start, incorrectEnd)
.withFamily("courier"));
assertThat(expected).factValue("expected").contains("end=" + incorrectEnd);
assertThat(expected).factValue("but found").contains("end=" + end);
}
@Test
public void typefaceSpan_wrongFamily() {
SpannableString spannable = SpannableString.valueOf("test with courier section");
int start = "test with ".length();
int end = start + "courier".length();
spannable.setSpan(new TypefaceSpan("courier"), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
AssertionError expected =
expectFailure(
whenTesting ->
whenTesting
.that(spannable)
.hasTypefaceSpanBetween(start, end)
.withFamily("roboto"));
assertThat(expected).factValue("value of").contains("family");
assertThat(expected).factValue("expected").contains("roboto");
assertThat(expected).factValue("but was").contains("courier");
}
@Test
public void typefaceSpan_wrongFlags() {
SpannableString spannable = SpannableString.valueOf("test with courier section");
int start = "test with ".length();
int end = start + "courier".length();
spannable.setSpan(new TypefaceSpan("courier"), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
AssertionError expected =
expectFailure(
whenTesting ->
whenTesting
.that(spannable)
.hasTypefaceSpanBetween(start, end)
.withFamily("courier")
.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 noTypefaceSpan_success() {
SpannableString spannable = SpannableString.valueOf("test with underline then courier spans");
spannable.setSpan(
new UnderlineSpan(),
"test with ".length(),
"test with underline".length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
spannable.setSpan(
new TypefaceSpan("courier"),
"test with underline then ".length(),
"test with underline then courier".length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
assertThat(spannable).hasNoTypefaceSpanBetween(0, "test with underline then".length());
}
@Test
public void noTypefaceSpan_failure() {
SpannableString spannable = SpannableString.valueOf("test with courier section");
int start = "test with ".length();
int end = start + "courier".length();
spannable.setSpan(new TypefaceSpan("courier"), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
AssertionError expected =
expectFailure(
whenTesting -> whenTesting.that(spannable).hasNoTypefaceSpanBetween(start + 1, end));
assertThat(expected)
.factKeys()
.contains("Found unexpected TypefaceSpans between start=" + (start + 1) + ",end=" + end);
assertThat(expected).factKeys().contains("expected none");
assertThat(expected).factValue("but found").contains("start=" + start);
}
@Test
public void rubySpan_success() { public void rubySpan_success() {
SpannableString spannable = SpannableString.valueOf("test with rubied section"); SpannableString spannable = SpannableString.valueOf("test with rubied section");
int start = "test with ".length(); int start = "test with ".length();
...@@ -455,6 +707,44 @@ public class SpannedSubjectTest { ...@@ -455,6 +707,44 @@ public class SpannedSubjectTest {
} }
@Test @Test
public void noRubySpan_success() {
SpannableString spannable = SpannableString.valueOf("test with underline then ruby spans");
spannable.setSpan(
new UnderlineSpan(),
"test with ".length(),
"test with underline".length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
spannable.setSpan(
new RubySpan("ruby text", RubySpan.POSITION_OVER),
"test with underline then ".length(),
"test with underline then ruby".length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
assertThat(spannable).hasNoRubySpanBetween(0, "test with underline then".length());
}
@Test
public void noRubySpan_failure() {
SpannableString spannable = SpannableString.valueOf("test with ruby section");
int start = "test with ".length();
int end = start + "ruby".length();
spannable.setSpan(
new RubySpan("ruby text", RubySpan.POSITION_OVER),
start,
end,
Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
AssertionError expected =
expectFailure(
whenTesting -> whenTesting.that(spannable).hasNoRubySpanBetween(start + 1, end));
assertThat(expected)
.factKeys()
.contains("Found unexpected RubySpans between start=" + (start + 1) + ",end=" + end);
assertThat(expected).factKeys().contains("expected none");
assertThat(expected).factValue("but found").contains("start=" + start);
}
@Test
public void horizontalTextInVerticalContextSpan_success() { public void horizontalTextInVerticalContextSpan_success() {
SpannableString spannable = SpannableString.valueOf("vertical text with horizontal section"); SpannableString spannable = SpannableString.valueOf("vertical text with horizontal section");
int start = "vertical text with ".length(); int start = "vertical text with ".length();
...@@ -467,6 +757,50 @@ public class SpannedSubjectTest { ...@@ -467,6 +757,50 @@ public class SpannedSubjectTest {
.withFlags(Spanned.SPAN_INCLUSIVE_EXCLUSIVE); .withFlags(Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
} }
@Test
public void noHorizontalTextInVerticalContextSpan_success() {
SpannableString spannable =
SpannableString.valueOf("test with underline then tate-chu-yoko spans");
spannable.setSpan(
new UnderlineSpan(),
"test with ".length(),
"test with underline".length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
spannable.setSpan(
new HorizontalTextInVerticalContextSpan(),
"test with underline then ".length(),
"test with underline then tate-chu-yoko".length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
assertThat(spannable)
.hasNoHorizontalTextInVerticalContextSpanBetween(0, "test with underline then".length());
}
@Test
public void noHorizontalTextInVerticalContextSpan_failure() {
SpannableString spannable = SpannableString.valueOf("test with tate-chu-yoko section");
int start = "test with ".length();
int end = start + "tate-chu-yoko".length();
spannable.setSpan(
new HorizontalTextInVerticalContextSpan(), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
AssertionError expected =
expectFailure(
whenTesting ->
whenTesting
.that(spannable)
.hasNoHorizontalTextInVerticalContextSpanBetween(start + 1, end));
assertThat(expected)
.factKeys()
.contains(
"Found unexpected HorizontalTextInVerticalContextSpans between start="
+ (start + 1)
+ ",end="
+ end);
assertThat(expected).factKeys().contains("expected none");
assertThat(expected).factValue("but found").contains("start=" + start);
}
private static AssertionError expectFailure( private static AssertionError expectFailure(
ExpectFailure.SimpleSubjectBuilderCallback<SpannedSubject, Spanned> callback) { ExpectFailure.SimpleSubjectBuilderCallback<SpannedSubject, Spanned> callback) {
return expectFailureAbout(spanned(), callback); return expectFailureAbout(spanned(), callback);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment