blob: 4fada336fb94c47868198a7a9a3adba0ce234d57 [file] [log] [blame]
/*
* Copyright (C) 2017 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.systemui.qs.tileimpl;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_CLICK;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_LONG_PRESS;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_SECONDARY_CLICK;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_QS_POSITION;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_QS_VALUE;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_STATUS_BAR_STATE;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_ACTION;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Matchers.argThat;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static java.lang.Thread.sleep;
import android.content.Intent;
import android.metrics.LogMaker;
import android.service.quicksettings.Tile;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.statusbar.StatusBarState;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatcher;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@SmallTest
public class QSTileImplTest extends SysuiTestCase {
public static final int POSITION = 14;
private static final String SPEC = "spec";
@Mock
private QSLogger mQsLogger;
private TestableLooper mTestableLooper;
private TileImpl mTile;
private QSTileHost mHost;
private MetricsLogger mMetricsLogger;
private StatusBarStateController mStatusBarStateController;
@Captor
private ArgumentCaptor<LogMaker> mLogCaptor;
@Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
mTestableLooper = TestableLooper.get(this);
mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
mDependency.injectMockDependency(ActivityStarter.class);
mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
mStatusBarStateController =
mDependency.injectMockDependency(StatusBarStateController.class);
mHost = mock(QSTileHost.class);
when(mHost.indexOf(SPEC)).thenReturn(POSITION);
when(mHost.getContext()).thenReturn(mContext.getBaseContext());
when(mHost.getQSLogger()).thenReturn(mQsLogger);
mTile = spy(new TileImpl(mHost));
mTile.mHandler = mTile.new H(mTestableLooper.getLooper());
mTile.setTileSpec(SPEC);
}
@Test
public void testClick_Metrics() {
mTile.click();
verify(mMetricsLogger).write(argThat(new TileLogMatcher(ACTION_QS_CLICK)));
}
@Test
public void testClick_log() {
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
mTile.click();
verify(mQsLogger).logTileClick(SPEC, StatusBarState.SHADE, Tile.STATE_ACTIVE);
}
@Test
public void testClick_Metrics_Status_Bar_Status() {
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
mTile.click();
verify(mMetricsLogger).write(mLogCaptor.capture());
assertEquals(StatusBarState.SHADE, mLogCaptor.getValue()
.getTaggedData(FIELD_STATUS_BAR_STATE));
}
@Test
public void testSecondaryClick_Metrics() {
mTile.secondaryClick();
verify(mMetricsLogger).write(argThat(new TileLogMatcher(ACTION_QS_SECONDARY_CLICK)));
}
@Test
public void testSecondaryClick_log() {
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
mTile.secondaryClick();
verify(mQsLogger).logTileSecondaryClick(SPEC, StatusBarState.SHADE, Tile.STATE_ACTIVE);
}
@Test
public void testSecondaryClick_Metrics_Status_Bar_Status() {
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
mTile.secondaryClick();
verify(mMetricsLogger).write(mLogCaptor.capture());
assertEquals(StatusBarState.KEYGUARD, mLogCaptor.getValue()
.getTaggedData(FIELD_STATUS_BAR_STATE));
}
@Test
public void testLongClick_Metrics() {
mTile.longClick();
verify(mMetricsLogger).write(argThat(new TileLogMatcher(ACTION_QS_LONG_PRESS)));
}
@Test
public void testLongClick_log() {
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
mTile.longClick();
verify(mQsLogger).logTileLongClick(SPEC, StatusBarState.SHADE, Tile.STATE_ACTIVE);
}
@Test
public void testLongClick_Metrics_Status_Bar_Status() {
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE_LOCKED);
mTile.click();
verify(mMetricsLogger).write(mLogCaptor.capture());
assertEquals(StatusBarState.SHADE_LOCKED, mLogCaptor.getValue()
.getTaggedData(FIELD_STATUS_BAR_STATE));
}
@Test
public void testPopulateWithLockedScreen() {
LogMaker maker = mock(LogMaker.class);
when(maker.setSubtype(anyInt())).thenReturn(maker);
when(maker.addTaggedData(anyInt(), any())).thenReturn(maker);
mTile.getState().value = true;
mTile.populate(maker);
verify(maker).addTaggedData(eq(FIELD_QS_VALUE), eq(1));
verify(maker).addTaggedData(eq(FIELD_QS_POSITION), eq(POSITION));
}
@Test
public void testPopulateWithUnlockedScreen() {
LogMaker maker = mock(LogMaker.class);
when(maker.setSubtype(anyInt())).thenReturn(maker);
when(maker.addTaggedData(anyInt(), any())).thenReturn(maker);
mTile.getState().value = true;
mTile.populate(maker);
verify(maker).addTaggedData(eq(FIELD_QS_VALUE), eq(1));
verify(maker).addTaggedData(eq(FIELD_QS_POSITION), eq(POSITION));
}
@Test
@Ignore("flaky")
public void testStaleTimeout() throws InterruptedException {
when(mTile.getStaleTimeout()).thenReturn(5l);
clearInvocations(mTile);
mTile.handleRefreshState(null);
mTestableLooper.processAllMessages();
verify(mTile, never()).handleStale();
sleep(10);
mTestableLooper.processAllMessages();
verify(mTile).handleStale();
}
@Test
public void testStaleListening() {
mTile.handleStale();
mTestableLooper.processAllMessages();
verify(mTile).handleSetListening(eq(true));
mTile.handleRefreshState(null);
mTestableLooper.processAllMessages();
verify(mTile).handleSetListening(eq(false));
}
@Test
public void testHandleDestroyClearsHandlerQueue() {
when(mTile.getStaleTimeout()).thenReturn(0L);
mTile.handleRefreshState(null); // this will add a delayed H.STALE message
mTile.handleDestroy();
mTestableLooper.processAllMessages();
verify(mTile, never()).handleStale();
}
@Test
public void testHandleDestroy_log() {
mTile.handleDestroy();
verify(mQsLogger).logTileDestroyed(eq(SPEC), anyString());
}
@Test
public void testListening_log() {
mTile.handleSetListening(true);
verify(mQsLogger).logTileChangeListening(SPEC, true);
mTile.handleSetListening(false);
verify(mQsLogger).logTileChangeListening(SPEC, false);
}
private class TileLogMatcher implements ArgumentMatcher<LogMaker> {
private final int mCategory;
public String mInvalid;
public TileLogMatcher(int category) {
mCategory = category;
}
@Override
public boolean matches(LogMaker arg) {
if (arg.getCategory() != mCategory) {
mInvalid = "Expected category " + mCategory + " but was " + arg.getCategory();
return false;
}
if (arg.getType() != TYPE_ACTION) {
mInvalid = "Expected type " + TYPE_ACTION + " but was " + arg.getType();
return false;
}
if (arg.getSubtype() != mTile.getMetricsCategory()) {
mInvalid = "Expected subtype " + mTile.getMetricsCategory() + " but was "
+ arg.getSubtype();
return false;
}
return true;
}
@Override
public String toString() {
return mInvalid;
}
}
private static class TileImpl extends QSTileImpl<QSTile.BooleanState> {
protected TileImpl(QSHost host) {
super(host);
getState().state = Tile.STATE_ACTIVE;
}
@Override
public BooleanState newTileState() {
return new BooleanState();
}
@Override
protected void handleClick() {
}
@Override
protected void handleUpdateState(BooleanState state, Object arg) {
}
@Override
public int getMetricsCategory() {
return 42;
}
@Override
public Intent getLongClickIntent() {
return null;
}
@Override
public CharSequence getTileLabel() {
return null;
}
}
}