1 /* 2 * Copyright (C) 2008 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.text.method.cts; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertNotNull; 21 import static org.junit.Assert.assertSame; 22 import static org.junit.Assert.assertTrue; 23 import static org.junit.Assert.fail; 24 import static org.mockito.Mockito.any; 25 import static org.mockito.Mockito.anyInt; 26 import static org.mockito.Mockito.atLeastOnce; 27 import static org.mockito.Mockito.reset; 28 import static org.mockito.Mockito.spy; 29 import static org.mockito.Mockito.verify; 30 31 import android.app.Instrumentation; 32 import android.app.UiAutomation; 33 import android.os.ParcelFileDescriptor; 34 import android.provider.Settings.SettingNotFoundException; 35 import android.provider.Settings.System; 36 import android.support.test.InstrumentationRegistry; 37 import android.support.test.filters.MediumTest; 38 import android.support.test.rule.ActivityTestRule; 39 import android.support.test.runner.AndroidJUnit4; 40 import android.text.method.PasswordTransformationMethod; 41 import android.util.TypedValue; 42 import android.view.KeyCharacterMap; 43 import android.widget.Button; 44 import android.widget.EditText; 45 import android.widget.LinearLayout; 46 import android.widget.LinearLayout.LayoutParams; 47 48 import com.android.compatibility.common.util.CtsKeyEventUtil; 49 import com.android.compatibility.common.util.PollingCheck; 50 51 import org.junit.After; 52 import org.junit.Before; 53 import org.junit.Rule; 54 import org.junit.Test; 55 import org.junit.runner.RunWith; 56 57 import java.io.FileInputStream; 58 import java.io.InputStream; 59 import java.util.Scanner; 60 61 /** 62 * Test {@link PasswordTransformationMethod}. 63 */ 64 @MediumTest 65 @RunWith(AndroidJUnit4.class) 66 public class PasswordTransformationMethodTest { 67 private static final int EDIT_TXT_ID = 1; 68 69 /** original text */ 70 private static final String TEST_CONTENT = "test content"; 71 72 /** text after transformation: ************(12 dots) */ 73 private static final String TEST_CONTENT_TRANSFORMED = 74 "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022"; 75 76 private Instrumentation mInstrumentation; 77 private CtsActivity mActivity; 78 private int mPasswordPrefBackUp; 79 private boolean isPasswordPrefSaved; 80 private PasswordTransformationMethod mMethod; 81 private EditText mEditText; 82 private CharSequence mTransformedText; 83 84 @Rule 85 public ActivityTestRule<CtsActivity> mActivityRule = new ActivityTestRule<>(CtsActivity.class); 86 87 @Before 88 public void setup() throws Throwable { 89 mActivity = mActivityRule.getActivity(); 90 PollingCheck.waitFor(1000, mActivity::hasWindowFocus); 91 mInstrumentation = InstrumentationRegistry.getInstrumentation(); 92 mMethod = spy(new PasswordTransformationMethod()); 93 94 mActivityRule.runOnUiThread(() -> { 95 EditText editText = new EditTextNoIme(mActivity); 96 editText.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10); 97 editText.setId(EDIT_TXT_ID); 98 editText.setTransformationMethod(mMethod); 99 Button button = new Button(mActivity); 100 LinearLayout layout = new LinearLayout(mActivity); 101 layout.setOrientation(LinearLayout.VERTICAL); 102 layout.addView(editText, new LayoutParams(LayoutParams.MATCH_PARENT, 103 LayoutParams.WRAP_CONTENT)); 104 layout.addView(button, new LayoutParams(LayoutParams.MATCH_PARENT, 105 LayoutParams.WRAP_CONTENT)); 106 mActivity.setContentView(layout); 107 editText.requestFocus(); 108 }); 109 mInstrumentation.waitForIdleSync(); 110 111 mEditText = (EditText) mActivity.findViewById(EDIT_TXT_ID); 112 assertTrue(mEditText.isFocused()); 113 114 enableAppOps(); 115 savePasswordPref(); 116 switchShowPassword(true); 117 } 118 119 @After 120 public void teardown() { 121 resumePasswordPref(); 122 } 123 124 private void enableAppOps() { 125 UiAutomation uiAutomation = mInstrumentation.getUiAutomation(); 126 127 StringBuilder cmd = new StringBuilder(); 128 cmd.append("appops set "); 129 cmd.append(mActivity.getPackageName()); 130 cmd.append(" android:write_settings allow"); 131 uiAutomation.executeShellCommand(cmd.toString()); 132 133 StringBuilder query = new StringBuilder(); 134 query.append("appops get "); 135 query.append(mActivity.getPackageName()); 136 query.append(" android:write_settings"); 137 String queryStr = query.toString(); 138 139 String result = "No operations."; 140 while (result.contains("No operations")) { 141 ParcelFileDescriptor pfd = uiAutomation.executeShellCommand(queryStr); 142 InputStream inputStream = new FileInputStream(pfd.getFileDescriptor()); 143 result = convertStreamToString(inputStream); 144 } 145 } 146 147 private String convertStreamToString(InputStream is) { 148 try (Scanner scanner = new Scanner(is).useDelimiter("\\A")) { 149 return scanner.hasNext() ? scanner.next() : ""; 150 } 151 } 152 153 @Test 154 public void testConstructor() { 155 new PasswordTransformationMethod(); 156 } 157 158 @Test 159 public void testTextChangedCallBacks() throws Throwable { 160 mActivityRule.runOnUiThread(() -> 161 mTransformedText = mMethod.getTransformation(mEditText.getText(), mEditText)); 162 163 reset(mMethod); 164 // 12-key support 165 KeyCharacterMap keymap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD); 166 if (keymap.getKeyboardType() == KeyCharacterMap.NUMERIC) { 167 // "HELLO" in case of 12-key(NUMERIC) keyboard 168 CtsKeyEventUtil.sendKeys(mInstrumentation, mEditText, 169 "6*4 6*3 7*5 DPAD_RIGHT 7*5 7*6 DPAD_RIGHT"); 170 } 171 else { 172 CtsKeyEventUtil.sendKeys(mInstrumentation, mEditText, "H E 2*L O"); 173 } 174 verify(mMethod, atLeastOnce()).beforeTextChanged(any(), anyInt(), anyInt(), anyInt()); 175 verify(mMethod, atLeastOnce()).onTextChanged(any(), anyInt(), anyInt(), anyInt()); 176 verify(mMethod, atLeastOnce()).afterTextChanged(any()); 177 178 reset(mMethod); 179 180 mActivityRule.runOnUiThread(() -> mEditText.append(" ")); 181 182 // the appended string will not get transformed immediately 183 // "***** " 184 assertEquals("\u2022\u2022\u2022\u2022\u2022 ", mTransformedText.toString()); 185 verify(mMethod, atLeastOnce()).beforeTextChanged(any(), anyInt(), anyInt(), anyInt()); 186 verify(mMethod, atLeastOnce()).onTextChanged(any(), anyInt(), anyInt(), anyInt()); 187 verify(mMethod, atLeastOnce()).afterTextChanged(any()); 188 189 // it will get transformed after a while 190 // "******" 191 PollingCheck.waitFor(() -> mTransformedText.toString() 192 .equals("\u2022\u2022\u2022\u2022\u2022\u2022")); 193 } 194 195 @Test 196 public void testGetTransformation() { 197 PasswordTransformationMethod method = new PasswordTransformationMethod(); 198 199 assertEquals(TEST_CONTENT_TRANSFORMED, 200 method.getTransformation(TEST_CONTENT, null).toString()); 201 202 CharSequence transformed = method.getTransformation(null, mEditText); 203 assertNotNull(transformed); 204 try { 205 transformed.toString(); 206 fail("Should throw NullPointerException if the source is null."); 207 } catch (NullPointerException e) { 208 // expected 209 } 210 } 211 212 @Test 213 public void testGetInstance() { 214 PasswordTransformationMethod method0 = PasswordTransformationMethod.getInstance(); 215 assertNotNull(method0); 216 217 PasswordTransformationMethod method1 = PasswordTransformationMethod.getInstance(); 218 assertNotNull(method1); 219 assertSame(method0, method1); 220 } 221 222 private void savePasswordPref() { 223 try { 224 mPasswordPrefBackUp = System.getInt(mActivity.getContentResolver(), 225 System.TEXT_SHOW_PASSWORD); 226 isPasswordPrefSaved = true; 227 } catch (SettingNotFoundException e) { 228 isPasswordPrefSaved = false; 229 } 230 } 231 232 private void resumePasswordPref() { 233 if (isPasswordPrefSaved) { 234 System.putInt(mActivity.getContentResolver(), System.TEXT_SHOW_PASSWORD, 235 mPasswordPrefBackUp); 236 } 237 } 238 239 private void switchShowPassword(boolean on) { 240 System.putInt(mActivity.getContentResolver(), System.TEXT_SHOW_PASSWORD, 241 on ? 1 : 0); 242 } 243 } 244