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