mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Add RubySpan support to SpannedSubject
PiperOrigin-RevId: 288280332
This commit is contained in:
parent
e55af3e3c8
commit
2b1a066339
2 changed files with 230 additions and 2 deletions
|
|
@ -30,6 +30,7 @@ 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.RubySpan;
|
||||||
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;
|
||||||
|
|
@ -194,7 +195,7 @@ public final class SpannedSubject extends Subject {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks that the subject as 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.
|
||||||
*
|
*
|
||||||
|
|
@ -222,7 +223,7 @@ public final class SpannedSubject extends Subject {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks that the subject as a {@link ForegroundColorSpan} 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.
|
||||||
*
|
*
|
||||||
|
|
@ -249,6 +250,30 @@ public final class SpannedSubject extends Subject {
|
||||||
.that(backgroundColorSpans);
|
.that(backgroundColorSpans);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @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 RubyText hasRubySpanBetween(int start, int end) {
|
||||||
|
if (actual == null) {
|
||||||
|
failWithoutActual(simpleFact("Spanned must not be null"));
|
||||||
|
return ALREADY_FAILED_WITH_TEXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<RubySpan> rubySpans = findMatchingSpans(start, end, RubySpan.class);
|
||||||
|
if (rubySpans.isEmpty()) {
|
||||||
|
failWithExpectedSpan(start, end, RubySpan.class, actual.toString().substring(start, end));
|
||||||
|
return ALREADY_FAILED_WITH_TEXT;
|
||||||
|
}
|
||||||
|
return check("RubySpan (start=%s,end=%s)", start, end).about(rubySpans(actual)).that(rubySpans);
|
||||||
|
}
|
||||||
|
|
||||||
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)) {
|
||||||
|
|
@ -440,4 +465,92 @@ public final class SpannedSubject extends Subject {
|
||||||
return check("flags").about(spanFlags()).that(matchingSpanFlags);
|
return check("flags").about(spanFlags()).that(matchingSpanFlags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Allows assertions about a span's ruby text and its position. */
|
||||||
|
public interface RubyText {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that at least one of the matched spans has the expected {@code text}.
|
||||||
|
*
|
||||||
|
* @param text The expected text.
|
||||||
|
* @param position The expected position of the text.
|
||||||
|
* @return A {@link WithSpanFlags} object for optional additional assertions on the flags.
|
||||||
|
*/
|
||||||
|
AndSpanFlags withTextAndPosition(String text, @RubySpan.Position int position);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final RubyText ALREADY_FAILED_WITH_TEXT =
|
||||||
|
(text, position) -> ALREADY_FAILED_AND_FLAGS;
|
||||||
|
|
||||||
|
private Factory<RubySpansSubject, List<RubySpan>> rubySpans(Spanned actualSpanned) {
|
||||||
|
return (FailureMetadata metadata, List<RubySpan> spans) ->
|
||||||
|
new RubySpansSubject(metadata, spans, actualSpanned);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class RubySpansSubject extends Subject implements RubyText {
|
||||||
|
|
||||||
|
private final List<RubySpan> actualSpans;
|
||||||
|
private final Spanned actualSpanned;
|
||||||
|
|
||||||
|
private RubySpansSubject(
|
||||||
|
FailureMetadata metadata, List<RubySpan> actualSpans, Spanned actualSpanned) {
|
||||||
|
super(metadata, actualSpans);
|
||||||
|
this.actualSpans = actualSpans;
|
||||||
|
this.actualSpanned = actualSpanned;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AndSpanFlags withTextAndPosition(String text, @RubySpan.Position int position) {
|
||||||
|
List<Integer> matchingSpanFlags = new ArrayList<>();
|
||||||
|
List<TextAndPosition> spanTextsAndPositions = new ArrayList<>();
|
||||||
|
for (RubySpan span : actualSpans) {
|
||||||
|
spanTextsAndPositions.add(new TextAndPosition(span.rubyText, span.position));
|
||||||
|
if (span.rubyText.equals(text)) {
|
||||||
|
matchingSpanFlags.add(actualSpanned.getSpanFlags(span));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
check("rubyTextAndPosition")
|
||||||
|
.that(spanTextsAndPositions)
|
||||||
|
.containsExactly(new TextAndPosition(text, position));
|
||||||
|
return check("flags").about(spanFlags()).that(matchingSpanFlags);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class TextAndPosition {
|
||||||
|
private final String text;
|
||||||
|
@RubySpan.Position private final int position;
|
||||||
|
|
||||||
|
private TextAndPosition(String text, int position) {
|
||||||
|
this.text = text;
|
||||||
|
this.position = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
TextAndPosition that = (TextAndPosition) o;
|
||||||
|
if (position != that.position) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return text.equals(that.text);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = text.hashCode();
|
||||||
|
result = 31 * result + position;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format("{text='%s',position=%s}", text, position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ import android.text.style.ForegroundColorSpan;
|
||||||
import android.text.style.StyleSpan;
|
import android.text.style.StyleSpan;
|
||||||
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.RubySpan;
|
||||||
import com.google.common.truth.ExpectFailure;
|
import com.google.common.truth.ExpectFailure;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
@ -338,6 +339,120 @@ public class SpannedSubjectTest {
|
||||||
.contains(String.valueOf(Spanned.SPAN_INCLUSIVE_EXCLUSIVE));
|
.contains(String.valueOf(Spanned.SPAN_INCLUSIVE_EXCLUSIVE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void rubySpan_success() {
|
||||||
|
SpannableString spannable = SpannableString.valueOf("test with rubied section");
|
||||||
|
int start = "test with ".length();
|
||||||
|
int end = start + "rubied".length();
|
||||||
|
spannable.setSpan(
|
||||||
|
new RubySpan("ruby text", RubySpan.POSITION_OVER),
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||||
|
|
||||||
|
assertThat(spannable)
|
||||||
|
.hasRubySpanBetween(start, end)
|
||||||
|
.withTextAndPosition("ruby text", RubySpan.POSITION_OVER)
|
||||||
|
.andFlags(Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void rubySpan_wrongEndIndex() {
|
||||||
|
SpannableString spannable = SpannableString.valueOf("test with cyan section");
|
||||||
|
int start = "test with ".length();
|
||||||
|
int end = start + "cyan".length();
|
||||||
|
spannable.setSpan(
|
||||||
|
new RubySpan("ruby text", RubySpan.POSITION_OVER),
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||||
|
|
||||||
|
int incorrectEnd = end + 2;
|
||||||
|
AssertionError expected =
|
||||||
|
expectFailure(
|
||||||
|
whenTesting ->
|
||||||
|
whenTesting
|
||||||
|
.that(spannable)
|
||||||
|
.hasRubySpanBetween(start, incorrectEnd)
|
||||||
|
.withTextAndPosition("ruby text", RubySpan.POSITION_OVER));
|
||||||
|
assertThat(expected).factValue("expected").contains("end=" + incorrectEnd);
|
||||||
|
assertThat(expected).factValue("but found").contains("end=" + end);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void rubySpan_wrongText() {
|
||||||
|
SpannableString spannable = SpannableString.valueOf("test with rubied section");
|
||||||
|
int start = "test with ".length();
|
||||||
|
int end = start + "rubied".length();
|
||||||
|
spannable.setSpan(
|
||||||
|
new RubySpan("ruby text", RubySpan.POSITION_OVER),
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||||
|
|
||||||
|
AssertionError expected =
|
||||||
|
expectFailure(
|
||||||
|
whenTesting ->
|
||||||
|
whenTesting
|
||||||
|
.that(spannable)
|
||||||
|
.hasRubySpanBetween(start, end)
|
||||||
|
.withTextAndPosition("incorrect text", RubySpan.POSITION_OVER));
|
||||||
|
assertThat(expected).factValue("value of").contains("rubyTextAndPosition");
|
||||||
|
assertThat(expected).factValue("expected").contains("text='incorrect text'");
|
||||||
|
assertThat(expected).factValue("but was").contains("text='ruby text'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void rubySpan_wrongPosition() {
|
||||||
|
SpannableString spannable = SpannableString.valueOf("test with rubied section");
|
||||||
|
int start = "test with ".length();
|
||||||
|
int end = start + "rubied".length();
|
||||||
|
spannable.setSpan(
|
||||||
|
new RubySpan("ruby text", RubySpan.POSITION_OVER),
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||||
|
|
||||||
|
AssertionError expected =
|
||||||
|
expectFailure(
|
||||||
|
whenTesting ->
|
||||||
|
whenTesting
|
||||||
|
.that(spannable)
|
||||||
|
.hasRubySpanBetween(start, end)
|
||||||
|
.withTextAndPosition("ruby text", RubySpan.POSITION_UNDER));
|
||||||
|
assertThat(expected).factValue("value of").contains("rubyTextAndPosition");
|
||||||
|
assertThat(expected).factValue("expected").contains("position=" + RubySpan.POSITION_UNDER);
|
||||||
|
assertThat(expected).factValue("but was").contains("position=" + RubySpan.POSITION_OVER);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void rubySpan_wrongFlags() {
|
||||||
|
SpannableString spannable = SpannableString.valueOf("test with rubied section");
|
||||||
|
int start = "test with ".length();
|
||||||
|
int end = start + "rubied".length();
|
||||||
|
spannable.setSpan(
|
||||||
|
new RubySpan("ruby text", RubySpan.POSITION_OVER),
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||||
|
|
||||||
|
AssertionError expected =
|
||||||
|
expectFailure(
|
||||||
|
whenTesting ->
|
||||||
|
whenTesting
|
||||||
|
.that(spannable)
|
||||||
|
.hasRubySpanBetween(start, end)
|
||||||
|
.withTextAndPosition("ruby text", RubySpan.POSITION_OVER)
|
||||||
|
.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(
|
private static AssertionError expectFailure(
|
||||||
ExpectFailure.SimpleSubjectBuilderCallback<SpannedSubject, Spanned> callback) {
|
ExpectFailure.SimpleSubjectBuilderCallback<SpannedSubject, Spanned> callback) {
|
||||||
return expectFailureAbout(spanned(), callback);
|
return expectFailureAbout(spanned(), callback);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue