1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * 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, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 17 package android.text; 18 19 import static org.junit.Assert.fail; 20 21 import android.platform.test.annotations.Presubmit; 22 import android.text.Layout.Directions; 23 import android.text.StaticLayoutTest.LayoutBuilder; 24 25 import androidx.test.filters.SmallTest; 26 import androidx.test.runner.AndroidJUnit4; 27 28 import org.junit.Test; 29 import org.junit.runner.RunWith; 30 31 import java.util.Arrays; 32 import java.util.Formatter; 33 34 @Presubmit 35 @SmallTest 36 @RunWith(AndroidJUnit4.class) 37 public class StaticLayoutDirectionsTest { 38 private static final char ALEF = '\u05d0'; 39 dirs(int ... dirs)40 private static Directions dirs(int ... dirs) { 41 return new Directions(dirs); 42 } 43 44 // constants from Layout that are package-protected 45 private static final int RUN_LENGTH_MASK = 0x03ffffff; 46 private static final int RUN_LEVEL_SHIFT = 26; 47 private static final int RUN_LEVEL_MASK = 0x3f; 48 private static final int RUN_RTL_FLAG = 1 << RUN_LEVEL_SHIFT; 49 50 private static final Directions DIRS_ALL_LEFT_TO_RIGHT = 51 new Directions(new int[] { 0, RUN_LENGTH_MASK }); 52 private static final Directions DIRS_ALL_RIGHT_TO_LEFT = 53 new Directions(new int[] { 0, RUN_LENGTH_MASK | RUN_RTL_FLAG }); 54 55 private static final int LVL1_1 = 1 | (1 << RUN_LEVEL_SHIFT); 56 private static final int LVL2_1 = 1 | (2 << RUN_LEVEL_SHIFT); 57 private static final int LVL2_2 = 2 | (2 << RUN_LEVEL_SHIFT); 58 59 private static String[] texts = { 60 "", 61 " ", 62 "a", 63 "a1", 64 "aA", 65 "a1b", 66 "a1A", 67 "aA1", 68 "aAb", 69 "aA1B", 70 "aA1B2", 71 72 // rtl 73 "A", 74 "A1", 75 "Aa", 76 "A1B", 77 "A1a", 78 "Aa1", 79 "AaB" 80 }; 81 82 // Expected directions are an array of start/length+level pairs, 83 // in visual order from the leading margin. 84 private static Directions[] expected = { 85 DIRS_ALL_LEFT_TO_RIGHT, 86 DIRS_ALL_LEFT_TO_RIGHT, 87 DIRS_ALL_LEFT_TO_RIGHT, 88 DIRS_ALL_LEFT_TO_RIGHT, 89 dirs(0, 1, 1, LVL1_1), 90 DIRS_ALL_LEFT_TO_RIGHT, 91 dirs(0, 2, 2, LVL1_1), 92 dirs(0, 1, 2, LVL2_1, 1, LVL1_1), 93 dirs(0, 1, 1, LVL1_1, 2, 1), 94 dirs(0, 1, 3, LVL1_1, 2, LVL2_1, 1, LVL1_1), 95 dirs(0, 1, 4, LVL2_1, 3, LVL1_1, 2, LVL2_1, 1, LVL1_1), 96 97 // rtl 98 DIRS_ALL_RIGHT_TO_LEFT, 99 dirs(0, LVL1_1, 1, LVL2_1), 100 dirs(0, LVL1_1, 1, LVL2_1), 101 dirs(0, LVL1_1, 1, LVL2_1, 2, LVL1_1), 102 dirs(0, LVL1_1, 1, LVL2_2), 103 dirs(0, LVL1_1, 1, LVL2_2), 104 dirs(0, LVL1_1, 1, LVL2_1, 2, LVL1_1), 105 }; 106 pseudoBidiToReal(String src)107 private static String pseudoBidiToReal(String src) { 108 char[] chars = src.toCharArray(); 109 for (int j = 0; j < chars.length; ++j) { 110 char c = chars[j]; 111 if (c >= 'A' && c <= 'D') { 112 chars[j] = (char)(ALEF + c - 'A'); 113 } 114 } 115 116 return new String(chars, 0, chars.length); 117 } 118 119 @Test testDirections()120 public void testDirections() { 121 StringBuilder buf = new StringBuilder("\n"); 122 Formatter f = new Formatter(buf); 123 124 LayoutBuilder b = StaticLayoutTest.builder(); 125 for (int i = 0; i < texts.length; ++i) { 126 b.setText(pseudoBidiToReal(texts[i])); 127 checkDirections(b.build(), i, b.text, expected, f); 128 } 129 if (buf.length() > 1) { 130 fail(buf.toString()); 131 } 132 } 133 134 @Test testTrailingWhitespace()135 public void testTrailingWhitespace() { 136 LayoutBuilder b = StaticLayoutTest.builder(); 137 b.setText(pseudoBidiToReal("Ab c")); 138 float width = b.paint.measureText(b.text, 0, 5); // exclude 'c' 139 b.setWidth(Math.round(width)); 140 Layout l = b.build(); 141 if (l.getLineCount() != 2) { 142 throw new RuntimeException("expected 2 lines, got: " + l.getLineCount()); 143 } 144 Directions result = l.getLineDirections(0); 145 Directions expected = dirs(0, LVL1_1, 1, LVL2_1, 2, 3 | (1 << Layout.RUN_LEVEL_SHIFT)); 146 expectDirections("split line", expected, result); 147 } 148 149 @Test testNextToRightOf()150 public void testNextToRightOf() { 151 LayoutBuilder b = StaticLayoutTest.builder(); 152 b.setText(pseudoBidiToReal("aA1B2")); 153 // visual a2B1A positions 04321 154 // 0: |a2B1A, strong is sol, after -> 0 155 // 1: a|2B1A, strong is a, after ->, 1 156 // 2: a2|B1A, strong is B, after -> 4 157 // 3: a2B|1A, strong is B, before -> 3 158 // 4: a2B1|A, strong is A, after -> 2 159 // 5: a2B1A|, strong is eol, before -> 5 160 int[] expected = { 0, 1, 4, 3, 2, 5 }; 161 Layout l = b.build(); 162 int n = 0; 163 for (int i = 1; i < expected.length; ++i) { 164 int t = l.getOffsetToRightOf(n); 165 if (t != expected[i]) { 166 fail("offset[" + i + "] to right of: " + n + " expected: " + 167 expected[i] + " got: " + t); 168 } 169 n = t; 170 } 171 } 172 173 @Test testNextToLeftOf()174 public void testNextToLeftOf() { 175 LayoutBuilder b = StaticLayoutTest.builder(); 176 b.setText(pseudoBidiToReal("aA1B2")); 177 int[] expected = { 0, 1, 4, 3, 2, 5 }; 178 Layout l = b.build(); 179 int n = 5; 180 for (int i = expected.length - 1; --i >= 0;) { 181 int t = l.getOffsetToLeftOf(n); 182 if (t != expected[i]) { 183 fail("offset[" + i + "] to left of: " + n + " expected: " + 184 expected[i] + " got: " + t); 185 } 186 n = t; 187 } 188 } 189 190 // utility for displaying arrays in hex hexArray(int[] array)191 private static String hexArray(int[] array) { 192 StringBuilder sb = new StringBuilder(); 193 sb.append('{'); 194 for (int i : array) { 195 if (sb.length() > 1) { 196 sb.append(", "); 197 } 198 sb.append(Integer.toHexString(i)); 199 } 200 sb.append('}'); 201 return sb.toString(); 202 } 203 checkDirections(Layout l, int i, String text, Directions[] expectedDirs, Formatter f)204 private void checkDirections(Layout l, int i, String text, 205 Directions[] expectedDirs, Formatter f) { 206 Directions expected = expectedDirs[i]; 207 Directions result = l.getLineDirections(0); 208 if (!Arrays.equals(expected.mDirections, result.mDirections)) { 209 f.format("%n[%2d] '%s', %s != %s", i, text, 210 hexArray(expected.mDirections), 211 hexArray(result.mDirections)); 212 } 213 } 214 expectDirections(String msg, Directions expected, Directions result)215 private void expectDirections(String msg, Directions expected, Directions result) { 216 if (!Arrays.equals(expected.mDirections, result.mDirections)) { 217 fail("expected: " + hexArray(expected.mDirections) + 218 " got: " + hexArray(result.mDirections)); 219 } 220 } 221 } 222