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