1 /* 2 * Copyright (C) 2019 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.contentcaptureservice.cts.unit; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.testng.Assert.assertThrows; 22 23 import android.content.Context; 24 import android.os.Bundle; 25 import android.os.LocaleList; 26 import android.os.Parcel; 27 import android.platform.test.annotations.AppModeFull; 28 import android.view.View; 29 import android.view.ViewStructure.HtmlInfo; 30 import android.view.autofill.AutofillId; 31 import android.view.autofill.AutofillValue; 32 import android.view.contentcapture.ViewNode; 33 import android.view.contentcapture.ViewNode.ViewStructureImpl; 34 import android.widget.FrameLayout; 35 36 import androidx.test.InstrumentationRegistry; 37 38 import org.junit.Test; 39 import org.junit.runner.RunWith; 40 import org.mockito.Mock; 41 import org.mockito.junit.MockitoJUnitRunner; 42 43 import java.util.Locale; 44 45 @AppModeFull(reason = "unit test") 46 @RunWith(MockitoJUnitRunner.class) 47 public class ViewNodeTest { 48 49 private final Context mContext = InstrumentationRegistry.getTargetContext(); 50 51 @Mock 52 private HtmlInfo mHtmlInfoMock; 53 54 @Test 55 public void testAutofillIdMethods_orphanView() { 56 View view = new View(mContext); 57 AutofillId initialId = new AutofillId(42); 58 view.setAutofillId(initialId); 59 60 ViewStructureImpl structure = new ViewStructureImpl(view); 61 ViewNode node = structure.getNode(); 62 63 assertThat(node.getAutofillId()).isEqualTo(initialId); 64 assertThat(node.getParentAutofillId()).isNull(); 65 66 AutofillId newId = new AutofillId(108); 67 structure.setAutofillId(newId); 68 assertThat(node.getAutofillId()).isEqualTo(newId); 69 assertThat(node.getParentAutofillId()).isNull(); 70 71 structure.setAutofillId(new AutofillId(66), 6); 72 assertThat(node.getAutofillId()).isEqualTo(new AutofillId(66, 6)); 73 assertThat(node.getParentAutofillId()).isEqualTo(new AutofillId(66)); 74 } 75 76 @Test 77 public void testAutofillIdMethods_parentedView() { 78 FrameLayout parent = new FrameLayout(mContext); 79 AutofillId initialParentId = new AutofillId(48); 80 parent.setAutofillId(initialParentId); 81 82 View child = new View(mContext); 83 AutofillId initialChildId = new AutofillId(42); 84 child.setAutofillId(initialChildId); 85 86 parent.addView(child); 87 88 ViewStructureImpl structure = new ViewStructureImpl(child); 89 ViewNode node = structure.getNode(); 90 91 assertThat(node.getAutofillId()).isEqualTo(initialChildId); 92 assertThat(node.getParentAutofillId()).isEqualTo(initialParentId); 93 94 AutofillId newChildId = new AutofillId(108); 95 structure.setAutofillId(newChildId); 96 assertThat(node.getAutofillId()).isEqualTo(newChildId); 97 assertThat(node.getParentAutofillId()).isEqualTo(initialParentId); 98 99 AutofillId newParentId = new AutofillId(15162342); 100 parent.setAutofillId(newParentId); 101 assertThat(node.getAutofillId()).isEqualTo(newChildId); 102 assertThat(node.getParentAutofillId()).isEqualTo(initialParentId); 103 104 structure.setAutofillId(new AutofillId(66), 6); 105 assertThat(node.getAutofillId()).isEqualTo(new AutofillId(66, 6)); 106 assertThat(node.getParentAutofillId()).isEqualTo(new AutofillId(66)); 107 } 108 109 @Test 110 public void testAutofillIdMethods_explicitIdsConstructor() { 111 AutofillId initialParentId = new AutofillId(42); 112 ViewStructureImpl structure = new ViewStructureImpl(initialParentId, 108, 666); 113 ViewNode node = structure.getNode(); 114 115 assertThat(node.getAutofillId()).isEqualTo(new AutofillId(initialParentId, 108, 666)); 116 assertThat(node.getParentAutofillId()).isEqualTo(initialParentId); 117 118 AutofillId newChildId = new AutofillId(108); 119 structure.setAutofillId(newChildId); 120 assertThat(node.getAutofillId()).isEqualTo(newChildId); 121 assertThat(node.getParentAutofillId()).isEqualTo(initialParentId); 122 123 structure.setAutofillId(new AutofillId(66), 6); 124 assertThat(node.getAutofillId()).isEqualTo(new AutofillId(66, 6)); 125 assertThat(node.getParentAutofillId()).isEqualTo(new AutofillId(66)); 126 } 127 128 @Test 129 public void testInvalidSetters() { 130 View view = new View(mContext); 131 AutofillId initialId = new AutofillId(42); 132 view.setAutofillId(initialId); 133 134 ViewStructureImpl structure = new ViewStructureImpl(view); 135 ViewNode node = structure.getNode(); 136 assertThat(node.getAutofillId()).isEqualTo(initialId); // sanity check 137 138 assertThrows(NullPointerException.class, () -> structure.setAutofillId(null)); 139 assertThat(node.getAutofillId()).isEqualTo(initialId); // invariant 140 141 assertThrows(NullPointerException.class, () -> structure.setAutofillId(null, 666)); 142 assertThat(node.getAutofillId()).isEqualTo(initialId); // invariant 143 144 assertThrows(NullPointerException.class, () -> structure.setTextIdEntry(null)); 145 assertThat(node.getTextIdEntry()).isNull(); 146 } 147 148 @Test 149 public void testValidProperties_directly() { 150 ViewStructureImpl structure = newSimpleStructure(); 151 assertSimpleStructure(structure); 152 assertSimpleNode(structure.getNode()); 153 } 154 155 @Test 156 public void testValidProperties_throughParcel() { 157 ViewStructureImpl structure = newSimpleStructure(); 158 final ViewNode node = structure.getNode(); 159 assertSimpleNode(node); // sanity check 160 161 final ViewNode clone = cloneThroughParcel(node); 162 assertSimpleNode(clone); 163 } 164 165 @Test 166 public void testComplexText_directly() { 167 ViewStructureImpl structure = newStructureWithComplexText(); 168 assertStructureWithComplexText(structure); 169 assertNodeWithComplexText(structure.getNode()); 170 } 171 172 @Test 173 public void testComplexText_throughParcel() { 174 ViewStructureImpl structure = newStructureWithComplexText(); 175 final ViewNode node = structure.getNode(); 176 assertNodeWithComplexText(node); // sanity check 177 178 ViewNode clone = cloneThroughParcel(node); 179 assertNodeWithComplexText(clone); 180 } 181 182 @Test 183 public void testVisibility() { 184 // Visibility is a special case becase it use flag masks, so we want to make sure it works 185 // fine 186 View view = new View(mContext); 187 ViewStructureImpl structure = new ViewStructureImpl(view); 188 ViewNode node = structure.getNode(); 189 190 structure.setVisibility(View.VISIBLE); 191 assertThat(node.getVisibility()).isEqualTo(View.VISIBLE); 192 assertThat(cloneThroughParcel(node).getVisibility()).isEqualTo(View.VISIBLE); 193 194 structure.setVisibility(View.GONE); 195 assertThat(node.getVisibility()).isEqualTo(View.GONE); 196 assertThat(cloneThroughParcel(node).getVisibility()).isEqualTo(View.GONE); 197 198 structure.setVisibility(View.VISIBLE); 199 assertThat(node.getVisibility()).isEqualTo(View.VISIBLE); 200 assertThat(cloneThroughParcel(node).getVisibility()).isEqualTo(View.VISIBLE); 201 202 structure.setVisibility(View.INVISIBLE); 203 assertThat(node.getVisibility()).isEqualTo(View.INVISIBLE); 204 assertThat(cloneThroughParcel(node).getVisibility()).isEqualTo(View.INVISIBLE); 205 206 structure.setVisibility(View.INVISIBLE | View.GONE); 207 assertThat(node.getVisibility()).isEqualTo(View.INVISIBLE | View.GONE); 208 assertThat(cloneThroughParcel(node).getVisibility()).isEqualTo(View.INVISIBLE | View.GONE); 209 210 211 final int invalidValue = Math.max(Math.max(View.VISIBLE, View.INVISIBLE), View.GONE) * 2; 212 structure.setVisibility(View.VISIBLE); 213 structure.setVisibility(invalidValue); // should be ignored 214 assertThat(node.getVisibility()).isEqualTo(View.VISIBLE); 215 assertThat(cloneThroughParcel(node).getVisibility()).isEqualTo(View.VISIBLE); 216 217 structure.setVisibility(View.GONE | invalidValue); 218 assertThat(node.getVisibility()).isEqualTo(View.GONE); 219 assertThat(cloneThroughParcel(node).getVisibility()).isEqualTo(View.GONE); 220 } 221 222 /** 223 * Creates a {@link ViewStructureImpl} that can be asserted through 224 * {@link #assertSimpleNode(ViewNode)}. 225 */ 226 private ViewStructureImpl newSimpleStructure() { 227 View view = new View(mContext); 228 view.setAutofillId(new AutofillId(42)); 229 230 ViewStructureImpl structure = new ViewStructureImpl(view); 231 232 // Basic properties 233 structure.setText("Text is set!"); 234 structure.setClassName("Classy!"); 235 structure.setContentDescription("Described I am!"); 236 structure.setVisibility(View.INVISIBLE); 237 238 // Autofill properties 239 structure.setAutofillType(View.AUTOFILL_TYPE_TEXT); 240 structure.setAutofillHints(new String[] { "Auto", "Man" }); 241 structure.setAutofillOptions(new String[] { "Maybe" }); 242 structure.setAutofillValue(AutofillValue.forText("Malkovich")); 243 244 // Extra text properties 245 structure.setMinTextEms(6); 246 structure.setMaxTextLength(66); 247 structure.setMaxTextEms(666); 248 structure.setInputType(42); 249 structure.setTextIdEntry("TEXT, Y U NO ENTRY?"); 250 structure.setLocaleList(new LocaleList(Locale.US, Locale.ENGLISH)); 251 252 // Resource id 253 structure.setId(16, "package.name", "type.name", "entry.name"); 254 255 // Dimensions 256 structure.setDimens(4, 8, 15, 16, 23, 42); 257 258 // Boolean properties 259 structure.setAssistBlocked(true); 260 structure.setEnabled(true); 261 structure.setClickable(true); 262 structure.setLongClickable(true); 263 structure.setContextClickable(true); 264 structure.setFocusable(true); 265 structure.setFocused(true); 266 structure.setAccessibilityFocused(true); 267 structure.setChecked(true); 268 structure.setActivated(true); 269 structure.setOpaque(true); 270 271 // Bundle 272 assertThat(structure.hasExtras()).isFalse(); 273 final Bundle bundle = structure.getExtras(); 274 assertThat(bundle).isNotNull(); 275 bundle.putString("Marlon", "Bundle"); 276 assertThat(structure.hasExtras()).isTrue(); 277 return structure; 278 } 279 280 /** 281 * Asserts the properties of a {@link ViewNode} that was created by 282 * {@link #newSimpleStructure()}. 283 */ 284 private void assertSimpleNode(ViewNode node) { 285 286 // Basic properties 287 assertThat(node.getAutofillId()).isEqualTo(new AutofillId(42)); 288 assertThat(node.getParentAutofillId()).isNull(); 289 assertThat(node.getText()).isEqualTo("Text is set!"); 290 assertThat(node.getClassName()).isEqualTo("Classy!"); 291 assertThat(node.getContentDescription().toString()).isEqualTo("Described I am!"); 292 assertThat(node.getVisibility()).isEqualTo(View.INVISIBLE); 293 294 // Autofill properties 295 assertThat(node.getAutofillType()).isEqualTo(View.AUTOFILL_TYPE_TEXT); 296 assertThat(node.getAutofillHints()).asList().containsExactly("Auto", "Man").inOrder(); 297 assertThat(node.getAutofillOptions()).asList().containsExactly("Maybe").inOrder(); 298 assertThat(node.getAutofillValue().getTextValue()).isEqualTo("Malkovich"); 299 300 // Extra text properties 301 assertThat(node.getMinTextEms()).isEqualTo(6); 302 assertThat(node.getMaxTextLength()).isEqualTo(66); 303 assertThat(node.getMaxTextEms()).isEqualTo(666); 304 assertThat(node.getInputType()).isEqualTo(42); 305 assertThat(node.getTextIdEntry()).isEqualTo("TEXT, Y U NO ENTRY?"); 306 assertThat(node.getLocaleList()).isEqualTo(new LocaleList(Locale.US, Locale.ENGLISH)); 307 308 // Resource id 309 assertThat(node.getId()).isEqualTo(16); 310 assertThat(node.getIdPackage()).isEqualTo("package.name"); 311 assertThat(node.getIdType()).isEqualTo("type.name"); 312 assertThat(node.getIdEntry()).isEqualTo("entry.name"); 313 314 // Dimensions 315 assertThat(node.getLeft()).isEqualTo(4); 316 assertThat(node.getTop()).isEqualTo(8); 317 assertThat(node.getScrollX()).isEqualTo(15); 318 assertThat(node.getScrollY()).isEqualTo(16); 319 assertThat(node.getWidth()).isEqualTo(23); 320 assertThat(node.getHeight()).isEqualTo(42); 321 322 // Boolean properties 323 assertThat(node.isAssistBlocked()).isTrue(); 324 assertThat(node.isEnabled()).isTrue(); 325 assertThat(node.isClickable()).isTrue(); 326 assertThat(node.isLongClickable()).isTrue(); 327 assertThat(node.isContextClickable()).isTrue(); 328 assertThat(node.isFocusable()).isTrue(); 329 assertThat(node.isFocused()).isTrue(); 330 assertThat(node.isAccessibilityFocused()).isTrue(); 331 assertThat(node.isChecked()).isTrue(); 332 assertThat(node.isActivated()).isTrue(); 333 assertThat(node.isOpaque()).isTrue(); 334 335 // Bundle 336 final Bundle bundle = node.getExtras(); 337 assertThat(bundle).isNotNull(); 338 assertThat(bundle.size()).isEqualTo(1); 339 assertThat(bundle.getString("Marlon")).isEqualTo("Bundle"); 340 } 341 342 /** 343 * Asserts the properties of a {@link ViewStructureImpl} that was created by 344 * {@link #newSimpleStructure()}. 345 */ 346 private void assertSimpleStructure(ViewStructureImpl structure) { 347 assertThat(structure.getAutofillId()).isEqualTo(new AutofillId(42)); 348 assertThat(structure.getText()).isEqualTo("Text is set!"); 349 350 // Bundle 351 final Bundle bundle = structure.getExtras(); 352 assertThat(bundle.size()).isEqualTo(1); 353 assertThat(bundle.getString("Marlon")).isEqualTo("Bundle"); 354 } 355 356 /** 357 * Creates a {@link ViewStructureImpl} with "complex" text properties (such as selection); it 358 * can be asserted through {@link #assertNodeWithComplexText(ViewNode)}. 359 */ 360 private ViewStructureImpl newStructureWithComplexText() { 361 View view = new View(mContext); 362 ViewStructureImpl structure = new ViewStructureImpl(view); 363 structure.setText("IGNORE ME!"); 364 structure.setText("Now we're talking!", 4, 8); 365 structure.setHint("Soylent Green is SPOILER ALERT"); 366 structure.setTextStyle(15.0f, 16, 23, 42); 367 structure.setTextLines(new int[] {4, 8, 15} , new int[] {16, 23, 42}); 368 return structure; 369 } 370 371 /** 372 * Asserts the properties of a {@link ViewNode} that was created by 373 * {@link #newStructureWithComplexText()}. 374 */ 375 private void assertNodeWithComplexText(ViewNode node) { 376 assertThat(node.getText()).isEqualTo("Now we're talking!"); 377 assertThat(node.getTextSelectionStart()).isEqualTo(4); 378 assertThat(node.getTextSelectionEnd()).isEqualTo(8); 379 assertThat(node.getHint()).isEqualTo("Soylent Green is SPOILER ALERT"); 380 assertThat(node.getTextSize()).isWithin(1.0e-10f).of(15.0f); 381 assertThat(node.getTextColor()).isEqualTo(16); 382 assertThat(node.getTextBackgroundColor()).isEqualTo(23); 383 assertThat(node.getTextStyle()).isEqualTo(42); 384 assertThat(node.getTextLineCharOffsets()).asList().containsExactly(4, 8, 15).inOrder(); 385 assertThat(node.getTextLineBaselines()).asList().containsExactly(16, 23, 42).inOrder(); 386 } 387 388 /** 389 * Asserts the properties of a {@link ViewStructureImpl} that was created by 390 * {@link #newStructureWithComplexText()}. 391 */ 392 private void assertStructureWithComplexText(ViewStructureImpl structure) { 393 assertThat(structure.getText()).isEqualTo("Now we're talking!"); 394 assertThat(structure.getTextSelectionStart()).isEqualTo(4); 395 assertThat(structure.getTextSelectionEnd()).isEqualTo(8); 396 assertThat(structure.getHint()).isEqualTo("Soylent Green is SPOILER ALERT"); 397 } 398 399 private ViewNode cloneThroughParcel(ViewNode node) { 400 Parcel parcel = Parcel.obtain(); 401 402 try { 403 // Write to parcel 404 parcel.setDataPosition(0); // Sanity / paranoid check 405 ViewNode.writeToParcel(parcel, node, 0); 406 407 // Read from parcel 408 parcel.setDataPosition(0); 409 ViewNode clone = ViewNode.readFromParcel(parcel); 410 assertThat(clone).isNotNull(); 411 return clone; 412 } finally { 413 parcel.recycle(); 414 } 415 } 416 } 417