1 /* 2 * Copyright (C) 2010 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 com.android.common.widget; 18 19 import static com.android.common.widget.GroupingListAdapter.ITEM_TYPE_GROUP_HEADER; 20 import static com.android.common.widget.GroupingListAdapter.ITEM_TYPE_IN_GROUP; 21 import static com.android.common.widget.GroupingListAdapter.ITEM_TYPE_STANDALONE; 22 23 import android.content.Context; 24 import android.database.Cursor; 25 import android.database.MatrixCursor; 26 import android.test.AndroidTestCase; 27 import android.text.TextUtils; 28 import android.view.View; 29 import android.view.ViewGroup; 30 31 /** 32 * Tests for {@link GroupingListAdapter}. 33 * 34 * Running all tests: 35 * 36 * adb shell am instrument -e class com.android.common.widget.GroupingListAdapterTests \ 37 * -w com.android.common.tests/android.test.InstrumentationTestRunner 38 */ 39 public class GroupingListAdapterTests extends AndroidTestCase { 40 41 static private final String[] PROJECTION = new String[] { 42 "_id", 43 "group", 44 }; 45 46 private static final int GROUPING_COLUMN_INDEX = 1; 47 48 private MatrixCursor mCursor; 49 private long mNextId; 50 51 private GroupingListAdapter mAdapter = new GroupingListAdapter(null) { 52 53 @Override 54 protected void addGroups(Cursor cursor) { 55 int count = cursor.getCount(); 56 int groupItemCount = 1; 57 cursor.moveToFirst(); 58 String currentValue = cursor.getString(GROUPING_COLUMN_INDEX); 59 for (int i = 1; i < count; i++) { 60 cursor.moveToNext(); 61 String value = cursor.getString(GROUPING_COLUMN_INDEX); 62 if (TextUtils.equals(value, currentValue)) { 63 groupItemCount++; 64 } else { 65 if (groupItemCount > 1) { 66 addGroup(i - groupItemCount, groupItemCount, false); 67 } 68 69 groupItemCount = 1; 70 currentValue = value; 71 } 72 } 73 if (groupItemCount > 1) { 74 addGroup(count - groupItemCount, groupItemCount, false); 75 } 76 } 77 78 @Override 79 protected void bindChildView(View view, Context context, Cursor cursor) { 80 } 81 82 @Override 83 protected void bindGroupView(View view, Context context, Cursor cursor, int groupSize, 84 boolean expanded) { 85 } 86 87 @Override 88 protected void bindStandAloneView(View view, Context context, Cursor cursor) { 89 } 90 91 @Override 92 protected View newChildView(Context context, ViewGroup parent) { 93 return null; 94 } 95 96 @Override 97 protected View newGroupView(Context context, ViewGroup parent) { 98 return null; 99 } 100 101 @Override 102 protected View newStandAloneView(Context context, ViewGroup parent) { 103 return null; 104 } 105 }; 106 107 private void buildCursor(String... numbers) { 108 mCursor = new MatrixCursor(PROJECTION); 109 mNextId = 1; 110 for (String number : numbers) { 111 mCursor.addRow(new Object[]{mNextId, number}); 112 mNextId++; 113 } 114 } 115 116 public void testGroupingWithoutGroups() { 117 buildCursor("1", "2", "3"); 118 mAdapter.changeCursor(mCursor); 119 120 assertEquals(3, mAdapter.getCount()); 121 assertPositionMetadata(0, ITEM_TYPE_STANDALONE, false, 0); 122 assertPositionMetadata(1, ITEM_TYPE_STANDALONE, false, 1); 123 assertPositionMetadata(2, ITEM_TYPE_STANDALONE, false, 2); 124 } 125 126 public void testGroupingWithCollapsedGroupAtTheBeginning() { 127 buildCursor("1", "1", "2"); 128 mAdapter.changeCursor(mCursor); 129 130 assertEquals(2, mAdapter.getCount()); 131 assertPositionMetadata(0, ITEM_TYPE_GROUP_HEADER, false, 0); 132 assertPositionMetadata(1, ITEM_TYPE_STANDALONE, false, 2); 133 } 134 135 public void testGroupingWithExpandedGroupAtTheBeginning() { 136 buildCursor("1", "1", "2"); 137 mAdapter.changeCursor(mCursor); 138 mAdapter.toggleGroup(0); 139 140 assertEquals(4, mAdapter.getCount()); 141 assertPositionMetadata(0, ITEM_TYPE_GROUP_HEADER, true, 0); 142 assertPositionMetadata(1, ITEM_TYPE_IN_GROUP, false, 0); 143 assertPositionMetadata(2, ITEM_TYPE_IN_GROUP, false, 1); 144 assertPositionMetadata(3, ITEM_TYPE_STANDALONE, false, 2); 145 } 146 147 public void testGroupingWithExpandCollapseCycleAtTheBeginning() { 148 buildCursor("1", "1", "2"); 149 mAdapter.changeCursor(mCursor); 150 mAdapter.toggleGroup(0); 151 mAdapter.toggleGroup(0); 152 153 assertEquals(2, mAdapter.getCount()); 154 assertPositionMetadata(0, ITEM_TYPE_GROUP_HEADER, false, 0); 155 assertPositionMetadata(1, ITEM_TYPE_STANDALONE, false, 2); 156 } 157 158 public void testGroupingWithCollapsedGroupInTheMiddle() { 159 buildCursor("1", "2", "2", "2", "3"); 160 mAdapter.changeCursor(mCursor); 161 162 assertEquals(3, mAdapter.getCount()); 163 assertPositionMetadata(0, ITEM_TYPE_STANDALONE, false, 0); 164 assertPositionMetadata(1, ITEM_TYPE_GROUP_HEADER, false, 1); 165 assertPositionMetadata(2, ITEM_TYPE_STANDALONE, false, 4); 166 } 167 168 public void testGroupingWithExpandedGroupInTheMiddle() { 169 buildCursor("1", "2", "2", "2", "3"); 170 mAdapter.changeCursor(mCursor); 171 mAdapter.toggleGroup(1); 172 173 assertEquals(6, mAdapter.getCount()); 174 assertPositionMetadata(0, ITEM_TYPE_STANDALONE, false, 0); 175 assertPositionMetadata(1, ITEM_TYPE_GROUP_HEADER, true, 1); 176 assertPositionMetadata(2, ITEM_TYPE_IN_GROUP, false, 1); 177 assertPositionMetadata(3, ITEM_TYPE_IN_GROUP, false, 2); 178 assertPositionMetadata(4, ITEM_TYPE_IN_GROUP, false, 3); 179 assertPositionMetadata(5, ITEM_TYPE_STANDALONE, false, 4); 180 } 181 182 public void testGroupingWithCollapsedGroupAtTheEnd() { 183 buildCursor("1", "2", "3", "3", "3"); 184 mAdapter.changeCursor(mCursor); 185 186 assertEquals(3, mAdapter.getCount()); 187 assertPositionMetadata(0, ITEM_TYPE_STANDALONE, false, 0); 188 assertPositionMetadata(1, ITEM_TYPE_STANDALONE, false, 1); 189 assertPositionMetadata(2, ITEM_TYPE_GROUP_HEADER, false, 2); 190 } 191 192 public void testGroupingWithExpandedGroupAtTheEnd() { 193 buildCursor("1", "2", "3", "3", "3"); 194 mAdapter.changeCursor(mCursor); 195 mAdapter.toggleGroup(2); 196 197 assertEquals(6, mAdapter.getCount()); 198 assertPositionMetadata(0, ITEM_TYPE_STANDALONE, false, 0); 199 assertPositionMetadata(1, ITEM_TYPE_STANDALONE, false, 1); 200 assertPositionMetadata(2, ITEM_TYPE_GROUP_HEADER, true, 2); 201 assertPositionMetadata(3, ITEM_TYPE_IN_GROUP, false, 2); 202 assertPositionMetadata(4, ITEM_TYPE_IN_GROUP, false, 3); 203 assertPositionMetadata(5, ITEM_TYPE_IN_GROUP, false, 4); 204 } 205 206 public void testGroupingWithMultipleCollapsedGroups() { 207 buildCursor("1", "2", "2", "3", "4", "4", "5", "5", "6"); 208 mAdapter.changeCursor(mCursor); 209 210 assertEquals(6, mAdapter.getCount()); 211 assertPositionMetadata(0, ITEM_TYPE_STANDALONE, false, 0); 212 assertPositionMetadata(1, ITEM_TYPE_GROUP_HEADER, false, 1); 213 assertPositionMetadata(2, ITEM_TYPE_STANDALONE, false, 3); 214 assertPositionMetadata(3, ITEM_TYPE_GROUP_HEADER, false, 4); 215 assertPositionMetadata(4, ITEM_TYPE_GROUP_HEADER, false, 6); 216 assertPositionMetadata(5, ITEM_TYPE_STANDALONE, false, 8); 217 } 218 219 public void testGroupingWithMultipleExpandedGroups() { 220 buildCursor("1", "2", "2", "3", "4", "4", "5", "5", "6"); 221 mAdapter.changeCursor(mCursor); 222 mAdapter.toggleGroup(1); 223 224 // Note that expanding the group of 2's shifted the group of 5's down from the 225 // 4th to the 6th position 226 mAdapter.toggleGroup(6); 227 228 assertEquals(10, mAdapter.getCount()); 229 assertPositionMetadata(0, ITEM_TYPE_STANDALONE, false, 0); 230 assertPositionMetadata(1, ITEM_TYPE_GROUP_HEADER, true, 1); 231 assertPositionMetadata(2, ITEM_TYPE_IN_GROUP, false, 1); 232 assertPositionMetadata(3, ITEM_TYPE_IN_GROUP, false, 2); 233 assertPositionMetadata(4, ITEM_TYPE_STANDALONE, false, 3); 234 assertPositionMetadata(5, ITEM_TYPE_GROUP_HEADER, false, 4); 235 assertPositionMetadata(6, ITEM_TYPE_GROUP_HEADER, true, 6); 236 assertPositionMetadata(7, ITEM_TYPE_IN_GROUP, false, 6); 237 assertPositionMetadata(8, ITEM_TYPE_IN_GROUP, false, 7); 238 assertPositionMetadata(9, ITEM_TYPE_STANDALONE, false, 8); 239 } 240 241 public void testPositionCache() { 242 buildCursor("1", "2", "2", "3", "4", "4", "5", "5", "6"); 243 mAdapter.changeCursor(mCursor); 244 245 // First pass - building up cache 246 assertEquals(6, mAdapter.getCount()); 247 assertPositionMetadata(0, ITEM_TYPE_STANDALONE, false, 0); 248 assertPositionMetadata(1, ITEM_TYPE_GROUP_HEADER, false, 1); 249 assertPositionMetadata(2, ITEM_TYPE_STANDALONE, false, 3); 250 assertPositionMetadata(3, ITEM_TYPE_GROUP_HEADER, false, 4); 251 assertPositionMetadata(4, ITEM_TYPE_GROUP_HEADER, false, 6); 252 assertPositionMetadata(5, ITEM_TYPE_STANDALONE, false, 8); 253 254 // Second pass - using cache 255 assertEquals(6, mAdapter.getCount()); 256 assertPositionMetadata(0, ITEM_TYPE_STANDALONE, false, 0); 257 assertPositionMetadata(1, ITEM_TYPE_GROUP_HEADER, false, 1); 258 assertPositionMetadata(2, ITEM_TYPE_STANDALONE, false, 3); 259 assertPositionMetadata(3, ITEM_TYPE_GROUP_HEADER, false, 4); 260 assertPositionMetadata(4, ITEM_TYPE_GROUP_HEADER, false, 6); 261 assertPositionMetadata(5, ITEM_TYPE_STANDALONE, false, 8); 262 263 // Invalidate cache by expanding a group 264 mAdapter.toggleGroup(1); 265 266 // First pass - building up cache 267 assertPositionMetadata(0, ITEM_TYPE_STANDALONE, false, 0); 268 assertPositionMetadata(1, ITEM_TYPE_GROUP_HEADER, true, 1); 269 assertPositionMetadata(2, ITEM_TYPE_IN_GROUP, false, 1); 270 assertPositionMetadata(3, ITEM_TYPE_IN_GROUP, false, 2); 271 assertPositionMetadata(4, ITEM_TYPE_STANDALONE, false, 3); 272 assertPositionMetadata(5, ITEM_TYPE_GROUP_HEADER, false, 4); 273 assertPositionMetadata(6, ITEM_TYPE_GROUP_HEADER, false, 6); 274 assertPositionMetadata(7, ITEM_TYPE_STANDALONE, false, 8); 275 276 // Second pass - using cache 277 assertPositionMetadata(0, ITEM_TYPE_STANDALONE, false, 0); 278 assertPositionMetadata(1, ITEM_TYPE_GROUP_HEADER, true, 1); 279 assertPositionMetadata(2, ITEM_TYPE_IN_GROUP, false, 1); 280 assertPositionMetadata(3, ITEM_TYPE_IN_GROUP, false, 2); 281 assertPositionMetadata(4, ITEM_TYPE_STANDALONE, false, 3); 282 assertPositionMetadata(5, ITEM_TYPE_GROUP_HEADER, false, 4); 283 assertPositionMetadata(6, ITEM_TYPE_GROUP_HEADER, false, 6); 284 assertPositionMetadata(7, ITEM_TYPE_STANDALONE, false, 8); 285 } 286 287 public void testGroupDescriptorArrayGrowth() { 288 String[] numbers = new String[500]; 289 for (int i = 0; i < numbers.length; i++) { 290 291 // Make groups of 2 292 numbers[i] = String.valueOf((i / 2) * 2); 293 } 294 295 buildCursor(numbers); 296 mAdapter.changeCursor(mCursor); 297 298 assertEquals(250, mAdapter.getCount()); 299 } 300 301 private void assertPositionMetadata(int position, int itemType, boolean isExpanded, 302 int cursorPosition) { 303 GroupingListAdapter.PositionMetadata metadata = new GroupingListAdapter.PositionMetadata(); 304 mAdapter.obtainPositionMetadata(metadata, position); 305 assertEquals(itemType, metadata.itemType); 306 if (metadata.itemType == ITEM_TYPE_GROUP_HEADER) { 307 assertEquals(isExpanded, metadata.isExpanded); 308 } 309 assertEquals(cursorPosition, metadata.cursorPosition); 310 } 311 } 312