1 /* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.text.cts; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertFalse; 21 import static org.junit.Assert.assertNull; 22 import static org.junit.Assert.assertTrue; 23 24 import android.icu.util.ULocale; 25 import android.support.test.filters.SmallTest; 26 import android.support.test.runner.AndroidJUnit4; 27 import android.text.BidiFormatter; 28 import android.text.SpannableString; 29 import android.text.Spanned; 30 import android.text.TextDirectionHeuristics; 31 import android.text.style.RelativeSizeSpan; 32 33 import org.junit.Test; 34 import org.junit.runner.RunWith; 35 36 import java.util.Locale; 37 38 @SmallTest 39 @RunWith(AndroidJUnit4.class) 40 public class BidiFormatterTest { 41 42 private static final BidiFormatter LTR_FMT = BidiFormatter.getInstance(false /* LTR context */); 43 private static final BidiFormatter RTL_FMT = BidiFormatter.getInstance(true /* RTL context */); 44 45 private static final BidiFormatter LTR_FMT_EXIT_RESET = 46 new BidiFormatter.Builder(false /* LTR context */).stereoReset(false).build(); 47 private static final BidiFormatter RTL_FMT_EXIT_RESET = 48 new BidiFormatter.Builder(true /* RTL context */).stereoReset(false).build(); 49 50 private static final String EN = "abba"; 51 private static final String HE = "\u05E0\u05E1"; 52 53 private static final String LRM = "\u200E"; 54 private static final String RLM = "\u200F"; 55 private static final String LRE = "\u202A"; 56 private static final String RLE = "\u202B"; 57 private static final String PDF = "\u202C"; 58 59 @Test 60 public void testIsRtlContext() { 61 assertEquals(false, LTR_FMT.isRtlContext()); 62 assertEquals(true, RTL_FMT.isRtlContext()); 63 64 assertEquals(false, BidiFormatter.getInstance(Locale.ENGLISH).isRtlContext()); 65 assertEquals(true, BidiFormatter.getInstance(true).isRtlContext()); 66 } 67 68 @Test 69 public void testCachedInstances() { 70 // Test that we get the same cached static instances for simple cases 71 BidiFormatter defaultFormatterInstance = BidiFormatter.getInstance(); 72 assertTrue(defaultFormatterInstance == LTR_FMT || defaultFormatterInstance == RTL_FMT); 73 74 assertEquals(LTR_FMT, BidiFormatter.getInstance(false)); 75 assertEquals(RTL_FMT, BidiFormatter.getInstance(true)); 76 77 assertEquals(LTR_FMT, BidiFormatter.getInstance(false)); 78 assertEquals(RTL_FMT, BidiFormatter.getInstance(Locale.forLanguageTag("ar"))); 79 } 80 81 @Test 82 public void testBuilderIsRtlContext() { 83 assertEquals(false, new BidiFormatter.Builder(false).build().isRtlContext()); 84 assertEquals(true, new BidiFormatter.Builder(true).build().isRtlContext()); 85 } 86 87 @Test 88 public void testIsRtl() { 89 assertEquals(true, BidiFormatter.getInstance(true).isRtl(HE)); 90 assertEquals(true, BidiFormatter.getInstance(false).isRtl(HE)); 91 92 assertEquals(false, BidiFormatter.getInstance(true).isRtl(EN)); 93 assertEquals(false, BidiFormatter.getInstance(false).isRtl(EN)); 94 } 95 96 @Test 97 public void testUnicodeWrap() { 98 // Make sure an input of null doesn't crash anything. 99 assertNull(LTR_FMT.unicodeWrap(null)); 100 101 // Uniform directionality in opposite context. 102 assertEquals("uniform dir opposite to LTR context", 103 RLE + "." + HE + "." + PDF + LRM, 104 LTR_FMT_EXIT_RESET.unicodeWrap("." + HE + ".")); 105 assertEquals("uniform dir opposite to LTR context, stereo reset", 106 LRM + RLE + "." + HE + "." + PDF + LRM, 107 LTR_FMT.unicodeWrap("." + HE + ".")); 108 assertEquals("uniform dir opposite to LTR context, stereo reset, no isolation", 109 RLE + "." + HE + "." + PDF, 110 LTR_FMT.unicodeWrap("." + HE + ".", false)); 111 assertEquals("neutral treated as opposite to LTR context", 112 RLE + "." + PDF + LRM, 113 LTR_FMT_EXIT_RESET.unicodeWrap(".", TextDirectionHeuristics.RTL)); 114 assertEquals("uniform dir opposite to RTL context", 115 LRE + "." + EN + "." + PDF + RLM, 116 RTL_FMT_EXIT_RESET.unicodeWrap("." + EN + ".")); 117 assertEquals("uniform dir opposite to RTL context, stereo reset", 118 RLM + LRE + "." + EN + "." + PDF + RLM, 119 RTL_FMT.unicodeWrap("." + EN + ".")); 120 assertEquals("uniform dir opposite to RTL context, stereo reset, no isolation", 121 LRE + "." + EN + "." + PDF, 122 RTL_FMT.unicodeWrap("." + EN + ".", false)); 123 assertEquals("neutral treated as opposite to RTL context", 124 LRE + "." + PDF + RLM, 125 RTL_FMT_EXIT_RESET.unicodeWrap(".", TextDirectionHeuristics.LTR)); 126 127 // We test mixed-directionality cases only with an explicit overall directionality parameter 128 // because the estimation logic is outside the sphere of BidiFormatter, and different 129 // estimators will treat them differently. 130 131 // Overall directionality matching context, but with opposite exit directionality. 132 assertEquals("exit dir opposite to LTR context", 133 EN + HE + LRM, 134 LTR_FMT_EXIT_RESET.unicodeWrap(EN + HE, TextDirectionHeuristics.LTR)); 135 assertEquals("exit dir opposite to LTR context, stereo reset", 136 EN + HE + LRM, 137 LTR_FMT.unicodeWrap(EN + HE, TextDirectionHeuristics.LTR)); 138 assertEquals("exit dir opposite to LTR context, stereo reset, no isolation", 139 EN + HE, 140 LTR_FMT.unicodeWrap(EN + HE, TextDirectionHeuristics.LTR, false)); 141 142 assertEquals("exit dir opposite to RTL context", 143 HE + EN + RLM, 144 RTL_FMT_EXIT_RESET.unicodeWrap(HE + EN, TextDirectionHeuristics.RTL)); 145 assertEquals("exit dir opposite to RTL context, stereo reset", 146 HE + EN + RLM, 147 RTL_FMT.unicodeWrap(HE + EN, TextDirectionHeuristics.RTL)); 148 assertEquals("exit dir opposite to RTL context, stereo reset, no isolation", 149 HE + EN, 150 RTL_FMT.unicodeWrap(HE + EN, TextDirectionHeuristics.RTL, false)); 151 152 // Overall directionality matching context, but with opposite entry directionality. 153 assertEquals("entry dir opposite to LTR context", 154 HE + EN, 155 LTR_FMT_EXIT_RESET.unicodeWrap(HE + EN, TextDirectionHeuristics.LTR)); 156 assertEquals("entry dir opposite to LTR context, stereo reset", 157 LRM + HE + EN, 158 LTR_FMT.unicodeWrap(HE + EN, TextDirectionHeuristics.LTR)); 159 assertEquals("entry dir opposite to LTR context, stereo reset, no isolation", 160 HE + EN, 161 LTR_FMT.unicodeWrap(HE + EN, TextDirectionHeuristics.LTR, false)); 162 163 assertEquals("entry dir opposite to RTL context", 164 EN + HE, 165 RTL_FMT_EXIT_RESET.unicodeWrap(EN + HE, TextDirectionHeuristics.RTL)); 166 assertEquals("entry dir opposite to RTL context, stereo reset", 167 RLM + EN + HE, 168 RTL_FMT.unicodeWrap(EN + HE, TextDirectionHeuristics.RTL)); 169 assertEquals("entry dir opposite to RTL context, stereo reset, no isolation", 170 EN + HE, 171 RTL_FMT.unicodeWrap(EN + HE, TextDirectionHeuristics.RTL, false)); 172 173 // Overall directionality matching context, but with opposite entry and exit directionality. 174 assertEquals("entry and exit dir opposite to LTR context", 175 HE + EN + HE + LRM, 176 LTR_FMT_EXIT_RESET.unicodeWrap(HE + EN + HE, TextDirectionHeuristics.LTR)); 177 assertEquals("entry and exit dir opposite to LTR context, stereo reset", 178 LRM + HE + EN + HE + LRM, 179 LTR_FMT.unicodeWrap(HE + EN + HE, TextDirectionHeuristics.LTR)); 180 assertEquals("entry and exit dir opposite to LTR context, no isolation", 181 HE + EN + HE, 182 LTR_FMT_EXIT_RESET.unicodeWrap(HE + EN + HE, TextDirectionHeuristics.LTR, false)); 183 184 assertEquals("entry and exit dir opposite to RTL context", 185 EN + HE + EN + RLM, 186 RTL_FMT_EXIT_RESET.unicodeWrap(EN + HE + EN, TextDirectionHeuristics.RTL)); 187 assertEquals("entry and exit dir opposite to RTL context, no isolation", 188 EN + HE + EN, 189 RTL_FMT_EXIT_RESET.unicodeWrap(EN + HE + EN, TextDirectionHeuristics.RTL, false)); 190 191 // Entry and exit directionality matching context, but with opposite overall directionality. 192 assertEquals("overall dir (but not entry or exit dir) opposite to LTR context", 193 RLE + EN + HE + EN + PDF + LRM, 194 LTR_FMT_EXIT_RESET.unicodeWrap(EN + HE + EN, TextDirectionHeuristics.RTL)); 195 assertEquals("overall dir (but not entry or exit dir) opposite to LTR context, stereo reset", 196 LRM + RLE + EN + HE + EN + PDF + LRM, 197 LTR_FMT.unicodeWrap(EN + HE + EN, TextDirectionHeuristics.RTL)); 198 assertEquals("overall dir (but not entry or exit dir) opposite to LTR context, no isolation", 199 RLE + EN + HE + EN + PDF, 200 LTR_FMT_EXIT_RESET.unicodeWrap(EN + HE + EN, TextDirectionHeuristics.RTL, false)); 201 202 assertEquals("overall dir (but not entry or exit dir) opposite to RTL context", 203 LRE + HE + EN + HE + PDF + RLM, 204 RTL_FMT_EXIT_RESET.unicodeWrap(HE + EN + HE, TextDirectionHeuristics.LTR)); 205 assertEquals("overall dir (but not entry or exit dir) opposite to RTL context, stereo reset", 206 RLM + LRE + HE + EN + HE + PDF + RLM, 207 RTL_FMT.unicodeWrap(HE + EN + HE, TextDirectionHeuristics.LTR)); 208 assertEquals("overall dir (but not entry or exit dir) opposite to RTL context, no isolation", 209 LRE + HE + EN + HE + PDF, 210 RTL_FMT_EXIT_RESET.unicodeWrap(HE + EN + HE, TextDirectionHeuristics.LTR, false)); 211 } 212 213 @Test 214 public void testGetStereoReset() { 215 assertTrue(LTR_FMT.getStereoReset()); 216 assertTrue(RTL_FMT.getStereoReset()); 217 assertFalse(LTR_FMT_EXIT_RESET.getStereoReset()); 218 assertFalse(RTL_FMT_EXIT_RESET.getStereoReset()); 219 } 220 221 @Test 222 public void testBuilder_construction() { 223 final BidiFormatter defaultFmt = new BidiFormatter.Builder().build(); 224 // Test that the default locale and the BidiFormatter's locale have the same direction. 225 assertEquals(ULocale.getDefault().isRightToLeft(), defaultFmt.isRtlContext()); 226 227 final BidiFormatter ltrFmt = new BidiFormatter.Builder(false).build(); 228 assertFalse(ltrFmt.isRtlContext()); 229 230 final BidiFormatter rtlFmt = new BidiFormatter.Builder(true).build(); 231 assertTrue(rtlFmt.isRtlContext()); 232 233 final BidiFormatter englishFmt = new BidiFormatter.Builder(Locale.ENGLISH).build(); 234 assertFalse(englishFmt.isRtlContext()); 235 236 final BidiFormatter arabicFmt = 237 new BidiFormatter.Builder(Locale.forLanguageTag("ar")).build(); 238 assertTrue(arabicFmt.isRtlContext()); 239 } 240 241 @Test 242 public void testBuilder_setTextDirectionHeuristic() { 243 final BidiFormatter defaultFmt = new BidiFormatter.Builder().build(); 244 assertFalse(defaultFmt.isRtl(EN + HE + EN)); 245 246 final BidiFormatter modifiedFmt = new BidiFormatter.Builder().setTextDirectionHeuristic( 247 TextDirectionHeuristics.ANYRTL_LTR).build(); 248 assertTrue(modifiedFmt.isRtl(EN + HE + EN)); 249 } 250 251 @Test 252 public void testCharSequenceApis() { 253 final CharSequence CS_HE = new SpannableString(HE); 254 assertEquals(true, BidiFormatter.getInstance(true).isRtl(CS_HE)); 255 256 final SpannableString CS_EN_HE = new SpannableString(EN + HE); 257 final Object RELATIVE_SIZE_SPAN = new RelativeSizeSpan(1.2f); 258 CS_EN_HE.setSpan(RELATIVE_SIZE_SPAN, 0, EN.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); 259 260 Spanned wrapped; 261 Object[] spans; 262 263 wrapped = (Spanned) LTR_FMT.unicodeWrap(CS_EN_HE); 264 assertEquals(EN + HE + LRM, wrapped.toString()); 265 spans = wrapped.getSpans(0, wrapped.length(), Object.class); 266 assertEquals(1, spans.length); 267 assertEquals(RELATIVE_SIZE_SPAN, spans[0]); 268 assertEquals(0, wrapped.getSpanStart(RELATIVE_SIZE_SPAN)); 269 assertEquals(EN.length(), wrapped.getSpanEnd(RELATIVE_SIZE_SPAN)); 270 271 wrapped = (Spanned) LTR_FMT.unicodeWrap(CS_EN_HE, TextDirectionHeuristics.LTR); 272 assertEquals(EN + HE + LRM, wrapped.toString()); 273 spans = wrapped.getSpans(0, wrapped.length(), Object.class); 274 assertEquals(1, spans.length); 275 assertEquals(RELATIVE_SIZE_SPAN, spans[0]); 276 assertEquals(0, wrapped.getSpanStart(RELATIVE_SIZE_SPAN)); 277 assertEquals(EN.length(), wrapped.getSpanEnd(RELATIVE_SIZE_SPAN)); 278 279 wrapped = (Spanned) LTR_FMT.unicodeWrap(CS_EN_HE, false); 280 assertEquals(EN + HE, wrapped.toString()); 281 spans = wrapped.getSpans(0, wrapped.length(), Object.class); 282 assertEquals(1, spans.length); 283 assertEquals(RELATIVE_SIZE_SPAN, spans[0]); 284 assertEquals(0, wrapped.getSpanStart(RELATIVE_SIZE_SPAN)); 285 assertEquals(EN.length(), wrapped.getSpanEnd(RELATIVE_SIZE_SPAN)); 286 287 wrapped = (Spanned) LTR_FMT.unicodeWrap(CS_EN_HE, TextDirectionHeuristics.LTR, false); 288 assertEquals(EN + HE, wrapped.toString()); 289 spans = wrapped.getSpans(0, wrapped.length(), Object.class); 290 assertEquals(1, spans.length); 291 assertEquals(RELATIVE_SIZE_SPAN, spans[0]); 292 assertEquals(0, wrapped.getSpanStart(RELATIVE_SIZE_SPAN)); 293 assertEquals(EN.length(), wrapped.getSpanEnd(RELATIVE_SIZE_SPAN)); 294 } 295 } 296