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