1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5  * except in compliance with the License. You may obtain a copy of the License at
6  *
7  *      http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the
10  * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11  * KIND, either express or implied. See the License for the specific language governing
12  * permissions and limitations under the License.
13  */
14 
15 package com.android.systemui.qs.tileimpl;
16 
17 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_CLICK;
18 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_LONG_PRESS;
19 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_SECONDARY_CLICK;
20 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_QS_POSITION;
21 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_QS_VALUE;
22 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_ACTION;
23 
24 import static org.mockito.ArgumentMatchers.any;
25 import static org.mockito.ArgumentMatchers.anyInt;
26 import static org.mockito.ArgumentMatchers.eq;
27 import static org.mockito.Matchers.argThat;
28 import static org.mockito.Mockito.clearInvocations;
29 import static org.mockito.Mockito.mock;
30 import static org.mockito.Mockito.never;
31 import static org.mockito.Mockito.spy;
32 import static org.mockito.Mockito.verify;
33 import static org.mockito.Mockito.when;
34 
35 import static java.lang.Thread.sleep;
36 
37 import android.content.Intent;
38 import android.metrics.LogMaker;
39 import android.support.test.filters.SmallTest;
40 import android.support.test.InstrumentationRegistry;
41 import android.testing.AndroidTestingRunner;
42 import android.testing.TestableLooper;
43 import android.testing.TestableLooper.RunWithLooper;
44 
45 import com.android.internal.logging.MetricsLogger;
46 import com.android.systemui.Dependency;
47 import com.android.systemui.SysuiTestCase;
48 import com.android.systemui.plugins.qs.QSTile;
49 import com.android.systemui.qs.QSHost;
50 import com.android.systemui.qs.QSTileHost;
51 
52 import org.junit.Before;
53 import org.junit.Ignore;
54 import org.junit.Test;
55 import org.junit.runner.RunWith;
56 import org.mockito.ArgumentMatcher;
57 
58 @RunWith(AndroidTestingRunner.class)
59 @RunWithLooper
60 @SmallTest
61 public class QSTileImplTest extends SysuiTestCase {
62 
63     public static final int POSITION = 14;
64     private TestableLooper mTestableLooper;
65     private TileImpl mTile;
66     private QSTileHost mHost;
67     private MetricsLogger mMetricsLogger;
68 
69     @Before
setup()70     public void setup() throws Exception {
71         String spec = "spec";
72         mTestableLooper = TestableLooper.get(this);
73         mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
74         mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
75         mHost = mock(QSTileHost.class);
76         when(mHost.indexOf(spec)).thenReturn(POSITION);
77         when(mHost.getContext()).thenReturn(mContext.getBaseContext());
78 
79         mTile = spy(new TileImpl(mHost));
80         mTile.mHandler = mTile.new H(mTestableLooper.getLooper());
81         mTile.setTileSpec(spec);
82     }
83 
84     @Test
testClick_Metrics()85     public void testClick_Metrics() {
86         mTile.click();
87         verify(mMetricsLogger).write(argThat(new TileLogMatcher(ACTION_QS_CLICK)));
88     }
89 
90     @Test
testSecondaryClick_Metrics()91     public void testSecondaryClick_Metrics() {
92         mTile.secondaryClick();
93         verify(mMetricsLogger).write(argThat(new TileLogMatcher(ACTION_QS_SECONDARY_CLICK)));
94     }
95 
96     @Test
testLongClick_Metrics()97     public void testLongClick_Metrics() {
98         mTile.longClick();
99         verify(mMetricsLogger).write(argThat(new TileLogMatcher(ACTION_QS_LONG_PRESS)));
100     }
101 
102     @Test
testPopulate()103     public void testPopulate() {
104         LogMaker maker = mock(LogMaker.class);
105         when(maker.setSubtype(anyInt())).thenReturn(maker);
106         when(maker.addTaggedData(anyInt(), any())).thenReturn(maker);
107         mTile.getState().value = true;
108         mTile.populate(maker);
109         verify(maker).addTaggedData(eq(FIELD_QS_VALUE), eq(1));
110         verify(maker).addTaggedData(eq(FIELD_QS_POSITION), eq(POSITION));
111     }
112 
113     @Test
114     @Ignore("flaky")
testStaleTimeout()115     public void testStaleTimeout() throws InterruptedException {
116         when(mTile.getStaleTimeout()).thenReturn(5l);
117         clearInvocations(mTile);
118 
119         mTile.handleRefreshState(null);
120         mTestableLooper.processAllMessages();
121         verify(mTile, never()).handleStale();
122 
123         sleep(10);
124         mTestableLooper.processAllMessages();
125         verify(mTile).handleStale();
126     }
127 
128     @Test
testStaleListening()129     public void testStaleListening() {
130         mTile.handleStale();
131         mTestableLooper.processAllMessages();
132         verify(mTile).handleSetListening(eq(true));
133 
134         mTile.handleRefreshState(null);
135         mTestableLooper.processAllMessages();
136         verify(mTile).handleSetListening(eq(false));
137     }
138 
139     private class TileLogMatcher implements ArgumentMatcher<LogMaker> {
140 
141         private final int mCategory;
142         public String mInvalid;
143 
TileLogMatcher(int category)144         public TileLogMatcher(int category) {
145             mCategory = category;
146         }
147 
148         @Override
matches(LogMaker arg)149         public boolean matches(LogMaker arg) {
150             if (arg.getCategory() != mCategory) {
151                 mInvalid = "Expected category " + mCategory + " but was " + arg.getCategory();
152                 return false;
153             }
154             if (arg.getType() != TYPE_ACTION) {
155                 mInvalid = "Expected type " + TYPE_ACTION + " but was " + arg.getType();
156                 return false;
157             }
158             if (arg.getSubtype() != mTile.getMetricsCategory()) {
159                 mInvalid = "Expected subtype " + mTile.getMetricsCategory() + " but was "
160                         + arg.getSubtype();
161                 return false;
162             }
163             return true;
164         }
165 
166         @Override
toString()167         public String toString() {
168             return mInvalid;
169         }
170     }
171 
172     private static class TileImpl extends QSTileImpl<QSTile.BooleanState> {
TileImpl(QSHost host)173         protected TileImpl(QSHost host) {
174             super(host);
175         }
176 
177         @Override
newTileState()178         public BooleanState newTileState() {
179             return new BooleanState();
180         }
181 
182         @Override
handleClick()183         protected void handleClick() {
184 
185         }
186 
187         @Override
handleUpdateState(BooleanState state, Object arg)188         protected void handleUpdateState(BooleanState state, Object arg) {
189 
190         }
191 
192         @Override
getMetricsCategory()193         public int getMetricsCategory() {
194             return 42;
195         }
196 
197         @Override
getLongClickIntent()198         public Intent getLongClickIntent() {
199             return null;
200         }
201 
202         @Override
handleSetListening(boolean listening)203         protected void handleSetListening(boolean listening) {
204 
205         }
206 
207         @Override
getTileLabel()208         public CharSequence getTileLabel() {
209             return null;
210         }
211     }
212 }
213