mirror of
https://github.com/samsonjs/media.git
synced 2026-03-29 10:05:48 +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.ColorInt;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.text.span.RubySpan;
|
||||
import com.google.common.truth.FailureMetadata;
|
||||
import com.google.common.truth.Subject;
|
||||
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.
|
||||
*
|
||||
|
|
@ -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.
|
||||
*
|
||||
|
|
@ -249,6 +250,30 @@ public final class SpannedSubject extends Subject {
|
|||
.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) {
|
||||
List<T> spans = new ArrayList<>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/** 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.UnderlineSpan;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import com.google.android.exoplayer2.text.span.RubySpan;
|
||||
import com.google.common.truth.ExpectFailure;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
|
@ -338,6 +339,120 @@ public class SpannedSubjectTest {
|
|||
.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(
|
||||
ExpectFailure.SimpleSubjectBuilderCallback<SpannedSubject, Spanned> callback) {
|
||||
return expectFailureAbout(spanned(), callback);
|
||||
|
|
|
|||
Loading…
Reference in a new issue