1 /*
2  * Copyright (C) 2016 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 
17 package android.widget;
18 
19 import android.perftests.utils.BenchmarkState;
20 import android.perftests.utils.PerfStatusReporter;
21 import android.perftests.utils.StubActivity;
22 import android.text.Selection;
23 import android.view.KeyEvent;
24 import android.view.View.MeasureSpec;
25 import android.view.ViewGroup;
26 
27 import androidx.test.InstrumentationRegistry;
28 import androidx.test.filters.LargeTest;
29 import androidx.test.rule.ActivityTestRule;
30 
31 import org.junit.Assert;
32 import org.junit.Rule;
33 import org.junit.Test;
34 import org.junit.runner.RunWith;
35 import org.junit.runners.Parameterized;
36 import org.junit.runners.Parameterized.Parameters;
37 
38 import java.util.Arrays;
39 import java.util.Collection;
40 
41 @LargeTest
42 @RunWith(Parameterized.class)
43 public class EditTextBackspacePerfTest {
44 
45     private static final String BOY = "\uD83D\uDC66";  // U+1F466
46     private static final String US_FLAG = "\uD83C\uDDFA\uD83C\uDDF8";  // U+1F1FA U+1F1F8
47     private static final String FAMILY =
48             // U+1F469 U+200D U+1F469 U+200D U+1F467 U+200D U+1F467
49             "\uD83D\uDC69\u200D\uD83D\uDC69\u200D\uD83D\uDC67\u200D\uD83D\uDC67";
50     private static final String EMOJI_MODIFIER = "\uD83C\uDFFD";  // U+1F3FD
51     private static final String KEYCAP = "\u20E3";
52     private static final String COLOR_COPYRIGHT = "\u00A9\uFE0F";
53 
54     @Parameters(name = "{0}")
cases()55     public static Collection cases() {
56         return Arrays.asList(new Object[][] {
57             { "Latin", "aaa", 1 },
58             { "Flags", US_FLAG + US_FLAG + US_FLAG, 4 },
59             { "EmojiModifier",
60                 BOY + EMOJI_MODIFIER + BOY + EMOJI_MODIFIER + BOY + EMOJI_MODIFIER, 4 },
61             { "KeyCap", "1" + KEYCAP + "1" + KEYCAP + "1" + KEYCAP, 2 },
62             { "ZwjSequence", FAMILY + FAMILY + FAMILY, 11 },
63             { "VariationSelector", COLOR_COPYRIGHT + COLOR_COPYRIGHT + COLOR_COPYRIGHT, 2 },
64         });
65     }
66 
67     private final String mMetricKey;
68     private final String mText;
69     private final int mCursorPos;
70 
71     private static final KeyEvent BACKSPACE_KEY_EVENT =
72             new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL);
73     private static final KeyEvent RIGHT_ARROW_KEY_EVENT =
74             new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT);
75 
EditTextBackspacePerfTest(String metricKey, String text, int cursorPos)76     public EditTextBackspacePerfTest(String metricKey, String text, int cursorPos) {
77         mMetricKey = metricKey;
78         mText = text;
79         mCursorPos = cursorPos;
80     }
81 
82     @Rule
83     public ActivityTestRule<StubActivity> mActivityRule = new ActivityTestRule(StubActivity.class);
84 
85     @Rule
86     public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
87 
prepareTextForBackspace(EditText editText)88     private void prepareTextForBackspace(EditText editText) {
89         editText.setText(mText, TextView.BufferType.EDITABLE);
90         Selection.setSelection(editText.getText(), 0, 0);
91 
92         // Do layout it here since the cursor movement requires layout information but it
93         // happens asynchronously even if the view is attached to an Activity.
94         editText.setLayoutParams(new ViewGroup.LayoutParams(
95                 ViewGroup.LayoutParams.WRAP_CONTENT,
96                 ViewGroup.LayoutParams.WRAP_CONTENT));
97         editText.invalidate();
98         editText.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
99                 MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
100         editText.layout(0, 0, 1024, 768);
101 
102         // mText contains three grapheme clusters. Move the cursor to the 2nd grapheme
103         // cluster by forwarding right arrow key event.
104         editText.onKeyDown(RIGHT_ARROW_KEY_EVENT.getKeyCode(), RIGHT_ARROW_KEY_EVENT);
105         Assert.assertEquals(mCursorPos, Selection.getSelectionStart(editText.getText()));
106     }
107 
108     @Test
testBackspace()109     public void testBackspace() {
110         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
111             EditText editText = new EditText(mActivityRule.getActivity());
112 
113             BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
114             while (state.keepRunning()) {
115                 // Prepare the test data for this iteration with pausing timer.
116                 state.pauseTiming();
117                 prepareTextForBackspace(editText);
118                 state.resumeTiming();
119 
120                 editText.onKeyDown(BACKSPACE_KEY_EVENT.getKeyCode(), BACKSPACE_KEY_EVENT);
121             }
122         });
123     }
124 }
125