blob: c926723503670dbe89be7ba1efda75134fac3bb5 [file] [log] [blame]
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.text.cts;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import android.icu.util.ULocale;
import android.text.BidiFormatter;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextDirectionHeuristics;
import android.text.style.RelativeSizeSpan;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Locale;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class BidiFormatterTest {
private static final BidiFormatter LTR_FMT = BidiFormatter.getInstance(false /* LTR context */);
private static final BidiFormatter RTL_FMT = BidiFormatter.getInstance(true /* RTL context */);
private static final BidiFormatter LTR_FMT_EXIT_RESET =
new BidiFormatter.Builder(false /* LTR context */).stereoReset(false).build();
private static final BidiFormatter RTL_FMT_EXIT_RESET =
new BidiFormatter.Builder(true /* RTL context */).stereoReset(false).build();
private static final String EN = "abba";
private static final String HE = "\u05E0\u05E1";
private static final String LRM = "\u200E";
private static final String RLM = "\u200F";
private static final String LRE = "\u202A";
private static final String RLE = "\u202B";
private static final String PDF = "\u202C";
@Test
public void testIsRtlContext() {
assertEquals(false, LTR_FMT.isRtlContext());
assertEquals(true, RTL_FMT.isRtlContext());
assertEquals(false, BidiFormatter.getInstance(Locale.ENGLISH).isRtlContext());
assertEquals(true, BidiFormatter.getInstance(true).isRtlContext());
}
@Test
public void testCachedInstances() {
// Test that we get the same cached static instances for simple cases
BidiFormatter defaultFormatterInstance = BidiFormatter.getInstance();
assertTrue(defaultFormatterInstance == LTR_FMT || defaultFormatterInstance == RTL_FMT);
assertEquals(LTR_FMT, BidiFormatter.getInstance(false));
assertEquals(RTL_FMT, BidiFormatter.getInstance(true));
assertEquals(LTR_FMT, BidiFormatter.getInstance(false));
assertEquals(RTL_FMT, BidiFormatter.getInstance(Locale.forLanguageTag("ar")));
}
@Test
public void testBuilderIsRtlContext() {
assertEquals(false, new BidiFormatter.Builder(false).build().isRtlContext());
assertEquals(true, new BidiFormatter.Builder(true).build().isRtlContext());
}
@Test
public void testIsRtl() {
assertEquals(true, BidiFormatter.getInstance(true).isRtl(HE));
assertEquals(true, BidiFormatter.getInstance(false).isRtl(HE));
assertEquals(false, BidiFormatter.getInstance(true).isRtl(EN));
assertEquals(false, BidiFormatter.getInstance(false).isRtl(EN));
}
@Test
public void testUnicodeWrap() {
// Make sure an input of null doesn't crash anything.
assertNull(LTR_FMT.unicodeWrap(null));
// Uniform directionality in opposite context.
assertEquals("uniform dir opposite to LTR context",
RLE + "." + HE + "." + PDF + LRM,
LTR_FMT_EXIT_RESET.unicodeWrap("." + HE + "."));
assertEquals("uniform dir opposite to LTR context, stereo reset",
LRM + RLE + "." + HE + "." + PDF + LRM,
LTR_FMT.unicodeWrap("." + HE + "."));
assertEquals("uniform dir opposite to LTR context, stereo reset, no isolation",
RLE + "." + HE + "." + PDF,
LTR_FMT.unicodeWrap("." + HE + ".", false));
assertEquals("neutral treated as opposite to LTR context",
RLE + "." + PDF + LRM,
LTR_FMT_EXIT_RESET.unicodeWrap(".", TextDirectionHeuristics.RTL));
assertEquals("uniform dir opposite to RTL context",
LRE + "." + EN + "." + PDF + RLM,
RTL_FMT_EXIT_RESET.unicodeWrap("." + EN + "."));
assertEquals("uniform dir opposite to RTL context, stereo reset",
RLM + LRE + "." + EN + "." + PDF + RLM,
RTL_FMT.unicodeWrap("." + EN + "."));
assertEquals("uniform dir opposite to RTL context, stereo reset, no isolation",
LRE + "." + EN + "." + PDF,
RTL_FMT.unicodeWrap("." + EN + ".", false));
assertEquals("neutral treated as opposite to RTL context",
LRE + "." + PDF + RLM,
RTL_FMT_EXIT_RESET.unicodeWrap(".", TextDirectionHeuristics.LTR));
// We test mixed-directionality cases only with an explicit overall directionality parameter
// because the estimation logic is outside the sphere of BidiFormatter, and different
// estimators will treat them differently.
// Overall directionality matching context, but with opposite exit directionality.
assertEquals("exit dir opposite to LTR context",
EN + HE + LRM,
LTR_FMT_EXIT_RESET.unicodeWrap(EN + HE, TextDirectionHeuristics.LTR));
assertEquals("exit dir opposite to LTR context, stereo reset",
EN + HE + LRM,
LTR_FMT.unicodeWrap(EN + HE, TextDirectionHeuristics.LTR));
assertEquals("exit dir opposite to LTR context, stereo reset, no isolation",
EN + HE,
LTR_FMT.unicodeWrap(EN + HE, TextDirectionHeuristics.LTR, false));
assertEquals("exit dir opposite to RTL context",
HE + EN + RLM,
RTL_FMT_EXIT_RESET.unicodeWrap(HE + EN, TextDirectionHeuristics.RTL));
assertEquals("exit dir opposite to RTL context, stereo reset",
HE + EN + RLM,
RTL_FMT.unicodeWrap(HE + EN, TextDirectionHeuristics.RTL));
assertEquals("exit dir opposite to RTL context, stereo reset, no isolation",
HE + EN,
RTL_FMT.unicodeWrap(HE + EN, TextDirectionHeuristics.RTL, false));
// Overall directionality matching context, but with opposite entry directionality.
assertEquals("entry dir opposite to LTR context",
HE + EN,
LTR_FMT_EXIT_RESET.unicodeWrap(HE + EN, TextDirectionHeuristics.LTR));
assertEquals("entry dir opposite to LTR context, stereo reset",
LRM + HE + EN,
LTR_FMT.unicodeWrap(HE + EN, TextDirectionHeuristics.LTR));
assertEquals("entry dir opposite to LTR context, stereo reset, no isolation",
HE + EN,
LTR_FMT.unicodeWrap(HE + EN, TextDirectionHeuristics.LTR, false));
assertEquals("entry dir opposite to RTL context",
EN + HE,
RTL_FMT_EXIT_RESET.unicodeWrap(EN + HE, TextDirectionHeuristics.RTL));
assertEquals("entry dir opposite to RTL context, stereo reset",
RLM + EN + HE,
RTL_FMT.unicodeWrap(EN + HE, TextDirectionHeuristics.RTL));
assertEquals("entry dir opposite to RTL context, stereo reset, no isolation",
EN + HE,
RTL_FMT.unicodeWrap(EN + HE, TextDirectionHeuristics.RTL, false));
// Overall directionality matching context, but with opposite entry and exit directionality.
assertEquals("entry and exit dir opposite to LTR context",
HE + EN + HE + LRM,
LTR_FMT_EXIT_RESET.unicodeWrap(HE + EN + HE, TextDirectionHeuristics.LTR));
assertEquals("entry and exit dir opposite to LTR context, stereo reset",
LRM + HE + EN + HE + LRM,
LTR_FMT.unicodeWrap(HE + EN + HE, TextDirectionHeuristics.LTR));
assertEquals("entry and exit dir opposite to LTR context, no isolation",
HE + EN + HE,
LTR_FMT_EXIT_RESET.unicodeWrap(HE + EN + HE, TextDirectionHeuristics.LTR, false));
assertEquals("entry and exit dir opposite to RTL context",
EN + HE + EN + RLM,
RTL_FMT_EXIT_RESET.unicodeWrap(EN + HE + EN, TextDirectionHeuristics.RTL));
assertEquals("entry and exit dir opposite to RTL context, no isolation",
EN + HE + EN,
RTL_FMT_EXIT_RESET.unicodeWrap(EN + HE + EN, TextDirectionHeuristics.RTL, false));
// Entry and exit directionality matching context, but with opposite overall directionality.
assertEquals("overall dir (but not entry or exit dir) opposite to LTR context",
RLE + EN + HE + EN + PDF + LRM,
LTR_FMT_EXIT_RESET.unicodeWrap(EN + HE + EN, TextDirectionHeuristics.RTL));
assertEquals("overall dir (but not entry or exit dir) opposite to LTR context, stereo reset",
LRM + RLE + EN + HE + EN + PDF + LRM,
LTR_FMT.unicodeWrap(EN + HE + EN, TextDirectionHeuristics.RTL));
assertEquals("overall dir (but not entry or exit dir) opposite to LTR context, no isolation",
RLE + EN + HE + EN + PDF,
LTR_FMT_EXIT_RESET.unicodeWrap(EN + HE + EN, TextDirectionHeuristics.RTL, false));
assertEquals("overall dir (but not entry or exit dir) opposite to RTL context",
LRE + HE + EN + HE + PDF + RLM,
RTL_FMT_EXIT_RESET.unicodeWrap(HE + EN + HE, TextDirectionHeuristics.LTR));
assertEquals("overall dir (but not entry or exit dir) opposite to RTL context, stereo reset",
RLM + LRE + HE + EN + HE + PDF + RLM,
RTL_FMT.unicodeWrap(HE + EN + HE, TextDirectionHeuristics.LTR));
assertEquals("overall dir (but not entry or exit dir) opposite to RTL context, no isolation",
LRE + HE + EN + HE + PDF,
RTL_FMT_EXIT_RESET.unicodeWrap(HE + EN + HE, TextDirectionHeuristics.LTR, false));
}
@Test
public void testGetStereoReset() {
assertTrue(LTR_FMT.getStereoReset());
assertTrue(RTL_FMT.getStereoReset());
assertFalse(LTR_FMT_EXIT_RESET.getStereoReset());
assertFalse(RTL_FMT_EXIT_RESET.getStereoReset());
}
@Test
public void testBuilder_construction() {
final BidiFormatter defaultFmt = new BidiFormatter.Builder().build();
// Test that the default locale and the BidiFormatter's locale have the same direction.
assertEquals(ULocale.getDefault().isRightToLeft(), defaultFmt.isRtlContext());
final BidiFormatter ltrFmt = new BidiFormatter.Builder(false).build();
assertFalse(ltrFmt.isRtlContext());
final BidiFormatter rtlFmt = new BidiFormatter.Builder(true).build();
assertTrue(rtlFmt.isRtlContext());
final BidiFormatter englishFmt = new BidiFormatter.Builder(Locale.ENGLISH).build();
assertFalse(englishFmt.isRtlContext());
final BidiFormatter arabicFmt =
new BidiFormatter.Builder(Locale.forLanguageTag("ar")).build();
assertTrue(arabicFmt.isRtlContext());
}
@Test
public void testBuilder_setTextDirectionHeuristic() {
final BidiFormatter defaultFmt = new BidiFormatter.Builder().build();
assertFalse(defaultFmt.isRtl(EN + HE + EN));
final BidiFormatter modifiedFmt = new BidiFormatter.Builder().setTextDirectionHeuristic(
TextDirectionHeuristics.ANYRTL_LTR).build();
assertTrue(modifiedFmt.isRtl(EN + HE + EN));
}
@Test
public void testCharSequenceApis() {
final CharSequence CS_HE = new SpannableString(HE);
assertEquals(true, BidiFormatter.getInstance(true).isRtl(CS_HE));
final SpannableString CS_EN_HE = new SpannableString(EN + HE);
final Object RELATIVE_SIZE_SPAN = new RelativeSizeSpan(1.2f);
CS_EN_HE.setSpan(RELATIVE_SIZE_SPAN, 0, EN.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
Spanned wrapped;
Object[] spans;
wrapped = (Spanned) LTR_FMT.unicodeWrap(CS_EN_HE);
assertEquals(EN + HE + LRM, wrapped.toString());
spans = wrapped.getSpans(0, wrapped.length(), Object.class);
assertEquals(1, spans.length);
assertEquals(RELATIVE_SIZE_SPAN, spans[0]);
assertEquals(0, wrapped.getSpanStart(RELATIVE_SIZE_SPAN));
assertEquals(EN.length(), wrapped.getSpanEnd(RELATIVE_SIZE_SPAN));
wrapped = (Spanned) LTR_FMT.unicodeWrap(CS_EN_HE, TextDirectionHeuristics.LTR);
assertEquals(EN + HE + LRM, wrapped.toString());
spans = wrapped.getSpans(0, wrapped.length(), Object.class);
assertEquals(1, spans.length);
assertEquals(RELATIVE_SIZE_SPAN, spans[0]);
assertEquals(0, wrapped.getSpanStart(RELATIVE_SIZE_SPAN));
assertEquals(EN.length(), wrapped.getSpanEnd(RELATIVE_SIZE_SPAN));
wrapped = (Spanned) LTR_FMT.unicodeWrap(CS_EN_HE, false);
assertEquals(EN + HE, wrapped.toString());
spans = wrapped.getSpans(0, wrapped.length(), Object.class);
assertEquals(1, spans.length);
assertEquals(RELATIVE_SIZE_SPAN, spans[0]);
assertEquals(0, wrapped.getSpanStart(RELATIVE_SIZE_SPAN));
assertEquals(EN.length(), wrapped.getSpanEnd(RELATIVE_SIZE_SPAN));
wrapped = (Spanned) LTR_FMT.unicodeWrap(CS_EN_HE, TextDirectionHeuristics.LTR, false);
assertEquals(EN + HE, wrapped.toString());
spans = wrapped.getSpans(0, wrapped.length(), Object.class);
assertEquals(1, spans.length);
assertEquals(RELATIVE_SIZE_SPAN, spans[0]);
assertEquals(0, wrapped.getSpanStart(RELATIVE_SIZE_SPAN));
assertEquals(EN.length(), wrapped.getSpanEnd(RELATIVE_SIZE_SPAN));
}
}