| /* |
| * Copyright (C) 2010 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; |
| |
| import static org.junit.Assert.fail; |
| |
| import android.platform.test.annotations.Presubmit; |
| import android.text.Layout.Directions; |
| import android.text.StaticLayoutTest.LayoutBuilder; |
| |
| import androidx.test.filters.SmallTest; |
| import androidx.test.runner.AndroidJUnit4; |
| |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| |
| import java.util.Arrays; |
| import java.util.Formatter; |
| |
| @Presubmit |
| @SmallTest |
| @RunWith(AndroidJUnit4.class) |
| public class StaticLayoutDirectionsTest { |
| private static final char ALEF = '\u05d0'; |
| |
| private static Directions dirs(int ... dirs) { |
| return new Directions(dirs); |
| } |
| |
| // constants from Layout that are package-protected |
| private static final int RUN_LENGTH_MASK = 0x03ffffff; |
| private static final int RUN_LEVEL_SHIFT = 26; |
| private static final int RUN_LEVEL_MASK = 0x3f; |
| private static final int RUN_RTL_FLAG = 1 << RUN_LEVEL_SHIFT; |
| |
| private static final Directions DIRS_ALL_LEFT_TO_RIGHT = |
| new Directions(new int[] { 0, RUN_LENGTH_MASK }); |
| private static final Directions DIRS_ALL_RIGHT_TO_LEFT = |
| new Directions(new int[] { 0, RUN_LENGTH_MASK | RUN_RTL_FLAG }); |
| |
| private static final int LVL1_1 = 1 | (1 << RUN_LEVEL_SHIFT); |
| private static final int LVL2_1 = 1 | (2 << RUN_LEVEL_SHIFT); |
| private static final int LVL2_2 = 2 | (2 << RUN_LEVEL_SHIFT); |
| |
| private static String[] texts = { |
| "", |
| " ", |
| "a", |
| "a1", |
| "aA", |
| "a1b", |
| "a1A", |
| "aA1", |
| "aAb", |
| "aA1B", |
| "aA1B2", |
| |
| // rtl |
| "A", |
| "A1", |
| "Aa", |
| "A1B", |
| "A1a", |
| "Aa1", |
| "AaB" |
| }; |
| |
| // Expected directions are an array of start/length+level pairs, |
| // in visual order from the leading margin. |
| private static Directions[] expected = { |
| DIRS_ALL_LEFT_TO_RIGHT, |
| DIRS_ALL_LEFT_TO_RIGHT, |
| DIRS_ALL_LEFT_TO_RIGHT, |
| DIRS_ALL_LEFT_TO_RIGHT, |
| dirs(0, 1, 1, LVL1_1), |
| DIRS_ALL_LEFT_TO_RIGHT, |
| dirs(0, 2, 2, LVL1_1), |
| dirs(0, 1, 2, LVL2_1, 1, LVL1_1), |
| dirs(0, 1, 1, LVL1_1, 2, 1), |
| dirs(0, 1, 3, LVL1_1, 2, LVL2_1, 1, LVL1_1), |
| dirs(0, 1, 4, LVL2_1, 3, LVL1_1, 2, LVL2_1, 1, LVL1_1), |
| |
| // rtl |
| DIRS_ALL_RIGHT_TO_LEFT, |
| dirs(0, LVL1_1, 1, LVL2_1), |
| dirs(0, LVL1_1, 1, LVL2_1), |
| dirs(0, LVL1_1, 1, LVL2_1, 2, LVL1_1), |
| dirs(0, LVL1_1, 1, LVL2_2), |
| dirs(0, LVL1_1, 1, LVL2_2), |
| dirs(0, LVL1_1, 1, LVL2_1, 2, LVL1_1), |
| }; |
| |
| private static String pseudoBidiToReal(String src) { |
| char[] chars = src.toCharArray(); |
| for (int j = 0; j < chars.length; ++j) { |
| char c = chars[j]; |
| if (c >= 'A' && c <= 'D') { |
| chars[j] = (char)(ALEF + c - 'A'); |
| } |
| } |
| |
| return new String(chars, 0, chars.length); |
| } |
| |
| @Test |
| public void testDirections() { |
| StringBuilder buf = new StringBuilder("\n"); |
| Formatter f = new Formatter(buf); |
| |
| LayoutBuilder b = StaticLayoutTest.builder(); |
| for (int i = 0; i < texts.length; ++i) { |
| b.setText(pseudoBidiToReal(texts[i])); |
| checkDirections(b.build(), i, b.text, expected, f); |
| } |
| if (buf.length() > 1) { |
| fail(buf.toString()); |
| } |
| } |
| |
| @Test |
| public void testTrailingWhitespace() { |
| LayoutBuilder b = StaticLayoutTest.builder(); |
| b.setText(pseudoBidiToReal("Ab c")); |
| float width = b.paint.measureText(b.text, 0, 5); // exclude 'c' |
| b.setWidth(Math.round(width)); |
| Layout l = b.build(); |
| if (l.getLineCount() != 2) { |
| throw new RuntimeException("expected 2 lines, got: " + l.getLineCount()); |
| } |
| Directions result = l.getLineDirections(0); |
| Directions expected = dirs(0, LVL1_1, 1, LVL2_1, 2, 3 | (1 << Layout.RUN_LEVEL_SHIFT)); |
| expectDirections("split line", expected, result); |
| } |
| |
| @Test |
| public void testNextToRightOf() { |
| LayoutBuilder b = StaticLayoutTest.builder(); |
| b.setText(pseudoBidiToReal("aA1B2")); |
| // visual a2B1A positions 04321 |
| // 0: |a2B1A, strong is sol, after -> 0 |
| // 1: a|2B1A, strong is a, after ->, 1 |
| // 2: a2|B1A, strong is B, after -> 4 |
| // 3: a2B|1A, strong is B, before -> 3 |
| // 4: a2B1|A, strong is A, after -> 2 |
| // 5: a2B1A|, strong is eol, before -> 5 |
| int[] expected = { 0, 1, 4, 3, 2, 5 }; |
| Layout l = b.build(); |
| int n = 0; |
| for (int i = 1; i < expected.length; ++i) { |
| int t = l.getOffsetToRightOf(n); |
| if (t != expected[i]) { |
| fail("offset[" + i + "] to right of: " + n + " expected: " + |
| expected[i] + " got: " + t); |
| } |
| n = t; |
| } |
| } |
| |
| @Test |
| public void testNextToLeftOf() { |
| LayoutBuilder b = StaticLayoutTest.builder(); |
| b.setText(pseudoBidiToReal("aA1B2")); |
| int[] expected = { 0, 1, 4, 3, 2, 5 }; |
| Layout l = b.build(); |
| int n = 5; |
| for (int i = expected.length - 1; --i >= 0;) { |
| int t = l.getOffsetToLeftOf(n); |
| if (t != expected[i]) { |
| fail("offset[" + i + "] to left of: " + n + " expected: " + |
| expected[i] + " got: " + t); |
| } |
| n = t; |
| } |
| } |
| |
| // utility for displaying arrays in hex |
| private static String hexArray(int[] array) { |
| StringBuilder sb = new StringBuilder(); |
| sb.append('{'); |
| for (int i : array) { |
| if (sb.length() > 1) { |
| sb.append(", "); |
| } |
| sb.append(Integer.toHexString(i)); |
| } |
| sb.append('}'); |
| return sb.toString(); |
| } |
| |
| private void checkDirections(Layout l, int i, String text, |
| Directions[] expectedDirs, Formatter f) { |
| Directions expected = expectedDirs[i]; |
| Directions result = l.getLineDirections(0); |
| if (!Arrays.equals(expected.mDirections, result.mDirections)) { |
| f.format("%n[%2d] '%s', %s != %s", i, text, |
| hexArray(expected.mDirections), |
| hexArray(result.mDirections)); |
| } |
| } |
| |
| private void expectDirections(String msg, Directions expected, Directions result) { |
| if (!Arrays.equals(expected.mDirections, result.mDirections)) { |
| fail("expected: " + hexArray(expected.mDirections) + |
| " got: " + hexArray(result.mDirections)); |
| } |
| } |
| } |