diff --git a/library/core/src/test/java/com/google/android/exoplayer2/text/webvtt/WebvttCueParserTest.java b/library/core/src/test/java/com/google/android/exoplayer2/text/webvtt/WebvttCueParserTest.java index c9e8488c60..ec4ed10f3d 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/text/webvtt/WebvttCueParserTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/text/webvtt/WebvttCueParserTest.java @@ -36,10 +36,7 @@ public final class WebvttCueParserTest { assertThat(text.toString()).isEqualTo("This is text with html tags"); assertThat(text).hasUnderlineSpanBetween("This ".length(), "This is".length()); assertThat(text) - .hasBoldItalicSpan( - "This is text with ".length(), - "This is text with html".length(), - Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + .hasBoldItalicSpanBetween("This is text with ".length(), "This is text with html".length()); } @Test @@ -60,10 +57,8 @@ public final class WebvttCueParserTest { assertThat(text) .hasUnderlineSpanBetween("An ".length(), "An unclosed u tag with italic inside".length()); assertThat(text) - .hasItalicSpan( - "An unclosed u tag with ".length(), - "An unclosed u tag with italic".length(), - Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + .hasItalicSpanBetween( + "An unclosed u tag with ".length(), "An unclosed u tag with italic".length()); } @Test @@ -72,10 +67,9 @@ public final class WebvttCueParserTest { assertThat(text.toString()).isEqualTo("An italic tag with unclosed underline inside"); assertThat(text) - .hasItalicSpan( + .hasItalicSpanBetween( "An italic tag with unclosed ".length(), - "An italic tag with unclosed underline".length(), - Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + "An italic tag with unclosed underline".length()); assertThat(text) .hasUnderlineSpanBetween( "An italic tag with unclosed ".length(), @@ -88,15 +82,11 @@ public final class WebvttCueParserTest { String expectedText = "Overlapping u and i tags"; assertThat(text.toString()).isEqualTo(expectedText); - assertThat(text).hasBoldSpan(0, expectedText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + assertThat(text).hasBoldSpanBetween(0, expectedText.length()); // Text between the tags is underlined. assertThat(text).hasUnderlineSpanBetween(0, "Overlapping u and".length()); // Only text from to <\\u> is italic (unexpected - but simplifies the parsing). - assertThat(text) - .hasItalicSpan( - "Overlapping u ".length(), - "Overlapping u and".length(), - Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + assertThat(text).hasItalicSpanBetween("Overlapping u ".length(), "Overlapping u and".length()); } @Test @@ -105,8 +95,7 @@ public final class WebvttCueParserTest { assertThat(text.toString()).isEqualTo("foobarbazbuzz"); // endIndex should be 9 when valid (i.e. "foobarbaz".length() - assertThat(text) - .hasBoldSpan("foo".length(), "foobar".length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + assertThat(text).hasBoldSpanBetween("foo".length(), "foobar".length()); } @Test @@ -156,13 +145,8 @@ public final class WebvttCueParserTest { Spanned text = parseCueText("blah blah blah foo"); assertThat(text.toString()).isEqualTo("blah blah blah foo"); - assertThat(text) - .hasBoldSpan("blah ".length(), "blah blah".length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - assertThat(text) - .hasBoldSpan( - "blah blah blah ".length(), - "blah blah blah foo".length(), - Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + assertThat(text).hasBoldSpanBetween("blah ".length(), "blah blah".length()); + assertThat(text).hasBoldSpanBetween("blah blah blah ".length(), "blah blah blah foo".length()); } @Test diff --git a/library/core/src/test/java/com/google/android/exoplayer2/text/webvtt/WebvttDecoderTest.java b/library/core/src/test/java/com/google/android/exoplayer2/text/webvtt/WebvttDecoderTest.java index a3ab3e8b1a..063d4e1bfd 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/text/webvtt/WebvttDecoderTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/text/webvtt/WebvttDecoderTest.java @@ -417,11 +417,7 @@ public class WebvttDecoderTest { assertThat(s4) .hasBackgroundColorSpanBetween(0, 16) .withColor(ColorParser.parseCssColor("lime")); - assertThat(s4) - .hasBoldSpan( - /* startIndex= */ 17, - /* endIndex= */ s4.length(), - /* flags= */ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + assertThat(s4).hasBoldSpanBetween(/* startIndex= */ 17, /* endIndex= */ s4.length()); } @Test diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/truth/SpannedSubject.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/truth/SpannedSubject.java index 84d40fb6f1..16144a170b 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/truth/SpannedSubject.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/truth/SpannedSubject.java @@ -33,6 +33,7 @@ import androidx.annotation.Nullable; import com.google.common.truth.FailureMetadata; import com.google.common.truth.Subject; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** A Truth {@link Subject} for assertions on {@link Spanned} instances containing text styling. */ @@ -68,59 +69,59 @@ public final class SpannedSubject extends Subject { failWithoutActual( simpleFact("Expected no spans"), fact("in text", actual), - fact("but found", getAllSpansAsStringWithoutFlags(actual))); + fact("but found", getAllSpansAsString(actual))); } } /** - * Checks that the subject has an italic span from {@code startIndex} to {@code endIndex}. + * Checks that the subject has an italic span 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. */ - // TODO: swap this to fluent-style. - public void hasItalicSpan(int startIndex, int endIndex, int flags) { - hasStyleSpan(startIndex, endIndex, flags, Typeface.ITALIC); + public WithSpanFlags hasItalicSpanBetween(int start, int end) { + return hasStyleSpan(start, end, Typeface.ITALIC); } /** - * Checks that the subject has a bold span from {@code startIndex} to {@code endIndex}. + * Checks that the subject has a bold span 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. */ - // TODO: swap this to fluent-style. - public void hasBoldSpan(int startIndex, int endIndex, int flags) { - hasStyleSpan(startIndex, endIndex, flags, Typeface.BOLD); + public WithSpanFlags hasBoldSpanBetween(int start, int end) { + return hasStyleSpan(start, end, Typeface.BOLD); } - private void hasStyleSpan(int startIndex, int endIndex, int flags, int style) { + private WithSpanFlags hasStyleSpan(int start, int end, int style) { if (actual == null) { failWithoutActual(simpleFact("Spanned must not be null")); - return; + return ALREADY_FAILED_WITH_FLAGS; } - for (StyleSpan span : findMatchingSpans(startIndex, endIndex, flags, StyleSpan.class)) { + List allFlags = new ArrayList<>(); + boolean matchingSpanFound = false; + for (StyleSpan span : findMatchingSpans(start, end, StyleSpan.class)) { + allFlags.add(actual.getSpanFlags(span)); if (span.getStyle() == style) { - return; + matchingSpanFound = true; + break; } } + if (matchingSpanFound) { + return check("StyleSpan (start=%s,end=%s,style=%s)", start, end, style) + .about(spanFlags()) + .that(allFlags); + } - failWithExpectedSpanWithFlags( - startIndex, - endIndex, - flags, - new StyleSpan(style), - actual.toString().substring(startIndex, endIndex)); + failWithExpectedSpan(start, end, StyleSpan.class, actual.toString().substring(start, end)); + return ALREADY_FAILED_WITH_FLAGS; } /** - * Checks that the subject has bold and italic styling from {@code startIndex} to {@code - * endIndex}. + * Checks that the subject has bold and italic styling from {@code start} to {@code end}. * *

This can either be: * @@ -130,47 +131,41 @@ public final class SpannedSubject extends Subject { * with {@code span.getStyle() == Typeface.ITALIC}. * * - * @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. */ - // TODO: swap this to fluent-style. - public void hasBoldItalicSpan(int startIndex, int endIndex, int flags) { + public WithSpanFlags hasBoldItalicSpanBetween(int start, int end) { if (actual == null) { failWithoutActual(simpleFact("Spanned must not be null")); - return; + return ALREADY_FAILED_WITH_FLAGS; } + List allFlags = new ArrayList<>(); List styles = new ArrayList<>(); - for (StyleSpan span : findMatchingSpans(startIndex, endIndex, flags, StyleSpan.class)) { + for (StyleSpan span : findMatchingSpans(start, end, StyleSpan.class)) { + allFlags.add(actual.getSpanFlags(span)); styles.add(span.getStyle()); } - if (styles.size() == 1 && styles.contains(Typeface.BOLD_ITALIC)) { - return; - } else if (styles.size() == 2 - && styles.contains(Typeface.BOLD) - && styles.contains(Typeface.ITALIC)) { - return; + if (styles.isEmpty()) { + failWithExpectedSpan(start, end, StyleSpan.class, actual.subSequence(start, end).toString()); + return ALREADY_FAILED_WITH_FLAGS; } - String spannedSubstring = actual.toString().substring(startIndex, endIndex); - String boldSpan = - getSpanAsStringWithFlags( - startIndex, endIndex, flags, new StyleSpan(Typeface.BOLD), spannedSubstring); - String italicSpan = - getSpanAsStringWithFlags( - startIndex, endIndex, flags, new StyleSpan(Typeface.ITALIC), spannedSubstring); - String boldItalicSpan = - getSpanAsStringWithFlags( - startIndex, endIndex, flags, new StyleSpan(Typeface.BOLD_ITALIC), spannedSubstring); - + if (styles.size() == 1 && styles.contains(Typeface.BOLD_ITALIC) + || styles.size() == 2 + && styles.contains(Typeface.BOLD) + && styles.contains(Typeface.ITALIC)) { + return check("StyleSpan (start=%s,end=%s)", start, end).about(spanFlags()).that(allFlags); + } failWithoutActual( - simpleFact("No matching span found"), + simpleFact( + String.format("No matching StyleSpans found between start=%s,end=%s", start, end)), fact("in text", actual.toString()), - fact("expected either", boldItalicSpan), - fact("or both", boldSpan + "\n" + italicSpan), - fact("but found", getAllSpansAsStringWithFlags(actual))); + fact("expected either styles", Arrays.asList(Typeface.BOLD_ITALIC)), + fact("or styles", Arrays.asList(Typeface.BOLD, Typeface.BOLD_ITALIC)), + fact("but found styles", styles)); + return ALREADY_FAILED_WITH_FLAGS; } /** @@ -194,8 +189,7 @@ public final class SpannedSubject extends Subject { 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)); + failWithExpectedSpan(start, end, UnderlineSpan.class, actual.toString().substring(start, end)); return ALREADY_FAILED_WITH_FLAGS; } @@ -218,7 +212,7 @@ public final class SpannedSubject extends Subject { List foregroundColorSpans = findMatchingSpans(start, end, ForegroundColorSpan.class); if (foregroundColorSpans.isEmpty()) { - failWithExpectedSpanWithoutFlags( + failWithExpectedSpan( start, end, ForegroundColorSpan.class, actual.toString().substring(start, end)); return ALREADY_FAILED_COLORED; } @@ -246,7 +240,7 @@ public final class SpannedSubject extends Subject { List backgroundColorSpans = findMatchingSpans(start, end, BackgroundColorSpan.class); if (backgroundColorSpans.isEmpty()) { - failWithExpectedSpanWithoutFlags( + failWithExpectedSpan( start, end, BackgroundColorSpan.class, actual.toString().substring(start, end)); return ALREADY_FAILED_COLORED; } @@ -255,19 +249,6 @@ public final class SpannedSubject extends Subject { .that(backgroundColorSpans); } - private List findMatchingSpans( - int startIndex, int endIndex, int flags, Class spanClazz) { - List spans = new ArrayList<>(); - for (T span : actual.getSpans(startIndex, endIndex, spanClazz)) { - if (actual.getSpanStart(span) == startIndex - && actual.getSpanEnd(span) == endIndex - && actual.getSpanFlags(span) == flags) { - spans.add(span); - } - } - return spans; - } - private List findMatchingSpans(int startIndex, int endIndex, Class spanClazz) { List spans = new ArrayList<>(); for (T span : actual.getSpans(startIndex, endIndex, spanClazz)) { @@ -278,72 +259,31 @@ public final class SpannedSubject extends Subject { 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", getSpanAsStringWithFlags(start, end, flags, span, spannedSubstring)), - fact("but found", getAllSpansAsStringWithFlags(actual))); - } - - private void failWithExpectedSpanWithoutFlags( + private void failWithExpectedSpan( 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))); + fact("expected", getSpanAsString(start, end, spanType, spannedSubstring)), + fact("but found", getAllSpansAsString(actual))); } - private static String getAllSpansAsStringWithFlags(Spanned spanned) { + private static String getAllSpansAsString(Spanned spanned) { List actualSpanStrings = new ArrayList<>(); for (Object span : spanned.getSpans(0, spanned.length(), Object.class)) { - actualSpanStrings.add(getSpanAsStringWithFlags(span, spanned)); + actualSpanStrings.add(getSpanAsString(span, spanned)); } return TextUtils.join("\n", actualSpanStrings); } - private static String getSpanAsStringWithFlags(Object span, Spanned spanned) { + private static String getSpanAsString(Object span, Spanned spanned) { int spanStart = spanned.getSpanStart(span); int spanEnd = spanned.getSpanEnd(span); - return getSpanAsStringWithFlags( - spanStart, - spanEnd, - spanned.getSpanFlags(span), - span, - spanned.toString().substring(spanStart, spanEnd)); - } - - private static String getSpanAsStringWithFlags( - int start, int end, int flags, Object span, String spannedSubstring) { - String suffix; - if (span instanceof StyleSpan) { - suffix = "\tstyle=" + ((StyleSpan) span).getStyle(); - } else { - suffix = ""; - } - return String.format( - "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 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( + return getSpanAsString( spanStart, spanEnd, span.getClass(), spanned.toString().substring(spanStart, spanEnd)); } - private static String getSpanAsStringWithoutFlags( + private static String getSpanAsString( int start, int end, Class span, String spannedSubstring) { return String.format( "start=%s\tend=%s\ttype=%s\tsubstring='%s'", diff --git a/testutils/src/test/java/com/google/android/exoplayer2/testutil/truth/SpannedSubjectTest.java b/testutils/src/test/java/com/google/android/exoplayer2/testutil/truth/SpannedSubjectTest.java index a01f6f66e0..c33a1128a0 100644 --- a/testutils/src/test/java/com/google/android/exoplayer2/testutil/truth/SpannedSubjectTest.java +++ b/testutils/src/test/java/com/google/android/exoplayer2/testutil/truth/SpannedSubjectTest.java @@ -63,7 +63,9 @@ public class SpannedSubjectTest { int end = start + "italic".length(); spannable.setSpan(new StyleSpan(Typeface.ITALIC), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - assertThat(spannable).hasItalicSpan(start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + assertThat(spannable) + .hasItalicSpanBetween(start, end) + .withFlags(Spanned.SPAN_INCLUSIVE_EXCLUSIVE); } @Test @@ -78,14 +80,21 @@ public class SpannedSubjectTest { whenTesting -> whenTesting .that(spannable) - .hasItalicSpan(start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE)); + .hasItalicSpanBetween(start, end) + .withFlags(Spanned.SPAN_INCLUSIVE_EXCLUSIVE)); - assertThat(failure).factKeys().contains("No matching span found"); - assertThat(failure).factValue("in text").isEqualTo(spannable.toString()); - assertThat(failure).factValue("expected").contains("flags=" + Spanned.SPAN_INCLUSIVE_EXCLUSIVE); assertThat(failure) - .factValue("but found") - .contains("flags=" + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + .factValue("value of") + .isEqualTo( + String.format( + "spanned.StyleSpan (start=%s,end=%s,style=%s).contains()", + start, end, Typeface.ITALIC)); + assertThat(failure) + .factValue("expected to contain") + .contains(String.valueOf(Spanned.SPAN_INCLUSIVE_EXCLUSIVE)); + assertThat(failure) + .factValue("but was") + .contains(String.valueOf(Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)); } @Test @@ -93,7 +102,10 @@ public class SpannedSubjectTest { AssertionError failure = expectFailure( whenTesting -> - whenTesting.that(null).hasItalicSpan(0, 5, Spanned.SPAN_INCLUSIVE_EXCLUSIVE)); + whenTesting + .that(null) + .hasItalicSpanBetween(0, 5) + .withFlags(Spanned.SPAN_INCLUSIVE_EXCLUSIVE)); assertThat(failure).factKeys().containsExactly("Spanned must not be null"); } @@ -105,7 +117,9 @@ public class SpannedSubjectTest { int end = start + "bold".length(); spannable.setSpan(new StyleSpan(Typeface.BOLD), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - assertThat(spannable).hasBoldSpan(start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + assertThat(spannable) + .hasBoldSpanBetween(start, end) + .withFlags(Spanned.SPAN_INCLUSIVE_EXCLUSIVE); } @Test @@ -116,7 +130,9 @@ public class SpannedSubjectTest { spannable.setSpan( new StyleSpan(Typeface.BOLD_ITALIC), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - assertThat(spannable).hasBoldItalicSpan(start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + assertThat(spannable) + .hasBoldItalicSpanBetween(start, end) + .withFlags(Spanned.SPAN_INCLUSIVE_EXCLUSIVE); } @Test @@ -127,7 +143,9 @@ public class SpannedSubjectTest { spannable.setSpan(new StyleSpan(Typeface.BOLD), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); spannable.setSpan(new StyleSpan(Typeface.ITALIC), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - assertThat(spannable).hasBoldItalicSpan(start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + assertThat(spannable) + .hasBoldItalicSpanBetween(start, end) + .withFlags(Spanned.SPAN_INCLUSIVE_EXCLUSIVE); } @Test @@ -144,8 +162,9 @@ public class SpannedSubjectTest { whenTesting -> whenTesting .that(spannable) - .hasBoldItalicSpan(incorrectStart, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE)); - assertThat(expected).factValue("expected either").contains("start=" + incorrectStart); + .hasBoldItalicSpanBetween(incorrectStart, end) + .withFlags(Spanned.SPAN_INCLUSIVE_EXCLUSIVE)); + assertThat(expected).factValue("expected").contains("start=" + incorrectStart); assertThat(expected).factValue("but found").contains("start=" + start); }