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.server.cts; 18 19 import com.android.tradefed.device.CollectingOutputReceiver; 20 import com.android.tradefed.device.DeviceNotAvailableException; 21 import com.android.tradefed.device.ITestDevice; 22 import com.android.tradefed.log.LogUtil.CLog; 23 import com.android.tradefed.testtype.DeviceTestCase; 24 25 import java.util.HashMap; 26 import java.util.Map; 27 import java.util.regex.Pattern; 28 29 public class CrossAppDragAndDropTests extends DeviceTestCase { 30 // Constants copied from ActivityManager.StackId. If they are changed there, these must be 31 // updated. 32 /** ID of stack where fullscreen activities are normally launched into. */ 33 private static final int FULLSCREEN_WORKSPACE_STACK_ID = 1; 34 35 /** ID of stack where freeform/resized activities are normally launched into. */ 36 private static final int FREEFORM_WORKSPACE_STACK_ID = FULLSCREEN_WORKSPACE_STACK_ID + 1; 37 38 /** ID of stack that occupies a dedicated region of the screen. */ 39 private static final int DOCKED_STACK_ID = FREEFORM_WORKSPACE_STACK_ID + 1; 40 41 /** ID of stack that always on top (always visible) when it exists. */ 42 private static final int PINNED_STACK_ID = DOCKED_STACK_ID + 1; 43 44 private static final String AM_FORCE_STOP = "am force-stop "; 45 private static final String AM_MOVE_TASK = "am stack move-task "; 46 private static final String AM_RESIZE_TASK = "am task resize "; 47 private static final String AM_REMOVE_STACK = "am stack remove "; 48 private static final String AM_START_N = "am start -n "; 49 private static final String AM_STACK_LIST = "am stack list"; 50 private static final String INPUT_MOUSE_SWIPE = "input mouse swipe "; 51 private static final String TASK_ID_PREFIX = "taskId"; 52 53 // Regex pattern to match adb shell am stack list output of the form: 54 // taskId=<TASK_ID>: <componentName> bounds=[LEFT,TOP][RIGHT,BOTTOM] 55 private static final String TASK_REGEX_PATTERN_STRING = 56 "taskId=[0-9]+: %s bounds=\\[[0-9]+,[0-9]+\\]\\[[0-9]+,[0-9]+\\]"; 57 58 private static final int SWIPE_DURATION_MS = 500; 59 60 private static final String SOURCE_PACKAGE_NAME = "android.wm.cts.dndsourceapp"; 61 private static final String TARGET_PACKAGE_NAME = "android.wm.cts.dndtargetapp"; 62 private static final String TARGET_23_PACKAGE_NAME = "android.wm.cts.dndtargetappsdk23"; 63 64 65 private static final String SOURCE_ACTIVITY_NAME = "DragSource"; 66 private static final String TARGET_ACTIVITY_NAME = "DropTarget"; 67 68 private static final String FILE_GLOBAL = "file_global"; 69 private static final String FILE_LOCAL = "file_local"; 70 private static final String DISALLOW_GLOBAL = "disallow_global"; 71 private static final String CANCEL_SOON = "cancel_soon"; 72 private static final String GRANT_NONE = "grant_none"; 73 private static final String GRANT_READ = "grant_read"; 74 private static final String GRANT_WRITE = "grant_write"; 75 private static final String GRANT_READ_PREFIX = "grant_read_prefix"; 76 private static final String GRANT_READ_NOPREFIX = "grant_read_noprefix"; 77 private static final String GRANT_READ_PERSISTABLE = "grant_read_persistable"; 78 79 private static final String REQUEST_NONE = "request_none"; 80 private static final String REQUEST_READ = "request_read"; 81 private static final String REQUEST_READ_NESTED = "request_read_nested"; 82 private static final String REQUEST_TAKE_PERSISTABLE = "request_take_persistable"; 83 private static final String REQUEST_WRITE = "request_write"; 84 85 private static final String SOURCE_LOG_TAG = "DragSource"; 86 private static final String TARGET_LOG_TAG = "DropTarget"; 87 88 private static final String RESULT_KEY_START_DRAG = "START_DRAG"; 89 private static final String RESULT_KEY_DRAG_STARTED = "DRAG_STARTED"; 90 private static final String RESULT_KEY_DRAG_ENDED = "DRAG_ENDED"; 91 private static final String RESULT_KEY_EXTRAS = "EXTRAS"; 92 private static final String RESULT_KEY_DROP_RESULT = "DROP"; 93 private static final String RESULT_KEY_ACCESS_BEFORE = "BEFORE"; 94 private static final String RESULT_KEY_ACCESS_AFTER = "AFTER"; 95 private static final String RESULT_KEY_CLIP_DATA_ERROR = "CLIP_DATA_ERROR"; 96 private static final String RESULT_KEY_CLIP_DESCR_ERROR = "CLIP_DESCR_ERROR"; 97 private static final String RESULT_KEY_LOCAL_STATE_ERROR = "LOCAL_STATE_ERROR"; 98 99 private static final String RESULT_MISSING = "Missing"; 100 private static final String RESULT_OK = "OK"; 101 private static final String RESULT_EXCEPTION = "Exception"; 102 private static final String RESULT_NULL_DROP_PERMISSIONS = "Null DragAndDropPermissions"; 103 104 private static final String AM_SUPPORTS_SPLIT_SCREEN_MULTIWINDOW = 105 "am supports-split-screen-multi-window"; 106 107 private ITestDevice mDevice; 108 109 private Map<String, String> mSourceResults; 110 private Map<String, String> mTargetResults; 111 112 private String mSourcePackageName; 113 private String mTargetPackageName; 114 115 @Override setUp()116 protected void setUp() throws Exception { 117 super.setUp(); 118 119 mDevice = getDevice(); 120 121 if (!supportsDragAndDrop()) { 122 return; 123 } 124 125 mSourcePackageName = SOURCE_PACKAGE_NAME; 126 mTargetPackageName = TARGET_PACKAGE_NAME; 127 cleanupState(); 128 } 129 130 @Override tearDown()131 protected void tearDown() throws Exception { 132 super.tearDown(); 133 134 if (!supportsDragAndDrop()) { 135 return; 136 } 137 138 mDevice.executeShellCommand(AM_FORCE_STOP + mSourcePackageName); 139 mDevice.executeShellCommand(AM_FORCE_STOP + mTargetPackageName); 140 } 141 executeShellCommand(String command)142 private String executeShellCommand(String command) throws DeviceNotAvailableException { 143 return mDevice.executeShellCommand(command); 144 } 145 clearLogs()146 private void clearLogs() throws DeviceNotAvailableException { 147 executeShellCommand("logcat -c"); 148 } 149 getStartCommand(String componentName, String modeExtra)150 private String getStartCommand(String componentName, String modeExtra) { 151 return AM_START_N + componentName + " -e mode " + modeExtra; 152 } 153 getMoveTaskCommand(int taskId, int stackId)154 private String getMoveTaskCommand(int taskId, int stackId) throws Exception { 155 return AM_MOVE_TASK + taskId + " " + stackId + " true"; 156 } 157 getResizeTaskCommand(int taskId, Point topLeft, Point bottomRight)158 private String getResizeTaskCommand(int taskId, Point topLeft, Point bottomRight) 159 throws Exception { 160 return AM_RESIZE_TASK + taskId + " " + topLeft.x + " " + topLeft.y + " " + bottomRight.x 161 + " " + bottomRight.y; 162 } 163 getComponentName(String packageName, String activityName)164 private String getComponentName(String packageName, String activityName) { 165 return packageName + "/" + packageName + "." + activityName; 166 } 167 168 /** 169 * Make sure that the special activity stacks are removed and the ActivityManager/WindowManager 170 * is in a good state. 171 */ cleanupState()172 private void cleanupState() throws Exception { 173 executeShellCommand(AM_FORCE_STOP + SOURCE_PACKAGE_NAME); 174 executeShellCommand(AM_FORCE_STOP + TARGET_PACKAGE_NAME); 175 executeShellCommand(AM_FORCE_STOP + TARGET_23_PACKAGE_NAME); 176 unlockDevice(); 177 178 // Reinitialize the docked stack to force the window manager to reset its default bounds. 179 // See b/29068935. 180 clearLogs(); 181 final String componentName = getComponentName(mSourcePackageName, SOURCE_ACTIVITY_NAME); 182 executeShellCommand(getStartCommand(componentName, null) + " --stack " + 183 FULLSCREEN_WORKSPACE_STACK_ID); 184 final int taskId = getActivityTaskId(componentName); 185 // Moving a task from the full screen stack to the docked stack resets 186 // WindowManagerService#mDockedStackCreateBounds. 187 executeShellCommand(getMoveTaskCommand(taskId, DOCKED_STACK_ID)); 188 waitForResume(mSourcePackageName, SOURCE_ACTIVITY_NAME); 189 executeShellCommand(AM_FORCE_STOP + SOURCE_PACKAGE_NAME); 190 191 // Remove special stacks. 192 executeShellCommand(AM_REMOVE_STACK + PINNED_STACK_ID); 193 executeShellCommand(AM_REMOVE_STACK + DOCKED_STACK_ID); 194 executeShellCommand(AM_REMOVE_STACK + FREEFORM_WORKSPACE_STACK_ID); 195 } 196 launchDockedActivity(String packageName, String activityName, String mode)197 private void launchDockedActivity(String packageName, String activityName, String mode) 198 throws Exception { 199 clearLogs(); 200 final String componentName = getComponentName(packageName, activityName); 201 executeShellCommand(getStartCommand(componentName, mode) + " --stack " + DOCKED_STACK_ID); 202 waitForResume(packageName, activityName); 203 } 204 launchFullscreenActivity(String packageName, String activityName, String mode)205 private void launchFullscreenActivity(String packageName, String activityName, String mode) 206 throws Exception { 207 clearLogs(); 208 final String componentName = getComponentName(packageName, activityName); 209 executeShellCommand(getStartCommand(componentName, mode) + " --stack " 210 + FULLSCREEN_WORKSPACE_STACK_ID); 211 waitForResume(packageName, activityName); 212 } 213 214 /** 215 * @param displaySize size of the display 216 * @param leftSide {@code true} to launch the app taking up the left half of the display, 217 * {@code false} to launch the app taking up the right half of the display. 218 */ launchFreeformActivity(String packageName, String activityName, String mode, Point displaySize, boolean leftSide)219 private void launchFreeformActivity(String packageName, String activityName, String mode, 220 Point displaySize, boolean leftSide) throws Exception{ 221 clearLogs(); 222 final String componentName = getComponentName(packageName, activityName); 223 executeShellCommand(getStartCommand(componentName, mode) + " --stack " 224 + FREEFORM_WORKSPACE_STACK_ID); 225 waitForResume(packageName, activityName); 226 Point topLeft = new Point(leftSide ? 0 : displaySize.x / 2, 0); 227 Point bottomRight = new Point(leftSide ? displaySize.x / 2 : displaySize.x, displaySize.y); 228 executeShellCommand(getResizeTaskCommand(getActivityTaskId(componentName), topLeft, 229 bottomRight)); 230 } 231 waitForResume(String packageName, String activityName)232 private void waitForResume(String packageName, String activityName) throws Exception { 233 final String fullActivityName = packageName + "." + activityName; 234 int retryCount = 3; 235 do { 236 Thread.sleep(500); 237 String logs = executeShellCommand("logcat -d -b events"); 238 for (String line : logs.split("\\n")) { 239 if(line.contains("am_on_resume_called") && line.contains(fullActivityName)) { 240 return; 241 } 242 } 243 } while (retryCount-- > 0); 244 245 throw new Exception(fullActivityName + " has failed to start"); 246 } 247 injectInput(Point from, Point to, int durationMs)248 private void injectInput(Point from, Point to, int durationMs) throws Exception { 249 executeShellCommand( 250 INPUT_MOUSE_SWIPE + from.x + " " + from.y + " " + to.x + " " + to.y + " " + 251 durationMs); 252 } 253 254 static class Point { 255 public int x, y; 256 Point(int _x, int _y)257 public Point(int _x, int _y) { 258 x=_x; 259 y=_y; 260 } 261 Point()262 public Point() {} 263 } 264 findTaskInfo(String name)265 private String findTaskInfo(String name) throws Exception { 266 CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver(); 267 mDevice.executeShellCommand(AM_STACK_LIST, outputReceiver); 268 final String output = outputReceiver.getOutput(); 269 final StringBuilder builder = new StringBuilder(); 270 builder.append("Finding task info for task: "); 271 builder.append(name); 272 builder.append("\nParsing adb shell am output: " ); 273 builder.append(output); 274 CLog.i(builder.toString()); 275 final Pattern pattern = Pattern.compile(String.format(TASK_REGEX_PATTERN_STRING, name)); 276 for (String line : output.split("\\n")) { 277 final String truncatedLine; 278 // Only look for the activity name before the "topActivity" string. 279 final int pos = line.indexOf("topActivity"); 280 if (pos > 0) { 281 truncatedLine = line.substring(0, pos); 282 } else { 283 truncatedLine = line; 284 } 285 if (pattern.matcher(truncatedLine).find()) { 286 return truncatedLine; 287 } 288 } 289 return ""; 290 } 291 getWindowBounds(String name, Point from, Point to)292 private boolean getWindowBounds(String name, Point from, Point to) throws Exception { 293 final String taskInfo = findTaskInfo(name); 294 final String[] sections = taskInfo.split("\\["); 295 if (sections.length > 2) { 296 try { 297 parsePoint(sections[1], from); 298 parsePoint(sections[2], to); 299 return true; 300 } catch (Exception e) { 301 return false; 302 } 303 } 304 return false; 305 } 306 getActivityTaskId(String name)307 private int getActivityTaskId(String name) throws Exception { 308 final String taskInfo = findTaskInfo(name); 309 for (String word : taskInfo.split("\\s+")) { 310 if (word.startsWith(TASK_ID_PREFIX)) { 311 final String withColon = word.split("=")[1]; 312 return Integer.parseInt(withColon.substring(0, withColon.length() - 1)); 313 } 314 } 315 return -1; 316 } 317 getDisplaySize()318 private Point getDisplaySize() throws Exception { 319 final String output = executeShellCommand("wm size"); 320 final String[] sizes = output.split(" ")[2].split("x"); 321 return new Point(Integer.valueOf(sizes[0].trim()), Integer.valueOf(sizes[1].trim())); 322 } 323 getWindowCenter(String name)324 private Point getWindowCenter(String name) throws Exception { 325 Point p1 = new Point(); 326 Point p2 = new Point(); 327 if (getWindowBounds(name, p1, p2)) { 328 return new Point((p1.x + p2.x) / 2, (p1.y + p2.y) / 2); 329 } 330 return null; 331 } 332 parsePoint(String string, Point point)333 private void parsePoint(String string, Point point) { 334 final String[] parts = string.split("[,|\\]]"); 335 point.x = Integer.parseInt(parts[0]); 336 point.y = Integer.parseInt(parts[1]); 337 } 338 unlockDevice()339 private void unlockDevice() throws DeviceNotAvailableException { 340 // Wake up the device, if necessary. 341 executeShellCommand("input keyevent 224"); 342 // Unlock the screen. 343 executeShellCommand("input keyevent 82"); 344 } 345 getLogResults(String className, String lastResultKey)346 private Map<String, String> getLogResults(String className, String lastResultKey) 347 throws Exception { 348 int retryCount = 10; 349 Map<String, String> output = new HashMap<String, String>(); 350 do { 351 352 String logs = executeShellCommand("logcat -v brief -d " + className + ":I" + " *:S"); 353 for (String line : logs.split("\\n")) { 354 if (line.startsWith("I/" + className)) { 355 String payload = line.split(":")[1].trim(); 356 final String[] split = payload.split("="); 357 if (split.length > 1) { 358 output.put(split[0], split[1]); 359 } 360 } 361 } 362 if (output.containsKey(lastResultKey)) { 363 return output; 364 } 365 } while (retryCount-- > 0); 366 return output; 367 } 368 assertDropResult(String sourceMode, String targetMode, String expectedDropResult)369 private void assertDropResult(String sourceMode, String targetMode, String expectedDropResult) 370 throws Exception { 371 assertDragAndDropResults(sourceMode, targetMode, RESULT_OK, expectedDropResult, RESULT_OK); 372 } 373 assertNoGlobalDragEvents(String sourceMode, String expectedStartDragResult)374 private void assertNoGlobalDragEvents(String sourceMode, String expectedStartDragResult) 375 throws Exception { 376 assertDragAndDropResults( 377 sourceMode, REQUEST_NONE, expectedStartDragResult, RESULT_MISSING, RESULT_MISSING); 378 } 379 assertDragAndDropResults(String sourceMode, String targetMode, String expectedStartDragResult, String expectedDropResult, String expectedListenerResults)380 private void assertDragAndDropResults(String sourceMode, String targetMode, 381 String expectedStartDragResult, String expectedDropResult, 382 String expectedListenerResults) throws Exception { 383 if (!supportsDragAndDrop()) { 384 return; 385 } 386 387 if (supportsSplitScreenMultiWindow()) { 388 launchDockedActivity(mSourcePackageName, SOURCE_ACTIVITY_NAME, sourceMode); 389 launchFullscreenActivity(mTargetPackageName, TARGET_ACTIVITY_NAME, targetMode); 390 } else if (supportsFreeformMultiWindow()) { 391 // Fallback to try to launch two freeform windows side by side. 392 Point displaySize = getDisplaySize(); 393 launchFreeformActivity(mSourcePackageName, SOURCE_ACTIVITY_NAME, sourceMode, 394 displaySize, true /* leftSide */); 395 launchFreeformActivity(mTargetPackageName, TARGET_ACTIVITY_NAME, targetMode, 396 displaySize, false /* leftSide */); 397 } else { 398 return; 399 } 400 401 clearLogs(); 402 403 injectInput( 404 getWindowCenter(getComponentName(mSourcePackageName, SOURCE_ACTIVITY_NAME)), 405 getWindowCenter(getComponentName(mTargetPackageName, TARGET_ACTIVITY_NAME)), 406 SWIPE_DURATION_MS); 407 408 mSourceResults = getLogResults(SOURCE_LOG_TAG, RESULT_KEY_START_DRAG); 409 assertSourceResult(RESULT_KEY_START_DRAG, expectedStartDragResult); 410 411 mTargetResults = getLogResults(TARGET_LOG_TAG, RESULT_KEY_DRAG_ENDED); 412 assertTargetResult(RESULT_KEY_DROP_RESULT, expectedDropResult); 413 if (!RESULT_MISSING.equals(expectedDropResult)) { 414 assertTargetResult(RESULT_KEY_ACCESS_BEFORE, RESULT_EXCEPTION); 415 assertTargetResult(RESULT_KEY_ACCESS_AFTER, RESULT_EXCEPTION); 416 } 417 assertListenerResults(expectedListenerResults); 418 } 419 assertListenerResults(String expectedResult)420 private void assertListenerResults(String expectedResult) throws Exception { 421 assertTargetResult(RESULT_KEY_DRAG_STARTED, expectedResult); 422 assertTargetResult(RESULT_KEY_DRAG_ENDED, expectedResult); 423 assertTargetResult(RESULT_KEY_EXTRAS, expectedResult); 424 425 assertTargetResult(RESULT_KEY_CLIP_DATA_ERROR, RESULT_MISSING); 426 assertTargetResult(RESULT_KEY_CLIP_DESCR_ERROR, RESULT_MISSING); 427 assertTargetResult(RESULT_KEY_LOCAL_STATE_ERROR, RESULT_MISSING); 428 } 429 assertSourceResult(String resultKey, String expectedResult)430 private void assertSourceResult(String resultKey, String expectedResult) throws Exception { 431 assertResult(mSourceResults, resultKey, expectedResult); 432 } 433 assertTargetResult(String resultKey, String expectedResult)434 private void assertTargetResult(String resultKey, String expectedResult) throws Exception { 435 assertResult(mTargetResults, resultKey, expectedResult); 436 } 437 assertResult(Map<String, String> results, String resultKey, String expectedResult)438 private void assertResult(Map<String, String> results, String resultKey, String expectedResult) 439 throws Exception { 440 if (!supportsDragAndDrop()) { 441 return; 442 } 443 444 if (RESULT_MISSING.equals(expectedResult)) { 445 if (results.containsKey(resultKey)) { 446 fail("Unexpected " + resultKey + "=" + results.get(resultKey)); 447 } 448 } else { 449 assertTrue("Missing " + resultKey, results.containsKey(resultKey)); 450 assertEquals(resultKey + " result mismatch,", expectedResult, 451 results.get(resultKey)); 452 } 453 } 454 supportsDragAndDrop()455 private boolean supportsDragAndDrop() throws Exception { 456 String supportsMultiwindow = mDevice.executeShellCommand("am supports-multiwindow").trim(); 457 if ("true".equals(supportsMultiwindow)) { 458 return true; 459 } else if ("false".equals(supportsMultiwindow)) { 460 return false; 461 } else { 462 throw new Exception( 463 "device does not support \"am supports-multiwindow\" shell command."); 464 } 465 } 466 supportsSplitScreenMultiWindow()467 private boolean supportsSplitScreenMultiWindow() throws DeviceNotAvailableException { 468 return !executeShellCommand(AM_SUPPORTS_SPLIT_SCREEN_MULTIWINDOW).startsWith("false"); 469 } 470 supportsFreeformMultiWindow()471 private boolean supportsFreeformMultiWindow() throws DeviceNotAvailableException { 472 return mDevice.hasFeature("feature:android.software.freeform_window_management"); 473 } 474 testCancelSoon()475 public void testCancelSoon() throws Exception { 476 assertDropResult(CANCEL_SOON, REQUEST_NONE, RESULT_MISSING); 477 } 478 testDisallowGlobal()479 public void testDisallowGlobal() throws Exception { 480 assertNoGlobalDragEvents(DISALLOW_GLOBAL, RESULT_OK); 481 } 482 testDisallowGlobalBelowSdk24()483 public void testDisallowGlobalBelowSdk24() throws Exception { 484 mTargetPackageName = TARGET_23_PACKAGE_NAME; 485 assertNoGlobalDragEvents(GRANT_NONE, RESULT_OK); 486 } 487 testFileUriLocal()488 public void testFileUriLocal() throws Exception { 489 assertNoGlobalDragEvents(FILE_LOCAL, RESULT_OK); 490 } 491 testFileUriGlobal()492 public void testFileUriGlobal() throws Exception { 493 assertNoGlobalDragEvents(FILE_GLOBAL, RESULT_EXCEPTION); 494 } 495 testGrantNoneRequestNone()496 public void testGrantNoneRequestNone() throws Exception { 497 assertDropResult(GRANT_NONE, REQUEST_NONE, RESULT_EXCEPTION); 498 } 499 testGrantNoneRequestRead()500 public void testGrantNoneRequestRead() throws Exception { 501 assertDropResult(GRANT_NONE, REQUEST_READ, RESULT_NULL_DROP_PERMISSIONS); 502 } 503 testGrantNoneRequestWrite()504 public void testGrantNoneRequestWrite() throws Exception { 505 assertDropResult(GRANT_NONE, REQUEST_WRITE, RESULT_NULL_DROP_PERMISSIONS); 506 } 507 testGrantReadRequestNone()508 public void testGrantReadRequestNone() throws Exception { 509 assertDropResult(GRANT_READ, REQUEST_NONE, RESULT_EXCEPTION); 510 } 511 testGrantReadRequestRead()512 public void testGrantReadRequestRead() throws Exception { 513 assertDropResult(GRANT_READ, REQUEST_READ, RESULT_OK); 514 } 515 testGrantReadRequestWrite()516 public void testGrantReadRequestWrite() throws Exception { 517 assertDropResult(GRANT_READ, REQUEST_WRITE, RESULT_EXCEPTION); 518 } 519 testGrantReadNoPrefixRequestReadNested()520 public void testGrantReadNoPrefixRequestReadNested() throws Exception { 521 assertDropResult(GRANT_READ_NOPREFIX, REQUEST_READ_NESTED, RESULT_EXCEPTION); 522 } 523 testGrantReadPrefixRequestReadNested()524 public void testGrantReadPrefixRequestReadNested() throws Exception { 525 assertDropResult(GRANT_READ_PREFIX, REQUEST_READ_NESTED, RESULT_OK); 526 } 527 testGrantPersistableRequestTakePersistable()528 public void testGrantPersistableRequestTakePersistable() throws Exception { 529 assertDropResult(GRANT_READ_PERSISTABLE, REQUEST_TAKE_PERSISTABLE, RESULT_OK); 530 } 531 testGrantReadRequestTakePersistable()532 public void testGrantReadRequestTakePersistable() throws Exception { 533 assertDropResult(GRANT_READ, REQUEST_TAKE_PERSISTABLE, RESULT_EXCEPTION); 534 } 535 testGrantWriteRequestNone()536 public void testGrantWriteRequestNone() throws Exception { 537 assertDropResult(GRANT_WRITE, REQUEST_NONE, RESULT_EXCEPTION); 538 } 539 testGrantWriteRequestRead()540 public void testGrantWriteRequestRead() throws Exception { 541 assertDropResult(GRANT_WRITE, REQUEST_READ, RESULT_EXCEPTION); 542 } 543 testGrantWriteRequestWrite()544 public void testGrantWriteRequestWrite() throws Exception { 545 assertDropResult(GRANT_WRITE, REQUEST_WRITE, RESULT_OK); 546 } 547 } 548