2 * Copyright (C) 2016 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.settings.dashboard;
19 import android.support.annotation.NonNull;
20 import android.support.v7.util.DiffUtil;
21 import android.support.v7.util.ListUpdateCallback;
22 import com.android.settings.TestConfig;
23 import com.android.settings.dashboard.conditional.AirplaneModeCondition;
24 import com.android.settings.dashboard.conditional.Condition;
25 import com.android.settingslib.drawer.DashboardCategory;
26 import com.android.settingslib.drawer.Tile;
27 import org.junit.Before;
28 import org.junit.Test;
29 import org.junit.runner.RunWith;
30 import org.mockito.Mock;
31 import org.mockito.MockitoAnnotations;
32 import org.robolectric.RobolectricTestRunner;
33 import org.robolectric.annotation.Config;
35 import java.util.ArrayList;
36 import java.util.Collections;
37 import java.util.List;
39 import static com.google.common.truth.Truth.assertThat;
40 import static org.mockito.Mockito.mock;
41 import static org.mockito.Mockito.when;
43 @RunWith(RobolectricTestRunner.class)
44 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
45 public class DashboardDataTest {
46 private static final String TEST_SUGGESTION_TITLE = "Use fingerprint";
47 private static final String TEST_CATEGORY_TILE_TITLE = "Display";
49 private DashboardData mDashboardDataWithOneConditions;
50 private DashboardData mDashboardDataWithTwoConditions;
51 private DashboardData mDashboardDataWithNoItems;
53 private Tile mTestCategoryTile;
55 private Tile mTestSuggestion;
57 private DashboardCategory mDashboardCategory;
59 private Condition mTestCondition;
61 private Condition mSecondCondition; // condition used to test insert in DiffUtil
65 MockitoAnnotations.initMocks(this);
68 final List<Tile> suggestions = new ArrayList<>();
69 mTestSuggestion.title = TEST_SUGGESTION_TITLE;
70 suggestions.add(mTestSuggestion);
72 // Build oneItemConditions
73 final List<Condition> oneItemConditions = new ArrayList<>();
74 when(mTestCondition.shouldShow()).thenReturn(true);
75 oneItemConditions.add(mTestCondition);
77 // Build twoItemConditions
78 final List<Condition> twoItemsConditions = new ArrayList<>();
79 when(mSecondCondition.shouldShow()).thenReturn(true);
80 twoItemsConditions.add(mTestCondition);
81 twoItemsConditions.add(mSecondCondition);
84 final List<DashboardCategory> categories = new ArrayList<>();
85 mTestCategoryTile.title = TEST_CATEGORY_TILE_TITLE;
86 mDashboardCategory.title = "test";
87 mDashboardCategory.tiles = new ArrayList<>();
88 mDashboardCategory.tiles.add(mTestCategoryTile);
89 categories.add(mDashboardCategory);
91 // Build DashboardData
92 mDashboardDataWithOneConditions = new DashboardData.Builder()
93 .setConditions(oneItemConditions)
94 .setCategories(categories)
95 .setSuggestions(suggestions)
96 .setSuggestionConditionMode(DashboardData.HEADER_MODE_FULLY_EXPANDED)
97 .setCombineSuggestionAndCondition(true)
100 mDashboardDataWithTwoConditions = new DashboardData.Builder()
101 .setConditions(twoItemsConditions)
102 .setCategories(categories)
103 .setSuggestions(suggestions)
104 .setSuggestionConditionMode(DashboardData.HEADER_MODE_FULLY_EXPANDED)
105 .setCombineSuggestionAndCondition(true)
108 mDashboardDataWithNoItems = new DashboardData.Builder()
111 .setSuggestions(null)
116 public void testBuildItemsData_containsAllData() {
117 final DashboardData.SuggestionConditionHeaderData data =
118 new DashboardData.SuggestionConditionHeaderData(
119 mDashboardDataWithOneConditions.getConditions(), 0);
120 final Object[] expectedObjects = {data,
121 mDashboardDataWithOneConditions.getSuggestions(),
122 mDashboardDataWithOneConditions.getConditions(),
123 null, mDashboardCategory, mTestCategoryTile};
124 final int expectedSize = expectedObjects.length;
126 assertThat(mDashboardDataWithOneConditions.getItemList().size())
127 .isEqualTo(expectedSize);
128 for (int i = 0; i < expectedSize; i++) {
129 final Object item = mDashboardDataWithOneConditions.getItemEntityByPosition(i);
130 if (item instanceof DashboardData.SuggestionHeaderData
131 || item instanceof List) {
132 // SuggestionHeaderData is created inside when build, we can only use isEqualTo
133 assertThat(item).isEqualTo(expectedObjects[i]);
134 } else if (item instanceof DashboardData.SuggestionConditionHeaderData) {
135 DashboardData.SuggestionConditionHeaderData i1 =
136 (DashboardData.SuggestionConditionHeaderData)item;
137 DashboardData.SuggestionConditionHeaderData i2 =
138 (DashboardData.SuggestionConditionHeaderData)expectedObjects[i];
139 assertThat(i1.title).isEqualTo(i2.title);
140 assertThat(i1.conditionCount).isEqualTo(i2.conditionCount);
141 assertThat(i1.hiddenSuggestionCount).isEqualTo(i2.hiddenSuggestionCount);
143 assertThat(item).isSameAs(expectedObjects[i]);
149 public void testGetPositionByEntity_selfInstance_returnPositionFound() {
150 final int position = mDashboardDataWithOneConditions
151 .getPositionByEntity(mDashboardDataWithOneConditions.getConditions());
152 assertThat(position).isNotEqualTo(DashboardData.POSITION_NOT_FOUND);
156 public void testGetPositionByEntity_notExisted_returnNotFound() {
157 final Condition condition = mock(AirplaneModeCondition.class);
158 final int position = mDashboardDataWithOneConditions.getPositionByEntity(condition);
159 assertThat(position).isEqualTo(DashboardData.POSITION_NOT_FOUND);
163 public void testGetPositionByTile_selfInstance_returnPositionFound() {
164 final int position = mDashboardDataWithOneConditions
165 .getPositionByTile(mTestCategoryTile);
166 assertThat(position).isNotEqualTo(DashboardData.POSITION_NOT_FOUND);
170 public void testGetPositionByTile_equalTitle_returnPositionFound() {
171 final Tile tile = mock(Tile.class);
172 tile.title = TEST_CATEGORY_TILE_TITLE;
173 final int position = mDashboardDataWithOneConditions.getPositionByTile(tile);
174 assertThat(position).isNotEqualTo(DashboardData.POSITION_NOT_FOUND);
178 public void testGetPositionByTile_notExisted_returnNotFound() {
179 final Tile tile = mock(Tile.class);
181 final int position = mDashboardDataWithOneConditions.getPositionByTile(tile);
182 assertThat(position).isEqualTo(DashboardData.POSITION_NOT_FOUND);
186 public void testDiffUtil_DataEqual_noResultData() {
187 List<ListUpdateResult.ResultData> testResultData = new ArrayList<>();
188 testDiffUtil(mDashboardDataWithOneConditions,
189 mDashboardDataWithOneConditions, testResultData);
193 public void testDiffUtil_InsertOneCondition_ResultDataTwoChanged() {
194 //Build testResultData
195 final List<ListUpdateResult.ResultData> testResultData = new ArrayList<>();
196 // Item in position 1 is the header, which contains the number of conditions, changed from
198 testResultData.add(new ListUpdateResult.ResultData(
199 ListUpdateResult.ResultData.TYPE_OPERATION_CHANGE, 0, 1));
200 // Item in position 3 is the condition container containing the list of conditions, which
202 testResultData.add(new ListUpdateResult.ResultData(
203 ListUpdateResult.ResultData.TYPE_OPERATION_CHANGE, 2, 1));
205 testDiffUtil(mDashboardDataWithOneConditions,
206 mDashboardDataWithTwoConditions, testResultData);
210 public void testDiffUtil_DeleteAllData_ResultDataOneDeleted() {
211 //Build testResultData
212 final List<ListUpdateResult.ResultData> testResultData = new ArrayList<>();
213 testResultData.add(new ListUpdateResult.ResultData(
214 ListUpdateResult.ResultData.TYPE_OPERATION_REMOVE, 0, 6));
216 testDiffUtil(mDashboardDataWithOneConditions, mDashboardDataWithNoItems, testResultData);
220 * Test when using the
221 * {@link com.android.settings.dashboard.DashboardData.ItemsDataDiffCallback}
222 * to transfer List from {@paramref baseDashboardData} to {@paramref diffDashboardData}, whether
223 * the transform data result is equals to {@paramref testResultData}
225 * The steps are described below:
226 * 1. Calculate a {@link android.support.v7.util.DiffUtil.DiffResult} from
227 * {@paramref baseDashboardData} to {@paramref diffDashboardData}
229 * 2. Dispatch the {@link android.support.v7.util.DiffUtil.DiffResult} calculated from step 1
230 * into {@link ListUpdateResult}
232 * 3. Get result data(a.k.a. baseResultData) from {@link ListUpdateResult} and compare it to
233 * {@paramref testResultData}
235 * Because baseResultData and {@paramref testResultData} don't have sequence. When do the
236 * comparison, we will sort them first and then compare the inside data from them one by one.
238 * @param baseDashboardData
239 * @param diffDashboardData
240 * @param testResultData
242 private void testDiffUtil(DashboardData baseDashboardData, DashboardData diffDashboardData,
243 List<ListUpdateResult.ResultData> testResultData) {
244 final DiffUtil.DiffResult diffUtilResult = DiffUtil.calculateDiff(
245 new DashboardData.ItemsDataDiffCallback(
246 baseDashboardData.getItemList(), diffDashboardData.getItemList()));
248 // Dispatch to listUpdateResult, then listUpdateResult will have result data
249 final ListUpdateResult listUpdateResult = new ListUpdateResult();
250 diffUtilResult.dispatchUpdatesTo(listUpdateResult);
252 final List<ListUpdateResult.ResultData> baseResultData = listUpdateResult.getResultData();
253 assertThat(testResultData.size()).isEqualTo(baseResultData.size());
255 // Sort them so we can compare them one by one using a for loop
256 Collections.sort(baseResultData);
257 Collections.sort(testResultData);
258 final int size = baseResultData.size();
259 for (int i = 0; i < size; i++) {
260 // Refer to equals method in ResultData
261 assertThat(baseResultData.get(i)).isEqualTo(testResultData.get(i));
266 * This class contains the result about how the changes made to convert one
267 * list to another list. It implements ListUpdateCallback to record the result data.
269 private static class ListUpdateResult implements ListUpdateCallback {
270 final private List<ResultData> mResultData;
272 public ListUpdateResult() {
273 mResultData = new ArrayList<>();
276 public List<ResultData> getResultData() {
281 public void onInserted(int position, int count) {
282 mResultData.add(new ResultData(ResultData.TYPE_OPERATION_INSERT, position, count));
286 public void onRemoved(int position, int count) {
287 mResultData.add(new ResultData(ResultData.TYPE_OPERATION_REMOVE, position, count));
291 public void onMoved(int fromPosition, int toPosition) {
293 new ResultData(ResultData.TYPE_OPERATION_MOVE, fromPosition, toPosition));
297 public void onChanged(int position, int count, Object payload) {
298 mResultData.add(new ResultData(ResultData.TYPE_OPERATION_CHANGE, position, count));
302 * This class contains general type and field to record the operation data generated
303 * in {@link ListUpdateCallback}. Please refer to {@link ListUpdateCallback} for more info.
305 * The following are examples about the data stored in this class:
307 * "The data starts from position(arg1) with count number(arg2) is changed(operation)"
308 * or "The data is moved(operation) from position1(arg1) to position2(arg2)"
310 private static class ResultData implements Comparable<ResultData> {
311 public static final int TYPE_OPERATION_INSERT = 0;
312 public static final int TYPE_OPERATION_REMOVE = 1;
313 public static final int TYPE_OPERATION_MOVE = 2;
314 public static final int TYPE_OPERATION_CHANGE = 3;
316 public final int operation;
317 public final int arg1;
318 public final int arg2;
320 public ResultData(int operation, int arg1, int arg2) {
321 this.operation = operation;
327 public boolean equals(Object obj) {
332 if (!(obj instanceof ResultData)) {
336 ResultData targetData = (ResultData) obj;
338 return operation == targetData.operation && arg1 == targetData.arg1
339 && arg2 == targetData.arg2;
343 public int compareTo(@NonNull ResultData resultData) {
344 if (this.operation != resultData.operation) {
345 return operation - resultData.operation;
348 if (arg1 != resultData.arg1) {
349 return arg1 - resultData.arg1;
352 return arg2 - resultData.arg2;
356 public String toString() {
357 return "op:" + operation + ",arg1:" + arg1 + ",arg2:" + arg2;