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 com.android.launcher3.popup;
18 
19 import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
20 
21 import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS;
22 import static com.android.launcher3.popup.PopupPopulator.NUM_DYNAMIC;
23 
24 import static org.junit.Assert.assertEquals;
25 import static org.junit.Assert.assertFalse;
26 import static org.junit.Assert.assertTrue;
27 import static org.mockito.Mockito.doReturn;
28 import static org.mockito.Mockito.spy;
29 
30 import android.content.pm.ShortcutInfo;
31 
32 import androidx.test.ext.junit.runners.AndroidJUnit4;
33 import androidx.test.filters.SmallTest;
34 
35 import org.junit.Test;
36 import org.junit.runner.RunWith;
37 
38 import java.util.ArrayList;
39 import java.util.Collections;
40 import java.util.List;
41 
42 /**
43  * Tests the sorting and filtering of shortcuts in {@link PopupPopulator}.
44  */
45 @SmallTest
46 @RunWith(AndroidJUnit4.class)
47 public class PopupPopulatorTest {
48 
49     @Test
testSortAndFilterShortcuts()50     public void testSortAndFilterShortcuts() {
51         filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(3, 0), 3, 0);
52         filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(0, 3), 0, 3);
53         filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(5, 0), MAX_SHORTCUTS, 0);
54         filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(0, 5), 0, MAX_SHORTCUTS);
55         filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(3, 3),
56                 MAX_SHORTCUTS - NUM_DYNAMIC, NUM_DYNAMIC);
57         filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(5, 5),
58                 MAX_SHORTCUTS - NUM_DYNAMIC, NUM_DYNAMIC);
59         filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(5, 1), MAX_SHORTCUTS - 1, 1);
60         filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(1, 5), 1, MAX_SHORTCUTS - 1);
61         filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(5, 3),
62                 MAX_SHORTCUTS - NUM_DYNAMIC, NUM_DYNAMIC);
63         filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(3, 5),
64                 MAX_SHORTCUTS - NUM_DYNAMIC, NUM_DYNAMIC);
65     }
66 
67     @Test
testDeDupeShortcutId()68     public void testDeDupeShortcutId() {
69         // Successfully remove one of the shortcuts
70         filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(3, 0), 2, 0, generateId(true, 1));
71         filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(0, 3), 0, 2, generateId(false, 1));
72         filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(2, 2), 2, 1, generateId(false, 1));
73         filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(2, 2), 1, 2, generateId(true, 1));
74         // Successfully keep all shortcuts when id doesn't exist
75         filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(3, 0), 3, 0, generateId(false, 1));
76         filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(3, 0), 3, 0, generateId(true, 4));
77         filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(2, 2), 2, 2, generateId(false, 4));
78         filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(2, 2), 2, 2, generateId(true, 4));
79     }
80 
generateId(boolean isStatic, int rank)81     private String generateId(boolean isStatic, int rank) {
82         return (isStatic ? "static" : "dynamic") + rank;
83     }
84 
filterShortcutsAndAssertNumStaticAndDynamic( List<ShortcutInfo> shortcuts, int expectedStatic, int expectedDynamic)85     private void filterShortcutsAndAssertNumStaticAndDynamic(
86             List<ShortcutInfo> shortcuts, int expectedStatic, int expectedDynamic) {
87         filterShortcutsAndAssertNumStaticAndDynamic(shortcuts, expectedStatic, expectedDynamic, null);
88     }
89 
filterShortcutsAndAssertNumStaticAndDynamic(List<ShortcutInfo> shortcuts, int expectedStatic, int expectedDynamic, String shortcutIdToRemove)90     private void filterShortcutsAndAssertNumStaticAndDynamic(List<ShortcutInfo> shortcuts,
91             int expectedStatic, int expectedDynamic, String shortcutIdToRemove) {
92         Collections.shuffle(shortcuts);
93         List<ShortcutInfo> filteredShortcuts = PopupPopulator.sortAndFilterShortcuts(
94                 shortcuts, shortcutIdToRemove);
95         assertIsSorted(filteredShortcuts);
96 
97         int numStatic = 0;
98         int numDynamic = 0;
99         for (ShortcutInfo shortcut : filteredShortcuts) {
100             if (shortcut.isDeclaredInManifest()) {
101                 numStatic++;
102             }
103             if (shortcut.isDynamic()) {
104                 numDynamic++;
105             }
106         }
107         assertEquals(expectedStatic, numStatic);
108         assertEquals(expectedDynamic, numDynamic);
109     }
110 
assertIsSorted(List<ShortcutInfo> shortcuts)111     private void assertIsSorted(List<ShortcutInfo> shortcuts) {
112         int lastStaticRank = -1;
113         int lastDynamicRank = -1;
114         boolean hasSeenDynamic = false;
115         for (ShortcutInfo shortcut : shortcuts) {
116             int rank = shortcut.getRank();
117             if (shortcut.isDeclaredInManifest()) {
118                 assertFalse("Static shortcuts should come before all dynamic shortcuts.",
119                         hasSeenDynamic);
120                 assertTrue(rank > lastStaticRank);
121                 lastStaticRank = rank;
122             }
123             if (shortcut.isDynamic()) {
124                 hasSeenDynamic = true;
125                 assertTrue(rank > lastDynamicRank);
126                 lastDynamicRank = rank;
127             }
128         }
129     }
130 
createShortcutsList(int numStatic, int numDynamic)131     private List<ShortcutInfo> createShortcutsList(int numStatic, int numDynamic) {
132         List<ShortcutInfo> shortcuts = new ArrayList<>();
133         for (int i = 0; i < numStatic; i++) {
134             shortcuts.add(createInfo(true, i));
135         }
136         for (int i = 0; i < numDynamic; i++) {
137             shortcuts.add(createInfo(false, i));
138         }
139         return shortcuts;
140     }
141 
createInfo(boolean isStatic, int rank)142     private ShortcutInfo createInfo(boolean isStatic, int rank) {
143         ShortcutInfo info = spy(new ShortcutInfo.Builder(
144                 getApplicationContext(), generateId(isStatic, rank))
145                 .setRank(rank)
146                 .build());
147         doReturn(isStatic).when(info).isDeclaredInManifest();
148         doReturn(!isStatic).when(info).isDynamic();
149         return info;
150     }
151 }