Add TypefaceSpan and hasNoFooSpanBetween() support to SpannedSubject

Use these to migrate the last WebvttDecoderTest method to SpannedSubject

PiperOrigin-RevId: 288688620
This commit is contained in:
ibaker 2020-01-08 15:04:09 +00:00 committed by Oliver Woodman
parent ee091e6a45
commit 448db89446
4 changed files with 570 additions and 47 deletions

View file

@ -20,7 +20,7 @@ STYLE
id
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
00:02.000 --> 00:02.001
@ -31,10 +31,10 @@ _id
This <lang.class.another>should be courier and bold.
00:04.000 --> 00:04.001
This <v Strider Trancos> shouldn't be bold.</v>
This <v.class.clazz Strider Trancos> should be bold.
This <v Strider Trancos>shouldn't be bold.</v>
This <v.class.clazz Strider Trancos>should be bold.
anId
00:05.000 --> 00:05.001
This is <v.class1.class3.class2 Pipo> specific </v>
<v.class1.class3.class2 Robert> But this is more italic</v>
This is <v.class1.class3.class2 Pipo>specific</v>
<v.class1.class3.class2 Robert>But this is more italic</v>

View file

@ -19,17 +19,14 @@ import static com.google.android.exoplayer2.testutil.truth.SpannedSubject.assert
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.ForegroundColorSpan;
import android.text.style.StyleSpan;
import android.text.style.TypefaceSpan;
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.Assertions;
import com.google.android.exoplayer2.util.ColorParser;
import com.google.common.collect.Iterables;
import com.google.common.truth.Expect;
@ -328,41 +325,39 @@ public class WebvttDecoderTest {
@Test
public void testWithComplexCssSelectors() throws Exception {
WebvttSubtitle subtitle = getSubtitleForTestAsset(WITH_CSS_COMPLEX_SELECTORS);
Spanned text = getUniqueSpanTextAt(subtitle, /* timeUs= */ 0);
assertThat(text.getSpans(/* start= */ 30, text.length(), ForegroundColorSpan.class))
.hasLength(1);
assertThat(
text.getSpans(/* start= */ 30, text.length(), ForegroundColorSpan.class)[0]
.getForegroundColor())
.isEqualTo(0xFFEE82EE);
assertThat(text.getSpans(/* start= */ 30, text.length(), TypefaceSpan.class)).hasLength(1);
assertThat(text.getSpans(/* start= */ 30, text.length(), TypefaceSpan.class)[0].getFamily())
.isEqualTo("courier");
Spanned firstCueText = getUniqueSpanTextAt(subtitle, /* timeUs= */ 0);
assertThat(firstCueText)
.hasForegroundColorSpanBetween(
"This should be underlined and ".length(), firstCueText.length())
.withColor(ColorParser.parseCssColor("violet"));
assertThat(firstCueText)
.hasTypefaceSpanBetween("This should be underlined and ".length(), firstCueText.length())
.withFamily("courier");
text = getUniqueSpanTextAt(subtitle, /* timeUs= */ 2000000);
assertThat(text.getSpans(/* start= */ 5, text.length(), TypefaceSpan.class)).hasLength(1);
assertThat(text.getSpans(/* start= */ 5, text.length(), TypefaceSpan.class)[0].getFamily())
.isEqualTo("courier");
Spanned secondCueText = getUniqueSpanTextAt(subtitle, /* timeUs= */ 2_000_000);
assertThat(secondCueText)
.hasTypefaceSpanBetween("This ".length(), secondCueText.length())
.withFamily("courier");
assertThat(secondCueText)
.hasNoForegroundColorSpanBetween("This ".length(), secondCueText.length());
text = getUniqueSpanTextAt(subtitle, /* timeUs= */ 2500000);
assertThat(text.getSpans(/* start= */ 5, text.length(), StyleSpan.class)).hasLength(1);
assertThat(text.getSpans(/* start= */ 5, text.length(), StyleSpan.class)[0].getStyle())
.isEqualTo(Typeface.BOLD);
assertThat(text.getSpans(/* start= */ 5, text.length(), TypefaceSpan.class)).hasLength(1);
assertThat(text.getSpans(/* start= */ 5, text.length(), TypefaceSpan.class)[0].getFamily())
.isEqualTo("courier");
Spanned thirdCueText = getUniqueSpanTextAt(subtitle, /* timeUs= */ 2_500_000);
assertThat(thirdCueText).hasBoldSpanBetween("This ".length(), thirdCueText.length());
assertThat(thirdCueText)
.hasTypefaceSpanBetween("This ".length(), thirdCueText.length())
.withFamily("courier");
text = getUniqueSpanTextAt(subtitle, /* timeUs= */ 4000000);
assertThat(text.getSpans(/* start= */ 6, /* end= */ 22, StyleSpan.class)).hasLength(0);
assertThat(text.getSpans(/* start= */ 30, text.length(), StyleSpan.class)).hasLength(1);
assertThat(text.getSpans(/* start= */ 30, text.length(), StyleSpan.class)[0].getStyle())
.isEqualTo(Typeface.BOLD);
Spanned fourthCueText = getUniqueSpanTextAt(subtitle, /* timeUs= */ 4_000_000);
assertThat(fourthCueText)
.hasNoStyleSpanBetween("This ".length(), "shouldn't be bold.".length());
assertThat(fourthCueText)
.hasBoldSpanBetween("This shouldn't be bold.\nThis ".length(), fourthCueText.length());
text = getUniqueSpanTextAt(subtitle, /* timeUs= */ 5000000);
assertThat(text.getSpans(/* start= */ 9, /* end= */ 17, StyleSpan.class)).hasLength(0);
assertThat(text.getSpans(/* start= */ 19, text.length(), StyleSpan.class)).hasLength(1);
assertThat(text.getSpans(/* start= */ 19, text.length(), StyleSpan.class)[0].getStyle())
.isEqualTo(Typeface.ITALIC);
Spanned fifthCueText = getUniqueSpanTextAt(subtitle, /* timeUs= */ 5_000_000);
assertThat(fifthCueText)
.hasNoStyleSpanBetween("This is ".length(), "This is specific".length());
assertThat(fifthCueText)
.hasItalicSpanBetween("This is specific\n".length(), fifthCueText.length());
}
@Test
@ -387,6 +382,6 @@ public class WebvttDecoderTest {
}
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);
}
}

View file

@ -26,12 +26,14 @@ import android.text.TextUtils;
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.CheckResult;
import androidx.annotation.ColorInt;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.text.span.HorizontalTextInVerticalContextSpan;
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.Subject;
import java.util.ArrayList;
@ -171,6 +173,19 @@ public final class SpannedSubject extends Subject {
return ALREADY_FAILED_WITH_FLAGS;
}
/**
* 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}.
*
@ -194,6 +209,19 @@ public final class SpannedSubject extends Subject {
return ALREADY_FAILED_WITH_FLAGS;
}
/**
* 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}.
*
@ -222,6 +250,19 @@ public final class SpannedSubject extends Subject {
.that(foregroundColorSpans);
}
/**
* 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}.
*
@ -250,6 +291,58 @@ public final class SpannedSubject extends Subject {
.that(backgroundColorSpans);
}
/**
* 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}.
*
@ -274,6 +367,19 @@ public final class SpannedSubject extends Subject {
return check("RubySpan (start=%s,end=%s)", start, end).about(rubySpans(actual)).that(rubySpans);
}
/**
* 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}
* to {@code end}.
@ -303,6 +409,45 @@ public final class SpannedSubject extends Subject {
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) {
List<T> spans = new ArrayList<>();
for (T span : actual.getSpans(startIndex, endIndex, spanClazz)) {
@ -421,8 +566,8 @@ public final class SpannedSubject extends Subject {
private static final Colored ALREADY_FAILED_COLORED = color -> ALREADY_FAILED_AND_FLAGS;
private Factory<ForegroundColorSpansSubject, List<ForegroundColorSpan>> foregroundColorSpans(
Spanned actualSpanned) {
private static Factory<ForegroundColorSpansSubject, List<ForegroundColorSpan>>
foregroundColorSpans(Spanned actualSpanned) {
return (FailureMetadata metadata, List<ForegroundColorSpan> spans) ->
new ForegroundColorSpansSubject(metadata, spans, actualSpanned);
}
@ -458,8 +603,8 @@ public final class SpannedSubject extends Subject {
}
}
private Factory<BackgroundColorSpansSubject, List<BackgroundColorSpan>> backgroundColorSpans(
Spanned actualSpanned) {
private static Factory<BackgroundColorSpansSubject, List<BackgroundColorSpan>>
backgroundColorSpans(Spanned actualSpanned) {
return (FailureMetadata metadata, List<BackgroundColorSpan> spans) ->
new BackgroundColorSpansSubject(metadata, spans, actualSpanned);
}
@ -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. */
public interface RubyText {
@ -511,7 +705,7 @@ public final class SpannedSubject extends Subject {
private static final RubyText ALREADY_FAILED_WITH_TEXT =
(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) ->
new RubySpansSubject(metadata, spans, actualSpanned);
}
@ -544,7 +738,7 @@ public final class SpannedSubject extends Subject {
return check("flags").about(spanFlags()).that(matchingSpanFlags);
}
private static class TextAndPosition {
private static final class TextAndPosition {
private final String text;
@RubySpan.Position private final int position;

View file

@ -28,6 +28,7 @@ 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.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.text.span.HorizontalTextInVerticalContextSpan;
@ -170,6 +171,40 @@ public class SpannedSubjectTest {
assertThat(expected).factValue("but found").contains("start=" + start);
}
@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() {
SpannableString spannable = SpannableString.valueOf("test with underlined section");
@ -182,6 +217,40 @@ public class SpannedSubjectTest {
.withFlags(Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
}
@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() {
SpannableString spannable = SpannableString.valueOf("test with cyan section");
@ -261,6 +330,43 @@ public class SpannedSubjectTest {
.contains(String.valueOf(Spanned.SPAN_INCLUSIVE_EXCLUSIVE));
}
@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() {
SpannableString spannable = SpannableString.valueOf("test with cyan section");
@ -340,6 +446,152 @@ public class SpannedSubjectTest {
.contains(String.valueOf(Spanned.SPAN_INCLUSIVE_EXCLUSIVE));
}
@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() {
SpannableString spannable = SpannableString.valueOf("test with rubied section");
@ -454,6 +706,44 @@ public class SpannedSubjectTest {
.contains(String.valueOf(Spanned.SPAN_INCLUSIVE_EXCLUSIVE));
}
@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() {
SpannableString spannable = SpannableString.valueOf("vertical text with horizontal section");
@ -467,6 +757,50 @@ public class SpannedSubjectTest {
.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(
ExpectFailure.SimpleSubjectBuilderCallback<SpannedSubject, Spanned> callback) {
return expectFailureAbout(spanned(), callback);