1f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi/*
2f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi * Copyright (C) 2016 The Android Open Source Project
3f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi *
4f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi * use this file except in compliance with the License. You may obtain a copy of
6f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi * the License at
7f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi *
8f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi * http://www.apache.org/licenses/LICENSE-2.0
9f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi *
10f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi * Unless required by applicable law or agreed to in writing, software
11f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi * License for the specific language governing permissions and limitations under
14f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi * the License.
15f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi */
16f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi
17f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagipackage android.text;
18f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi
19f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagiimport android.app.Activity;
20f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagiimport android.graphics.Canvas;
21f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagiimport android.graphics.Paint;
22f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagiimport android.graphics.Paint.FontMetricsInt;
23f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagiimport android.os.Bundle;
24f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagiimport android.perftests.utils.BenchmarkState;
25f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagiimport android.perftests.utils.PerfStatusReporter;
26f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagiimport android.perftests.utils.StubActivity;
27f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi
28f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagiimport android.support.test.InstrumentationRegistry;
29f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagiimport android.support.test.filters.LargeTest;
30f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagiimport android.support.test.rule.ActivityTestRule;
31f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagiimport android.support.test.runner.AndroidJUnit4;
32f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagiimport android.text.style.ReplacementSpan;
33f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagiimport android.util.ArraySet;
34f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi
35f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagiimport static android.text.Layout.Alignment.ALIGN_NORMAL;
36f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi
37f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagiimport java.util.Arrays;
38f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagiimport java.util.Collection;
39f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagiimport java.util.Locale;
40f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagiimport java.util.Random;
41f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi
42f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagiimport org.junit.Assert;
43f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagiimport org.junit.Rule;
44f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagiimport org.junit.Test;
45f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagiimport org.junit.runner.RunWith;
46f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagiimport org.junit.runners.Parameterized.Parameters;
47f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagiimport org.junit.runners.Parameterized;
48f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi
49f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi@LargeTest
50f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi@RunWith(Parameterized.class)
51f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagipublic class DynamicLayoutPerfTest {
52f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi
53f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi    @Parameters(name = "{0}")
54f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi    public static Collection cases() {
55f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi        return Arrays.asList(new Object[][] {
56f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi            { "0%", 0.0f},
57f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi            { "1%", 0.01f},
58f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi            { "5%", 0.05f},
59f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi            { "30%", 0.3f},
60f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi            { "100%", 1.0f},
61f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi        });
62f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi    }
63f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi
64f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi    private final String mMetricKey;
65f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi    private final float mProbability;
66f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi    public DynamicLayoutPerfTest(String metricKey, float probability) {
67f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi        mMetricKey = metricKey;
68f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi        mProbability = probability;
69f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi    }
70f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi
71f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi    private static class MockReplacementSpan extends ReplacementSpan {
72f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi        @Override
73f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi        public int getSize(Paint paint, CharSequence text, int start, int end, FontMetricsInt fm) {
74f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi            return 10;
75f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi        }
76f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi
77f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi        @Override
78f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi        public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top,
79f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi                int y, int bottom, Paint paint) {
80f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi        }
81f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi    }
82f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi
83f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi    @Rule
84f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi    public ActivityTestRule<StubActivity> mActivityRule = new ActivityTestRule(StubActivity.class);
85f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi
86f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi    @Rule
87f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
88f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi
89f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi
90f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi    private final static String ALPHABETS = "abcdefghijklmnopqrstuvwxyz";
91f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi
92f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi    private SpannableStringBuilder getText() {
93f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi        final long seed = 1234567890;
94f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi        final Random r = new Random(seed);
95f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi        final SpannableStringBuilder builder = new SpannableStringBuilder();
96f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi
97f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi        final int paragraphCount = 100;
98f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi        for (int i = 0; i < paragraphCount; i++) {
99f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi            final int wordCount = 5 + r.nextInt(20);
100f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi            final boolean containsReplacementSpan = r.nextFloat() < mProbability;
101f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi            final int replacedWordIndex = containsReplacementSpan ? r.nextInt(wordCount) : -1;
102f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi            for (int j = 0; j < wordCount; j++) {
103f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi                final int startIndex = builder.length();
104f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi                final int wordLength = 1 + r.nextInt(10);
105f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi                for (int k = 0; k < wordLength; k++) {
106f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi                    char c = ALPHABETS.charAt(r.nextInt(ALPHABETS.length()));
107f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi                    builder.append(c);
108f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi                }
109f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi                if (replacedWordIndex == j) {
110f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi                    builder.setSpan(new MockReplacementSpan(), startIndex,
111f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi                            builder.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
112f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi                }
113f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi                builder.append(' ');
114f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi            }
115f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi            builder.append('\n');
116f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi        }
117f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi        return builder;
118f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi    }
119f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi
120f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi    @Test
121f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi    public void testGetBlocksAlwaysNeedToBeRedrawn() {
122f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi        final SpannableStringBuilder text = getText();
123f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi        final DynamicLayout layout = new DynamicLayout(text, new TextPaint(), 1000,
124f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi                ALIGN_NORMAL, 0, 0, false);
125f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi
126f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
127f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi        final int steps = 10;
128f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi        while (state.keepRunning()) {
129f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi            for (int i = 0; i < steps; i++) {
130f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi                int offset = (text.length() * i) / steps;
131f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi                text.insert(offset, "\n");
132f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi                text.delete(offset, offset + 1);
133f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi                final ArraySet<Integer> set = layout.getBlocksAlwaysNeedToBeRedrawn();
134f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi                if (set != null) {
135f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi                    for (int j = 0; j < set.size(); j++) {
136f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi                        layout.getBlockIndex(set.valueAt(j));
137f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi                    }
138f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi                }
139f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi            }
140f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi        }
141f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi    }
142f5af4a344e086efa87740a0154cace064465fe54Keisuke Kuroyanagi}
143