19f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt/*
29f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt * Copyright (C) 2010 The Android Open Source Project
39f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt *
49f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt * Licensed under the Apache License, Version 2.0 (the "License"); you may not
59f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt * use this file except in compliance with the License. You may obtain a copy of
69f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt * the License at
79f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt *
89f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt * http://www.apache.org/licenses/LICENSE-2.0
99f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt *
109f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt * Unless required by applicable law or agreed to in writing, software
119f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
129f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
139f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt * License for the specific language governing permissions and limitations under
149f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt * the License.
159f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt */
169f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
179f7a4442b89cc06cb8cae6992484e7ae795323abDoug Feltpackage android.text;
189f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
199f7a4442b89cc06cb8cae6992484e7ae795323abDoug Feltimport android.text.Layout.Directions;
209f7a4442b89cc06cb8cae6992484e7ae795323abDoug Feltimport android.text.StaticLayoutTest.LayoutBuilder;
219f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
229f7a4442b89cc06cb8cae6992484e7ae795323abDoug Feltimport java.util.Arrays;
239f7a4442b89cc06cb8cae6992484e7ae795323abDoug Feltimport java.util.Formatter;
249f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
259f7a4442b89cc06cb8cae6992484e7ae795323abDoug Feltimport junit.framework.TestCase;
269f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
279f7a4442b89cc06cb8cae6992484e7ae795323abDoug Feltpublic class StaticLayoutDirectionsTest extends TestCase {
289f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    private static final char ALEF = '\u05d0';
299f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
309f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    private static Directions dirs(int ... dirs) {
319f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        return new Directions(dirs);
329f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    }
339f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
34a87c3c0af13ce2ed8ebc07a91a9fb04eeb959882Doug Felt    // constants from Layout that are package-protected
35a87c3c0af13ce2ed8ebc07a91a9fb04eeb959882Doug Felt    private static final int RUN_LENGTH_MASK = 0x03ffffff;
36a87c3c0af13ce2ed8ebc07a91a9fb04eeb959882Doug Felt    private static final int RUN_LEVEL_SHIFT = 26;
37a87c3c0af13ce2ed8ebc07a91a9fb04eeb959882Doug Felt    private static final int RUN_LEVEL_MASK = 0x3f;
38a87c3c0af13ce2ed8ebc07a91a9fb04eeb959882Doug Felt    private static final int RUN_RTL_FLAG = 1 << RUN_LEVEL_SHIFT;
39a87c3c0af13ce2ed8ebc07a91a9fb04eeb959882Doug Felt
40a87c3c0af13ce2ed8ebc07a91a9fb04eeb959882Doug Felt    private static final Directions DIRS_ALL_LEFT_TO_RIGHT =
41a87c3c0af13ce2ed8ebc07a91a9fb04eeb959882Doug Felt        new Directions(new int[] { 0, RUN_LENGTH_MASK });
42a87c3c0af13ce2ed8ebc07a91a9fb04eeb959882Doug Felt    private static final Directions DIRS_ALL_RIGHT_TO_LEFT =
43a87c3c0af13ce2ed8ebc07a91a9fb04eeb959882Doug Felt        new Directions(new int[] { 0, RUN_LENGTH_MASK | RUN_RTL_FLAG });
44a87c3c0af13ce2ed8ebc07a91a9fb04eeb959882Doug Felt
45a87c3c0af13ce2ed8ebc07a91a9fb04eeb959882Doug Felt    private static final int LVL1_1 = 1 | (1 << RUN_LEVEL_SHIFT);
46a87c3c0af13ce2ed8ebc07a91a9fb04eeb959882Doug Felt    private static final int LVL2_1 = 1 | (2 << RUN_LEVEL_SHIFT);
47a87c3c0af13ce2ed8ebc07a91a9fb04eeb959882Doug Felt    private static final int LVL2_2 = 2 | (2 << RUN_LEVEL_SHIFT);
48a87c3c0af13ce2ed8ebc07a91a9fb04eeb959882Doug Felt
499f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    private static String[] texts = {
509f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        "",
519f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        " ",
529f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        "a",
539f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        "a1",
549f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        "aA",
559f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        "a1b",
569f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        "a1A",
579f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        "aA1",
589f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        "aAb",
599f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        "aA1B",
609f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        "aA1B2",
619f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
629f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // rtl
639f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        "A",
649f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        "A1",
659f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        "Aa",
669f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        "A1B",
679f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        "A1a",
689f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        "Aa1",
699f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        "AaB"
709f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    };
719f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
729f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    // Expected directions are an array of start/length+level pairs,
739f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    // in visual order from the leading margin.
749f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    private static Directions[] expected = {
75a87c3c0af13ce2ed8ebc07a91a9fb04eeb959882Doug Felt        DIRS_ALL_LEFT_TO_RIGHT,
76a87c3c0af13ce2ed8ebc07a91a9fb04eeb959882Doug Felt        DIRS_ALL_LEFT_TO_RIGHT,
77a87c3c0af13ce2ed8ebc07a91a9fb04eeb959882Doug Felt        DIRS_ALL_LEFT_TO_RIGHT,
78a87c3c0af13ce2ed8ebc07a91a9fb04eeb959882Doug Felt        DIRS_ALL_LEFT_TO_RIGHT,
799f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        dirs(0, 1, 1, LVL1_1),
80a87c3c0af13ce2ed8ebc07a91a9fb04eeb959882Doug Felt        DIRS_ALL_LEFT_TO_RIGHT,
819f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        dirs(0, 2, 2, LVL1_1),
829f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        dirs(0, 1, 2, LVL2_1, 1, LVL1_1),
839f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        dirs(0, 1, 1, LVL1_1, 2, 1),
849f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        dirs(0, 1, 3, LVL1_1, 2, LVL2_1, 1, LVL1_1),
859f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        dirs(0, 1, 4, LVL2_1, 3, LVL1_1, 2, LVL2_1, 1, LVL1_1),
869f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
879f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // rtl
88a87c3c0af13ce2ed8ebc07a91a9fb04eeb959882Doug Felt        DIRS_ALL_RIGHT_TO_LEFT,
899f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        dirs(0, LVL1_1, 1, LVL2_1),
909f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        dirs(0, LVL1_1, 1, LVL2_1),
919f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        dirs(0, LVL1_1, 1, LVL2_1, 2, LVL1_1),
929f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        dirs(0, LVL1_1, 1, LVL2_2),
939f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        dirs(0, LVL1_1, 1, LVL2_2),
949f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        dirs(0, LVL1_1, 1, LVL2_1, 2, LVL1_1),
959f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    };
969f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
979f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    private static String pseudoBidiToReal(String src) {
989f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        char[] chars = src.toCharArray();
999f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        for (int j = 0; j < chars.length; ++j) {
1009f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            char c = chars[j];
1019f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            if (c >= 'A' && c <= 'D') {
1029f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                chars[j] = (char)(ALEF + c - 'A');
1039f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            }
1049f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        }
1059f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
1069f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        return new String(chars, 0, chars.length);
1079f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    }
1089f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
1099f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    // @SmallTest
1109f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    public void testDirections() {
1119f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        StringBuilder buf = new StringBuilder("\n");
1129f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        Formatter f = new Formatter(buf);
1139f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
1149f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        LayoutBuilder b = StaticLayoutTest.builder();
1159f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        for (int i = 0; i < texts.length; ++i) {
1169f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            b.setText(pseudoBidiToReal(texts[i]));
1179f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            checkDirections(b.build(), i, b.text, expected, f);
1189f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        }
1199f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        if (buf.length() > 1) {
1209f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            fail(buf.toString());
1219f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        }
1229f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    }
123a87c3c0af13ce2ed8ebc07a91a9fb04eeb959882Doug Felt
1249f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    // @SmallTest
1259f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    public void testTrailingWhitespace() {
1269f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        LayoutBuilder b = StaticLayoutTest.builder();
1279f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        b.setText(pseudoBidiToReal("Ab   c"));
1289f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        float width = b.paint.measureText(b.text, 0, 5);  // exclude 'c'
1299f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        b.setWidth(Math.round(width));
1309f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        Layout l = b.build();
1319f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        if (l.getLineCount() != 2) {
1329f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            throw new RuntimeException("expected 2 lines, got: " + l.getLineCount());
1339f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        }
1349f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        Directions result = l.getLineDirections(0);
1359f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        Directions expected = dirs(0, LVL1_1, 1, LVL2_1, 2, 3 | (1 << Layout.RUN_LEVEL_SHIFT));
1369f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        expectDirections("split line", expected, result);
1379f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    }
1389f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
1399f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    public void testNextToRightOf() {
1409f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        LayoutBuilder b = StaticLayoutTest.builder();
1419f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        b.setText(pseudoBidiToReal("aA1B2"));
1429f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // visual a2B1A positions 04321
1439f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // 0: |a2B1A, strong is sol, after -> 0
1449f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // 1: a|2B1A, strong is a, after ->, 1
1459f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // 2: a2|B1A, strong is B, after -> 4
1469f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // 3: a2B|1A, strong is B, before -> 3
1479f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // 4: a2B1|A, strong is A, after -> 2
1489f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        // 5: a2B1A|, strong is eol, before -> 5
1499f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        int[] expected = { 0, 1, 4, 3, 2, 5 };
1509f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        Layout l = b.build();
1519f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        int n = 0;
1529f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        for (int i = 1; i < expected.length; ++i) {
1539f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            int t = l.getOffsetToRightOf(n);
1549f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            if (t != expected[i]) {
155a87c3c0af13ce2ed8ebc07a91a9fb04eeb959882Doug Felt                fail("offset[" + i + "] to right of: " + n + " expected: " +
1569f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                        expected[i] + " got: " + t);
1579f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            }
1589f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            n = t;
1599f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        }
1609f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    }
1619f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
1629f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    public void testNextToLeftOf() {
1639f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        LayoutBuilder b = StaticLayoutTest.builder();
1649f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        b.setText(pseudoBidiToReal("aA1B2"));
1659f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        int[] expected = { 0, 1, 4, 3, 2, 5 };
1669f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        Layout l = b.build();
1679f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        int n = 5;
1689f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        for (int i = expected.length - 1; --i >= 0;) {
1699f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            int t = l.getOffsetToLeftOf(n);
1709f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            if (t != expected[i]) {
171a87c3c0af13ce2ed8ebc07a91a9fb04eeb959882Doug Felt                fail("offset[" + i + "] to left of: " + n + " expected: " +
1729f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                        expected[i] + " got: " + t);
1739f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            }
1749f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            n = t;
1759f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        }
1769f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    }
177a87c3c0af13ce2ed8ebc07a91a9fb04eeb959882Doug Felt
1789f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    // utility, not really a test
179a87c3c0af13ce2ed8ebc07a91a9fb04eeb959882Doug Felt    /*
1809f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    public void testMeasureText1() {
1819f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        LayoutBuilder b = StaticLayoutTest.builder();
1829f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        String text = "ABC"; // "abAB"
1839f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        b.setText(pseudoBidiToReal(text));
1849f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        Layout l = b.build();
1859f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        Directions directions = l.getLineDirections(0);
186a87c3c0af13ce2ed8ebc07a91a9fb04eeb959882Doug Felt
1879f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        TextPaint workPaint = new TextPaint();
1889f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
1899f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        int dir = -1; // LEFT_TO_RIGHT
1909f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        boolean trailing = true;
1919f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        boolean alt = true;
1929f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        do {
1939f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            dir = -dir;
1949f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            do {
1959f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                trailing = !trailing;
1969f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                for (int offset = 0, end = b.text.length(); offset <= end; ++offset) {
1979f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                    float width = Layout.measureText(b.paint,
1989f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                            workPaint,
1999f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                            b.text,
2009f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                            0, offset, end,
2019f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                            dir, directions,
2029f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                            trailing, false,
2039f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                            null);
2049f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                    Log.i("BIDI", "dir: " + dir + " trail: " + trailing +
2059f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                            " offset: " + offset + " width: " + width);
2069f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                }
2079f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            } while (!trailing);
2089f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        } while (dir > 0);
2099f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    }
210a87c3c0af13ce2ed8ebc07a91a9fb04eeb959882Doug Felt    */
2119f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt
2129f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    // utility for displaying arrays in hex
2139f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    private static String hexArray(int[] array) {
2149f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        StringBuilder sb = new StringBuilder();
2159f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        sb.append('{');
2169f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        for (int i : array) {
2179f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            if (sb.length() > 1) {
2189f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                sb.append(", ");
2199f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            }
2209f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            sb.append(Integer.toHexString(i));
2219f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        }
2229f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        sb.append('}');
2239f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        return sb.toString();
2249f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    }
225a87c3c0af13ce2ed8ebc07a91a9fb04eeb959882Doug Felt
226a87c3c0af13ce2ed8ebc07a91a9fb04eeb959882Doug Felt    private void checkDirections(Layout l, int i, String text,
2279f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt            Directions[] expectedDirs, Formatter f) {
2289f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        Directions expected = expectedDirs[i];
2299f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        Directions result = l.getLineDirections(0);
2309f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        if (!Arrays.equals(expected.mDirections, result.mDirections)) {
231a87c3c0af13ce2ed8ebc07a91a9fb04eeb959882Doug Felt            f.format("%n[%2d] '%s', %s != %s", i, text,
2329f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                    hexArray(expected.mDirections),
2339f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                    hexArray(result.mDirections));
2349f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        }
2359f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    }
236a87c3c0af13ce2ed8ebc07a91a9fb04eeb959882Doug Felt
2379f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    private void expectDirections(String msg, Directions expected, Directions result) {
2389f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        if (!Arrays.equals(expected.mDirections, result.mDirections)) {
239a87c3c0af13ce2ed8ebc07a91a9fb04eeb959882Doug Felt            fail("expected: " + hexArray(expected.mDirections) +
2409f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt                    " got: " + hexArray(result.mDirections));
2419f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt        }
2429f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt    }
2439f7a4442b89cc06cb8cae6992484e7ae795323abDoug Felt}
244