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 17package androidx.core.text; 18 19import static org.junit.Assert.assertEquals; 20import static org.junit.Assert.assertNull; 21 22import android.support.test.filters.SmallTest; 23import android.support.test.runner.AndroidJUnit4; 24import android.text.SpannableString; 25import android.text.Spanned; 26import android.text.style.RelativeSizeSpan; 27 28import org.junit.Test; 29import org.junit.runner.RunWith; 30 31import java.util.Locale; 32 33@RunWith(AndroidJUnit4.class) 34@SmallTest 35public class BidiFormatterTest { 36 37 private static final BidiFormatter LTR_FMT = BidiFormatter.getInstance(false /* LTR context */); 38 private static final BidiFormatter RTL_FMT = BidiFormatter.getInstance(true /* RTL context */); 39 40 private static final BidiFormatter LTR_FMT_EXIT_RESET = 41 new BidiFormatter.Builder(false /* LTR context */).stereoReset(false).build(); 42 private static final BidiFormatter RTL_FMT_EXIT_RESET = 43 new BidiFormatter.Builder(true /* RTL context */).stereoReset(false).build(); 44 45 private static final String EN = "abba"; 46 private static final String HE = "\u05E0\u05E1"; 47 48 private static final String LRM = "\u200E"; 49 private static final String RLM = "\u200F"; 50 private static final String LRE = "\u202A"; 51 private static final String RLE = "\u202B"; 52 private static final String PDF = "\u202C"; 53 54 @Test 55 public void testIsRtlContext() { 56 assertEquals(false, LTR_FMT.isRtlContext()); 57 assertEquals(true, RTL_FMT.isRtlContext()); 58 59 assertEquals(false, BidiFormatter.getInstance(Locale.ENGLISH).isRtlContext()); 60 assertEquals(true, BidiFormatter.getInstance(true).isRtlContext()); 61 } 62 63 @Test 64 public void testBuilderIsRtlContext() { 65 assertEquals(false, new BidiFormatter.Builder(false).build().isRtlContext()); 66 assertEquals(true, new BidiFormatter.Builder(true).build().isRtlContext()); 67 } 68 69 @Test 70 public void testIsRtl() { 71 assertEquals(true, BidiFormatter.getInstance(true).isRtl(HE)); 72 assertEquals(true, BidiFormatter.getInstance(false).isRtl(HE)); 73 74 assertEquals(false, BidiFormatter.getInstance(true).isRtl(EN)); 75 assertEquals(false, BidiFormatter.getInstance(false).isRtl(EN)); 76 } 77 78 @Test 79 public void testUnicodeWrap() { 80 // Make sure an input of null doesn't crash anything. 81 assertNull(LTR_FMT.unicodeWrap(null)); 82 83 // Uniform directionality in opposite context. 84 assertEquals("uniform dir opposite to LTR context", 85 RLE + "." + HE + "." + PDF + LRM, 86 LTR_FMT_EXIT_RESET.unicodeWrap("." + HE + ".")); 87 assertEquals("uniform dir opposite to LTR context, stereo reset", 88 LRM + RLE + "." + HE + "." + PDF + LRM, 89 LTR_FMT.unicodeWrap("." + HE + ".")); 90 assertEquals("uniform dir opposite to LTR context, stereo reset, no isolation", 91 RLE + "." + HE + "." + PDF, 92 LTR_FMT.unicodeWrap("." + HE + ".", false)); 93 assertEquals("neutral treated as opposite to LTR context", 94 RLE + "." + PDF + LRM, 95 LTR_FMT_EXIT_RESET.unicodeWrap(".", TextDirectionHeuristicsCompat.RTL)); 96 assertEquals("uniform dir opposite to RTL context", 97 LRE + "." + EN + "." + PDF + RLM, 98 RTL_FMT_EXIT_RESET.unicodeWrap("." + EN + ".")); 99 assertEquals("uniform dir opposite to RTL context, stereo reset", 100 RLM + LRE + "." + EN + "." + PDF + RLM, 101 RTL_FMT.unicodeWrap("." + EN + ".")); 102 assertEquals("uniform dir opposite to RTL context, stereo reset, no isolation", 103 LRE + "." + EN + "." + PDF, 104 RTL_FMT.unicodeWrap("." + EN + ".", false)); 105 assertEquals("neutral treated as opposite to RTL context", 106 LRE + "." + PDF + RLM, 107 RTL_FMT_EXIT_RESET.unicodeWrap(".", TextDirectionHeuristicsCompat.LTR)); 108 109 // We test mixed-directionality cases only with an explicit overall directionality parameter 110 // because the estimation logic is outside the sphere of BidiFormatter, and different 111 // estimators will treat them differently. 112 113 // Overall directionality matching context, but with opposite exit directionality. 114 assertEquals("exit dir opposite to LTR context", 115 EN + HE + LRM, 116 LTR_FMT_EXIT_RESET.unicodeWrap(EN + HE, TextDirectionHeuristicsCompat.LTR)); 117 assertEquals("exit dir opposite to LTR context, stereo reset", 118 EN + HE + LRM, 119 LTR_FMT.unicodeWrap(EN + HE, TextDirectionHeuristicsCompat.LTR)); 120 assertEquals("exit dir opposite to LTR context, stereo reset, no isolation", 121 EN + HE, 122 LTR_FMT.unicodeWrap(EN + HE, TextDirectionHeuristicsCompat.LTR, false)); 123 124 assertEquals("exit dir opposite to RTL context", 125 HE + EN + RLM, 126 RTL_FMT_EXIT_RESET.unicodeWrap(HE + EN, TextDirectionHeuristicsCompat.RTL)); 127 assertEquals("exit dir opposite to RTL context, stereo reset", 128 HE + EN + RLM, 129 RTL_FMT.unicodeWrap(HE + EN, TextDirectionHeuristicsCompat.RTL)); 130 assertEquals("exit dir opposite to RTL context, stereo reset, no isolation", 131 HE + EN, 132 RTL_FMT.unicodeWrap(HE + EN, TextDirectionHeuristicsCompat.RTL, false)); 133 134 // Overall directionality matching context, but with opposite entry directionality. 135 assertEquals("entry dir opposite to LTR context", 136 HE + EN, 137 LTR_FMT_EXIT_RESET.unicodeWrap(HE + EN, TextDirectionHeuristicsCompat.LTR)); 138 assertEquals("entry dir opposite to LTR context, stereo reset", 139 LRM + HE + EN, 140 LTR_FMT.unicodeWrap(HE + EN, TextDirectionHeuristicsCompat.LTR)); 141 assertEquals("entry dir opposite to LTR context, stereo reset, no isolation", 142 HE + EN, 143 LTR_FMT.unicodeWrap(HE + EN, TextDirectionHeuristicsCompat.LTR, false)); 144 145 assertEquals("entry dir opposite to RTL context", 146 EN + HE, 147 RTL_FMT_EXIT_RESET.unicodeWrap(EN + HE, TextDirectionHeuristicsCompat.RTL)); 148 assertEquals("entry dir opposite to RTL context, stereo reset", 149 RLM + EN + HE, 150 RTL_FMT.unicodeWrap(EN + HE, TextDirectionHeuristicsCompat.RTL)); 151 assertEquals("entry dir opposite to RTL context, stereo reset, no isolation", 152 EN + HE, 153 RTL_FMT.unicodeWrap(EN + HE, TextDirectionHeuristicsCompat.RTL, false)); 154 155 // Overall directionality matching context, but with opposite entry and exit directionality. 156 assertEquals("entry and exit dir opposite to LTR context", 157 HE + EN + HE + LRM, 158 LTR_FMT_EXIT_RESET.unicodeWrap(HE + EN + HE, TextDirectionHeuristicsCompat.LTR)); 159 assertEquals("entry and exit dir opposite to LTR context, stereo reset", 160 LRM + HE + EN + HE + LRM, 161 LTR_FMT.unicodeWrap(HE + EN + HE, TextDirectionHeuristicsCompat.LTR)); 162 assertEquals("entry and exit dir opposite to LTR context, no isolation", 163 HE + EN + HE, 164 LTR_FMT_EXIT_RESET.unicodeWrap(HE + EN + HE, TextDirectionHeuristicsCompat.LTR, 165 false)); 166 167 assertEquals("entry and exit dir opposite to RTL context", 168 EN + HE + EN + RLM, 169 RTL_FMT_EXIT_RESET.unicodeWrap(EN + HE + EN, TextDirectionHeuristicsCompat.RTL)); 170 assertEquals("entry and exit dir opposite to RTL context, no isolation", 171 EN + HE + EN, 172 RTL_FMT_EXIT_RESET.unicodeWrap(EN + HE + EN, TextDirectionHeuristicsCompat.RTL, 173 false)); 174 175 // Entry and exit directionality matching context, but with opposite overall directionality. 176 assertEquals("overall dir (but not entry or exit dir) opposite to LTR context", 177 RLE + EN + HE + EN + PDF + LRM, 178 LTR_FMT_EXIT_RESET.unicodeWrap(EN + HE + EN, TextDirectionHeuristicsCompat.RTL)); 179 assertEquals("overall dir (but not entry or exit dir) opposite to LTR context, stereo reset", 180 LRM + RLE + EN + HE + EN + PDF + LRM, 181 LTR_FMT.unicodeWrap(EN + HE + EN, TextDirectionHeuristicsCompat.RTL)); 182 assertEquals("overall dir (but not entry or exit dir) opposite to LTR context, no isolation", 183 RLE + EN + HE + EN + PDF, 184 LTR_FMT_EXIT_RESET.unicodeWrap(EN + HE + EN, TextDirectionHeuristicsCompat.RTL, 185 false)); 186 187 assertEquals("overall dir (but not entry or exit dir) opposite to RTL context", 188 LRE + HE + EN + HE + PDF + RLM, 189 RTL_FMT_EXIT_RESET.unicodeWrap(HE + EN + HE, TextDirectionHeuristicsCompat.LTR)); 190 assertEquals("overall dir (but not entry or exit dir) opposite to RTL context, stereo reset", 191 RLM + LRE + HE + EN + HE + PDF + RLM, 192 RTL_FMT.unicodeWrap(HE + EN + HE, TextDirectionHeuristicsCompat.LTR)); 193 assertEquals("overall dir (but not entry or exit dir) opposite to RTL context, no isolation", 194 LRE + HE + EN + HE + PDF, 195 RTL_FMT_EXIT_RESET.unicodeWrap(HE + EN + HE, TextDirectionHeuristicsCompat.LTR, 196 false)); 197 } 198 199 @Test 200 public void testCharSequenceApis() { 201 final CharSequence CS_HE = new SpannableString(HE); 202 assertEquals(true, BidiFormatter.getInstance(true).isRtl(CS_HE)); 203 204 final SpannableString CS_EN_HE = new SpannableString(EN + HE); 205 final Object RELATIVE_SIZE_SPAN = new RelativeSizeSpan(1.2f); 206 CS_EN_HE.setSpan(RELATIVE_SIZE_SPAN, 0, EN.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); 207 208 Spanned wrapped; 209 Object[] spans; 210 211 wrapped = (Spanned) LTR_FMT.unicodeWrap(CS_EN_HE); 212 assertEquals(EN + HE + LRM, wrapped.toString()); 213 spans = wrapped.getSpans(0, wrapped.length(), Object.class); 214 assertEquals(1, spans.length); 215 assertEquals(RELATIVE_SIZE_SPAN, spans[0]); 216 assertEquals(0, wrapped.getSpanStart(RELATIVE_SIZE_SPAN)); 217 assertEquals(EN.length(), wrapped.getSpanEnd(RELATIVE_SIZE_SPAN)); 218 219 wrapped = (Spanned) LTR_FMT.unicodeWrap(CS_EN_HE, TextDirectionHeuristicsCompat.LTR); 220 assertEquals(EN + HE + LRM, wrapped.toString()); 221 spans = wrapped.getSpans(0, wrapped.length(), Object.class); 222 assertEquals(1, spans.length); 223 assertEquals(RELATIVE_SIZE_SPAN, spans[0]); 224 assertEquals(0, wrapped.getSpanStart(RELATIVE_SIZE_SPAN)); 225 assertEquals(EN.length(), wrapped.getSpanEnd(RELATIVE_SIZE_SPAN)); 226 227 wrapped = (Spanned) LTR_FMT.unicodeWrap(CS_EN_HE, false); 228 assertEquals(EN + HE, wrapped.toString()); 229 spans = wrapped.getSpans(0, wrapped.length(), Object.class); 230 assertEquals(1, spans.length); 231 assertEquals(RELATIVE_SIZE_SPAN, spans[0]); 232 assertEquals(0, wrapped.getSpanStart(RELATIVE_SIZE_SPAN)); 233 assertEquals(EN.length(), wrapped.getSpanEnd(RELATIVE_SIZE_SPAN)); 234 235 wrapped = (Spanned) LTR_FMT.unicodeWrap(CS_EN_HE, TextDirectionHeuristicsCompat.LTR, false); 236 assertEquals(EN + HE, wrapped.toString()); 237 spans = wrapped.getSpans(0, wrapped.length(), Object.class); 238 assertEquals(1, spans.length); 239 assertEquals(RELATIVE_SIZE_SPAN, spans[0]); 240 assertEquals(0, wrapped.getSpanStart(RELATIVE_SIZE_SPAN)); 241 assertEquals(EN.length(), wrapped.getSpanEnd(RELATIVE_SIZE_SPAN)); 242 } 243} 244