blob: cc4f7417902178a70c5daae7c5a1b63b77ac0dcd [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.dashboard;
import static com.android.settings.dashboard.DashboardData.STABLE_ID_CONDITION_CONTAINER;
import static com.android.settings.dashboard.DashboardData.STABLE_ID_SUGGESTION_CONDITION_FOOTER;
import static com.android.settings.dashboard.DashboardData.STABLE_ID_SUGGESTION_CONTAINER;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.support.annotation.NonNull;
import android.support.v7.util.DiffUtil;
import android.support.v7.util.ListUpdateCallback;
import android.widget.RemoteViews;
import com.android.settings.TestConfig;
import com.android.settings.dashboard.conditional.AirplaneModeCondition;
import com.android.settings.dashboard.conditional.Condition;
import com.android.settingslib.drawer.DashboardCategory;
import com.android.settingslib.drawer.Tile;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
@RunWith(RobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class DashboardDataTest {
private static final String TEST_SUGGESTION_TITLE = "Use fingerprint";
private static final String TEST_CATEGORY_TILE_TITLE = "Display";
private DashboardData mDashboardDataWithOneConditions;
private DashboardData mDashboardDataWithTwoConditions;
private DashboardData mDashboardDataWithNoItems;
@Mock
private Tile mTestCategoryTile;
@Mock
private Tile mTestSuggestion;
@Mock
private DashboardCategory mDashboardCategory;
@Mock
private Condition mTestCondition;
@Mock
private Condition mSecondCondition; // condition used to test insert in DiffUtil
@Before
public void SetUp() {
MockitoAnnotations.initMocks(this);
// Build suggestions
final List<Tile> suggestions = new ArrayList<>();
mTestSuggestion.title = TEST_SUGGESTION_TITLE;
suggestions.add(mTestSuggestion);
// Build oneItemConditions
final List<Condition> oneItemConditions = new ArrayList<>();
when(mTestCondition.shouldShow()).thenReturn(true);
oneItemConditions.add(mTestCondition);
// Build twoItemConditions
final List<Condition> twoItemsConditions = new ArrayList<>();
when(mSecondCondition.shouldShow()).thenReturn(true);
twoItemsConditions.add(mTestCondition);
twoItemsConditions.add(mSecondCondition);
// Build category
mTestCategoryTile.title = TEST_CATEGORY_TILE_TITLE;
mDashboardCategory.title = "test";
mDashboardCategory.tiles = new ArrayList<>();
mDashboardCategory.tiles.add(mTestCategoryTile);
// Build DashboardData
mDashboardDataWithOneConditions = new DashboardData.Builder()
.setConditions(oneItemConditions)
.setCategory(mDashboardCategory)
.setSuggestions(suggestions)
.setSuggestionConditionMode(DashboardData.HEADER_MODE_FULLY_EXPANDED)
.build();
mDashboardDataWithTwoConditions = new DashboardData.Builder()
.setConditions(twoItemsConditions)
.setCategory(mDashboardCategory)
.setSuggestions(suggestions)
.setSuggestionConditionMode(DashboardData.HEADER_MODE_FULLY_EXPANDED)
.build();
mDashboardDataWithNoItems = new DashboardData.Builder()
.setConditions(null)
.setCategory(null)
.setSuggestions(null)
.build();
}
@Test
public void testBuildItemsData_shouldSetstableId() {
final List<DashboardData.Item> items = mDashboardDataWithOneConditions.getItemList();
// Header, suggestion, condition, footer, 1 tile
assertThat(items).hasSize(4);
assertThat(items.get(0).id).isEqualTo(STABLE_ID_SUGGESTION_CONTAINER);
assertThat(items.get(1).id).isEqualTo(STABLE_ID_CONDITION_CONTAINER);
assertThat(items.get(2).id).isEqualTo(STABLE_ID_SUGGESTION_CONDITION_FOOTER);
assertThat(items.get(3).id).isEqualTo(Objects.hash(mTestCategoryTile.title));
}
@Test
public void testBuildItemsData_containsAllData() {
final Object[] expectedObjects = {
mDashboardDataWithOneConditions.getSuggestions(),
mDashboardDataWithOneConditions.getConditions(),
null, mTestCategoryTile};
final int expectedSize = expectedObjects.length;
assertThat(mDashboardDataWithOneConditions.getItemList()).hasSize(expectedSize);
for (int i = 0; i < expectedSize; i++) {
final Object item = mDashboardDataWithOneConditions.getItemEntityByPosition(i);
if (item instanceof List) {
assertThat(item).isEqualTo(expectedObjects[i]);
} else if (item instanceof DashboardData.SuggestionConditionHeaderData) {
DashboardData.SuggestionConditionHeaderData i1 =
(DashboardData.SuggestionConditionHeaderData) item;
DashboardData.SuggestionConditionHeaderData i2 =
(DashboardData.SuggestionConditionHeaderData) expectedObjects[i];
assertThat(i1.title).isEqualTo(i2.title);
assertThat(i1.conditionCount).isEqualTo(i2.conditionCount);
assertThat(i1.hiddenSuggestionCount).isEqualTo(i2.hiddenSuggestionCount);
} else {
assertThat(item).isSameAs(expectedObjects[i]);
}
}
}
@Test
public void testGetPositionByEntity_selfInstance_returnPositionFound() {
final int position = mDashboardDataWithOneConditions
.getPositionByEntity(mDashboardDataWithOneConditions.getConditions());
assertThat(position).isNotEqualTo(DashboardData.POSITION_NOT_FOUND);
}
@Test
public void testGetPositionByEntity_notExisted_returnNotFound() {
final Condition condition = mock(AirplaneModeCondition.class);
final int position = mDashboardDataWithOneConditions.getPositionByEntity(condition);
assertThat(position).isEqualTo(DashboardData.POSITION_NOT_FOUND);
}
@Test
public void testGetPositionByTile_selfInstance_returnPositionFound() {
final int position = mDashboardDataWithOneConditions
.getPositionByTile(mTestCategoryTile);
assertThat(position).isNotEqualTo(DashboardData.POSITION_NOT_FOUND);
}
@Test
public void testGetPositionByTile_equalTitle_returnPositionFound() {
final Tile tile = mock(Tile.class);
tile.title = TEST_CATEGORY_TILE_TITLE;
final int position = mDashboardDataWithOneConditions.getPositionByTile(tile);
assertThat(position).isNotEqualTo(DashboardData.POSITION_NOT_FOUND);
}
@Test
public void testGetPositionByTile_notExisted_returnNotFound() {
final Tile tile = mock(Tile.class);
tile.title = "";
final int position = mDashboardDataWithOneConditions.getPositionByTile(tile);
assertThat(position).isEqualTo(DashboardData.POSITION_NOT_FOUND);
}
@Test
public void testDiffUtil_DataEqual_noResultData() {
List<ListUpdateResult.ResultData> testResultData = new ArrayList<>();
testDiffUtil(mDashboardDataWithOneConditions,
mDashboardDataWithOneConditions, testResultData);
}
@Test
public void testDiffUtil_InsertOneCondition_ResultDataOneChanged() {
//Build testResultData
final List<ListUpdateResult.ResultData> testResultData = new ArrayList<>();
// Item in position 2 is the condition container containing the list of conditions, which
// gets 1 more item
testResultData.add(new ListUpdateResult.ResultData(
ListUpdateResult.ResultData.TYPE_OPERATION_CHANGE, 1, 1));
testDiffUtil(mDashboardDataWithOneConditions,
mDashboardDataWithTwoConditions, testResultData);
}
@Test
public void testDiffUtil_RemoveOneSuggestion_causeItemRemoveAndChange() {
//Build testResultData
final List<ListUpdateResult.ResultData> testResultData = new ArrayList<>();
testResultData.add(new ListUpdateResult.ResultData(
ListUpdateResult.ResultData.TYPE_OPERATION_REMOVE, 0, 1));
testResultData.add(new ListUpdateResult.ResultData(
ListUpdateResult.ResultData.TYPE_OPERATION_CHANGE, 1, 1));
// Build DashboardData
final List<Condition> oneItemConditions = new ArrayList<>();
when(mTestCondition.shouldShow()).thenReturn(true);
oneItemConditions.add(mTestCondition);
final List<Tile> suggestions = new ArrayList<>();
mTestSuggestion.title = TEST_SUGGESTION_TITLE;
suggestions.add(mTestSuggestion);
final DashboardData oldData = new DashboardData.Builder()
.setConditions(oneItemConditions)
.setCategory(mDashboardCategory)
.setSuggestions(suggestions)
.setSuggestionConditionMode(DashboardData.HEADER_MODE_DEFAULT)
.build();
final DashboardData newData = new DashboardData.Builder()
.setConditions(oneItemConditions)
.setSuggestions(null)
.setCategory(mDashboardCategory)
.setSuggestionConditionMode(DashboardData.HEADER_MODE_DEFAULT)
.build();
testDiffUtil(oldData, newData, testResultData);
}
@Test
public void testDiffUtil_DeleteAllData_ResultDataOneDeleted() {
//Build testResultData
final List<ListUpdateResult.ResultData> testResultData = new ArrayList<>();
testResultData.add(new ListUpdateResult.ResultData(
ListUpdateResult.ResultData.TYPE_OPERATION_REMOVE, 0, 4));
testDiffUtil(mDashboardDataWithOneConditions, mDashboardDataWithNoItems, testResultData);
}
@Test
public void testDiffUtil_typeSuggestedContainer_ResultDataNothingChanged() {
//Build testResultData
final List<ListUpdateResult.ResultData> testResultData = new ArrayList<>();
testResultData.add(new ListUpdateResult.ResultData(
ListUpdateResult.ResultData.TYPE_OPERATION_CHANGE, 0, 1));
Tile tile = new Tile();
tile.remoteViews = mock(RemoteViews.class);
DashboardData prevData = new DashboardData.Builder()
.setConditions(null)
.setCategory(null)
.setSuggestions(Arrays.asList(tile))
.build();
DashboardData currentData = new DashboardData.Builder()
.setConditions(null)
.setCategory(null)
.setSuggestions(Arrays.asList(tile))
.build();
testDiffUtil(prevData, currentData, testResultData);
}
/**
* Test when using the
* {@link com.android.settings.dashboard.DashboardData.ItemsDataDiffCallback}
* to transfer List from {@paramref baseDashboardData} to {@paramref diffDashboardData}, whether
* the transform data result is equals to {@paramref testResultData}
* <p>
* The steps are described below:
* 1. Calculate a {@link android.support.v7.util.DiffUtil.DiffResult} from
* {@paramref baseDashboardData} to {@paramref diffDashboardData}
* <p>
* 2. Dispatch the {@link android.support.v7.util.DiffUtil.DiffResult} calculated from step 1
* into {@link ListUpdateResult}
* <p>
* 3. Get result data(a.k.a. baseResultData) from {@link ListUpdateResult} and compare it to
* {@paramref testResultData}
* <p>
* Because baseResultData and {@paramref testResultData} don't have sequence. When do the
* comparison, we will sort them first and then compare the inside data from them one by one.
*/
private void testDiffUtil(DashboardData baseDashboardData, DashboardData diffDashboardData,
List<ListUpdateResult.ResultData> testResultData) {
final DiffUtil.DiffResult diffUtilResult = DiffUtil.calculateDiff(
new DashboardData.ItemsDataDiffCallback(
baseDashboardData.getItemList(), diffDashboardData.getItemList()));
// Dispatch to listUpdateResult, then listUpdateResult will have result data
final ListUpdateResult listUpdateResult = new ListUpdateResult();
diffUtilResult.dispatchUpdatesTo(listUpdateResult);
final List<ListUpdateResult.ResultData> baseResultData = listUpdateResult.getResultData();
assertThat(testResultData.size()).isEqualTo(baseResultData.size());
// Sort them so we can compare them one by one using a for loop
Collections.sort(baseResultData);
Collections.sort(testResultData);
final int size = baseResultData.size();
for (int i = 0; i < size; i++) {
// Refer to equals method in ResultData
assertThat(baseResultData.get(i)).isEqualTo(testResultData.get(i));
}
}
/**
* This class contains the result about how the changes made to convert one
* list to another list. It implements ListUpdateCallback to record the result data.
*/
private static class ListUpdateResult implements ListUpdateCallback {
final private List<ResultData> mResultData;
public ListUpdateResult() {
mResultData = new ArrayList<>();
}
public List<ResultData> getResultData() {
return mResultData;
}
@Override
public void onInserted(int position, int count) {
mResultData.add(new ResultData(ResultData.TYPE_OPERATION_INSERT, position, count));
}
@Override
public void onRemoved(int position, int count) {
mResultData.add(new ResultData(ResultData.TYPE_OPERATION_REMOVE, position, count));
}
@Override
public void onMoved(int fromPosition, int toPosition) {
mResultData.add(
new ResultData(ResultData.TYPE_OPERATION_MOVE, fromPosition, toPosition));
}
@Override
public void onChanged(int position, int count, Object payload) {
mResultData.add(new ResultData(ResultData.TYPE_OPERATION_CHANGE, position, count));
}
/**
* This class contains general type and field to record the operation data generated
* in {@link ListUpdateCallback}. Please refer to {@link ListUpdateCallback} for more info.
* <p>
* The following are examples about the data stored in this class:
* <p>
* "The data starts from position(arg1) with count number(arg2) is changed(operation)"
* or "The data is moved(operation) from position1(arg1) to position2(arg2)"
*/
private static class ResultData implements Comparable<ResultData> {
public static final int TYPE_OPERATION_INSERT = 0;
public static final int TYPE_OPERATION_REMOVE = 1;
public static final int TYPE_OPERATION_MOVE = 2;
public static final int TYPE_OPERATION_CHANGE = 3;
public final int operation;
public final int arg1;
public final int arg2;
public ResultData(int operation, int arg1, int arg2) {
this.operation = operation;
this.arg1 = arg1;
this.arg2 = arg2;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof ResultData)) {
return false;
}
ResultData targetData = (ResultData) obj;
return operation == targetData.operation && arg1 == targetData.arg1
&& arg2 == targetData.arg2;
}
@Override
public int compareTo(@NonNull ResultData resultData) {
if (this.operation != resultData.operation) {
return operation - resultData.operation;
}
if (arg1 != resultData.arg1) {
return arg1 - resultData.arg1;
}
return arg2 - resultData.arg2;
}
@Override
public String toString() {
return "op:" + operation + ",arg1:" + arg1 + ",arg2:" + arg2;
}
}
}
}