1/* 2 * Copyright (C) 2015 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 17package com.android.documentsui.dirlist; 18 19import android.support.v7.widget.RecyclerView; 20import android.test.AndroidTestCase; 21import android.test.suitebuilder.annotation.SmallTest; 22import android.util.SparseBooleanArray; 23 24import com.android.documentsui.TestInputEvent; 25import com.android.documentsui.dirlist.MultiSelectManager.Selection; 26import com.google.common.collect.Lists; 27 28import java.util.ArrayList; 29import java.util.HashSet; 30import java.util.List; 31import java.util.Set; 32 33@SmallTest 34public class MultiSelectManagerTest extends AndroidTestCase { 35 36 private static final List<String> items; 37 static { 38 items = new ArrayList<String>(); 39 for (int i = 0; i < 100; ++i) { 40 items.add(Integer.toString(i)); 41 } 42 } 43 44 private MultiSelectManager mManager; 45 private TestCallback mCallback; 46 private TestSelectionEnvironment mEnv; 47 private TestDocumentsAdapter mAdapter; 48 49 public void setUp() throws Exception { 50 mCallback = new TestCallback(); 51 mEnv = new TestSelectionEnvironment(items); 52 mAdapter = new TestDocumentsAdapter(items); 53 mManager = new MultiSelectManager(mEnv, mAdapter, MultiSelectManager.MODE_MULTIPLE, null); 54 mManager.addCallback(mCallback); 55 } 56 57 public void testSelection() { 58 // Check selection. 59 mManager.toggleSelection(items.get(7)); 60 assertSelection(items.get(7)); 61 // Check deselection. 62 mManager.toggleSelection(items.get(7)); 63 assertSelectionSize(0); 64 } 65 66 public void testSelection_NotifiesSelectionChanged() { 67 // Selection should notify. 68 mManager.toggleSelection(items.get(7)); 69 mCallback.assertSelectionChanged(); 70 // Deselection should notify. 71 mManager.toggleSelection(items.get(7)); 72 mCallback.assertSelectionChanged(); 73 } 74 75 public void testMouseClick_ShiftClickExtendsSelection() { 76 longPress(7); 77 shiftClick(11); 78 assertRangeSelection(7, 11); 79 } 80 81 public void testMouseClick_NoPosition_ClearsSelection() { 82 longPress(7); 83 click(11); 84 click(RecyclerView.NO_POSITION); 85 assertSelection(); 86 } 87 88 public void testSetSelectionFocusBegin() { 89 mManager.setItemsSelected(Lists.newArrayList(items.get(7)), true); 90 mManager.setSelectionRangeBegin(7); 91 shiftClick(11); 92 assertRangeSelection(7, 11); 93 } 94 95 public void testLongPress_StartsSelectionMode() { 96 longPress(7); 97 assertSelection(items.get(7)); 98 } 99 100 public void testLongPress_SecondPressExtendsSelection() { 101 longPress(7); 102 longPress(99); 103 assertSelection(items.get(7), items.get(99)); 104 } 105 106 public void testSingleTapUp_UnselectsSelectedItem() { 107 longPress(7); 108 tap(7); 109 assertSelection(); 110 } 111 112 public void testSingleTapUp_NoPosition_ClearsSelection() { 113 longPress(7); 114 tap(11); 115 tap(RecyclerView.NO_POSITION); 116 assertSelection(); 117 } 118 119 public void testSingleTapUp_ExtendsSelection() { 120 longPress(99); 121 tap(7); 122 tap(13); 123 assertSelection(items.get(7), items.get(99), items.get(13)); 124 } 125 126 public void testSingleTapUp_ShiftCreatesRangeSelection() { 127 longPress(7); 128 shiftTap(17); 129 assertRangeSelection(7, 17); 130 } 131 132 public void testSingleTapUp_ShiftCreatesRangeSeletion_Backwards() { 133 longPress(17); 134 shiftTap(7); 135 assertRangeSelection(7, 17); 136 } 137 138 public void testSingleTapUp_SecondShiftClickExtendsSelection() { 139 longPress(7); 140 shiftTap(11); 141 shiftTap(17); 142 assertRangeSelection(7, 17); 143 } 144 145 public void testSingleTapUp_MultipleContiguousRangesSelected() { 146 longPress(7); 147 shiftTap(11); 148 tap(20); 149 shiftTap(25); 150 assertRangeSelected(7, 11); 151 assertRangeSelected(20, 25); 152 assertSelectionSize(11); 153 } 154 155 public void testSingleTapUp_ShiftReducesSelectionRange_FromPreviousShiftClick() { 156 longPress(7); 157 shiftTap(17); 158 shiftTap(10); 159 assertRangeSelection(7, 10); 160 } 161 162 public void testSingleTapUp_ShiftReducesSelectionRange_FromPreviousShiftClick_Backwards() { 163 mManager.onLongPress(TestInputEvent.tap(17)); 164 shiftTap(7); 165 shiftTap(14); 166 assertRangeSelection(14, 17); 167 } 168 169 public void testSingleTapUp_ShiftReversesSelectionDirection() { 170 longPress(7); 171 shiftTap(17); 172 shiftTap(0); 173 assertRangeSelection(0, 7); 174 } 175 176 public void testSingleSelectMode() { 177 mManager = new MultiSelectManager(mEnv, mAdapter, MultiSelectManager.MODE_SINGLE, null); 178 mManager.addCallback(mCallback); 179 longPress(20); 180 tap(13); 181 assertSelection(items.get(13)); 182 } 183 184 public void testSingleSelectMode_ShiftTap() { 185 mManager = new MultiSelectManager(mEnv, mAdapter, MultiSelectManager.MODE_SINGLE, null); 186 mManager.addCallback(mCallback); 187 longPress(13); 188 shiftTap(20); 189 assertSelection(items.get(20)); 190 } 191 192 public void testRangeSelection() { 193 mManager.startRangeSelection(15); 194 mManager.snapRangeSelection(19); 195 assertRangeSelection(15, 19); 196 } 197 198 public void testRangeSelection_snapExpand() { 199 mManager.startRangeSelection(15); 200 mManager.snapRangeSelection(19); 201 mManager.snapRangeSelection(27); 202 assertRangeSelection(15, 27); 203 } 204 205 public void testRangeSelection_snapContract() { 206 mManager.startRangeSelection(15); 207 mManager.snapRangeSelection(27); 208 mManager.snapRangeSelection(19); 209 assertRangeSelection(15, 19); 210 } 211 212 public void testRangeSelection_snapInvert() { 213 mManager.startRangeSelection(15); 214 mManager.snapRangeSelection(27); 215 mManager.snapRangeSelection(3); 216 assertRangeSelection(3, 15); 217 } 218 219 public void testRangeSelection_multiple() { 220 mManager.startRangeSelection(15); 221 mManager.snapRangeSelection(27); 222 mManager.endRangeSelection(); 223 mManager.startRangeSelection(42); 224 mManager.snapRangeSelection(57); 225 assertSelectionSize(29); 226 assertRangeSelected(15, 27); 227 assertRangeSelected(42, 57); 228 229 } 230 231 public void testRangeSelection_singleSelect() { 232 mManager = new MultiSelectManager(mEnv, mAdapter, MultiSelectManager.MODE_SINGLE, null); 233 mManager.addCallback(mCallback); 234 mManager.startRangeSelection(11); 235 mManager.snapRangeSelection(19); 236 assertSelectionSize(1); 237 assertSelection(items.get(19)); 238 } 239 240 public void testProvisionalSelection() { 241 Selection s = mManager.getSelection(); 242 assertSelection(); 243 244 SparseBooleanArray provisional = new SparseBooleanArray(); 245 provisional.append(1, true); 246 provisional.append(2, true); 247 s.setProvisionalSelection(getItemIds(provisional)); 248 assertSelection(items.get(1), items.get(2)); 249 } 250 251 public void testProvisionalSelection_Replace() { 252 Selection s = mManager.getSelection(); 253 254 SparseBooleanArray provisional = new SparseBooleanArray(); 255 provisional.append(1, true); 256 provisional.append(2, true); 257 s.setProvisionalSelection(getItemIds(provisional)); 258 259 provisional.clear(); 260 provisional.append(3, true); 261 provisional.append(4, true); 262 s.setProvisionalSelection(getItemIds(provisional)); 263 assertSelection(items.get(3), items.get(4)); 264 } 265 266 public void testProvisionalSelection_IntersectsExistingProvisionalSelection() { 267 Selection s = mManager.getSelection(); 268 269 SparseBooleanArray provisional = new SparseBooleanArray(); 270 provisional.append(1, true); 271 provisional.append(2, true); 272 s.setProvisionalSelection(getItemIds(provisional)); 273 274 provisional.clear(); 275 provisional.append(1, true); 276 s.setProvisionalSelection(getItemIds(provisional)); 277 assertSelection(items.get(1)); 278 } 279 280 public void testProvisionalSelection_Apply() { 281 Selection s = mManager.getSelection(); 282 283 SparseBooleanArray provisional = new SparseBooleanArray(); 284 provisional.append(1, true); 285 provisional.append(2, true); 286 s.setProvisionalSelection(getItemIds(provisional)); 287 s.applyProvisionalSelection(); 288 assertSelection(items.get(1), items.get(2)); 289 } 290 291 public void testProvisionalSelection_Cancel() { 292 mManager.toggleSelection(items.get(1)); 293 mManager.toggleSelection(items.get(2)); 294 Selection s = mManager.getSelection(); 295 296 SparseBooleanArray provisional = new SparseBooleanArray(); 297 provisional.append(3, true); 298 provisional.append(4, true); 299 s.setProvisionalSelection(getItemIds(provisional)); 300 s.cancelProvisionalSelection(); 301 302 // Original selection should remain. 303 assertSelection(items.get(1), items.get(2)); 304 } 305 306 public void testProvisionalSelection_IntersectsAppliedSelection() { 307 mManager.toggleSelection(items.get(1)); 308 mManager.toggleSelection(items.get(2)); 309 Selection s = mManager.getSelection(); 310 311 SparseBooleanArray provisional = new SparseBooleanArray(); 312 provisional.append(2, true); 313 provisional.append(3, true); 314 s.setProvisionalSelection(getItemIds(provisional)); 315 assertSelection(items.get(1), items.get(2), items.get(3)); 316 } 317 318 private static Set<String> getItemIds(SparseBooleanArray selection) { 319 Set<String> ids = new HashSet<>(); 320 321 int count = selection.size(); 322 for (int i = 0; i < count; ++i) { 323 ids.add(items.get(selection.keyAt(i))); 324 } 325 326 return ids; 327 } 328 329 private void longPress(int position) { 330 mManager.onLongPress(TestInputEvent.tap(position)); 331 } 332 333 private void tap(int position) { 334 mManager.onSingleTapUp(TestInputEvent.tap(position)); 335 } 336 337 private void shiftTap(int position) { 338 mManager.onSingleTapUp(TestInputEvent.shiftTap(position)); 339 } 340 341 private void click(int position) { 342 mManager.onSingleTapUp(TestInputEvent.click(position)); 343 } 344 345 private void shiftClick(int position) { 346 mManager.onSingleTapUp(TestInputEvent.shiftClick(position)); 347 } 348 349 private void assertSelected(String... expected) { 350 for (int i = 0; i < expected.length; i++) { 351 Selection selection = mManager.getSelection(); 352 String err = String.format( 353 "Selection %s does not contain %s", selection, expected[i]); 354 assertTrue(err, selection.contains(expected[i])); 355 } 356 } 357 358 private void assertSelection(String... expected) { 359 assertSelectionSize(expected.length); 360 assertSelected(expected); 361 } 362 363 private void assertRangeSelected(int begin, int end) { 364 for (int i = begin; i <= end; i++) { 365 assertSelected(items.get(i)); 366 } 367 } 368 369 private void assertRangeSelection(int begin, int end) { 370 assertSelectionSize(end - begin + 1); 371 assertRangeSelected(begin, end); 372 } 373 374 private void assertSelectionSize(int expected) { 375 Selection selection = mManager.getSelection(); 376 assertEquals(selection.toString(), expected, selection.size()); 377 } 378 379 private static final class TestCallback implements MultiSelectManager.Callback { 380 381 Set<String> ignored = new HashSet<>(); 382 private boolean mSelectionChanged = false; 383 384 @Override 385 public void onItemStateChanged(String modelId, boolean selected) {} 386 387 @Override 388 public boolean onBeforeItemStateChange(String modelId, boolean selected) { 389 return !ignored.contains(modelId); 390 } 391 392 @Override 393 public void onSelectionChanged() { 394 mSelectionChanged = true; 395 } 396 397 void assertSelectionChanged() { 398 assertTrue(mSelectionChanged); 399 } 400 } 401} 402