blob: 8f358d085c3e87a7dc1047eb2f432f3f09eb766b [file] [log] [blame]
/*
* Copyright (C) 2021 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.cts.tv;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assume.assumeTrue;
import android.cts.statsdatom.lib.AtomTestUtils;
import android.cts.statsdatom.lib.ConfigUtils;
import android.cts.statsdatom.lib.DeviceUtils;
import android.cts.statsdatom.lib.ReportUtils;
import android.stats.tv.TifTuneState;
import com.android.os.AtomsProto;
import com.android.os.StatsLog;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.truth.Correspondence;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runner.RunWith;
import org.junit.runners.model.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
/**
* Test {@link com.android.server.tv.TvInputManagerService} statsd metrics.
*/
@RunWith(DeviceJUnit4ClassRunner.class)
public class TvInputManagerServiceHostTest extends BaseHostJUnit4Test {
public static final String TEST_APK = "CtsTvTestCasesHelperApp.apk";
public static final String TEST_PKG = "android.tv.cts";
/** Compares the fields state, hdmiPort and inputId */
private static final Correspondence<AtomsProto.TifTuneStateChanged,
AtomsProto.TifTuneStateChanged>
TIF_TUNE_STATE_CHANGED_CORRESPONDENCE = CorrespondenceFieldChainBuilder
.builder(AtomsProto.TifTuneStateChanged.class)
.addField(AtomsProto.TifTuneStateChanged::getState, "state")
.addField(AtomsProto.TifTuneStateChanged::getHdmiPort, "hdmi_port")
.addField(AtomsProto.TifTuneStateChanged::getInputId, "input_id")
.build();
@Rule
public RequiredFeatureRule requiredFeatureRule = new RequiredFeatureRule(this,
"android.software.live_tv");
@Before
public void setup() throws Exception {
installPackage(TEST_APK);
ConfigUtils.removeConfig(getDevice());
ReportUtils.clearReports(getDevice());
Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
}
@After
public void tearDown() throws Exception {
ConfigUtils.removeConfig(getDevice());
ReportUtils.clearReports(getDevice());
uninstallPackage(TEST_APK);
}
@Test
public void verifyCallbackVideoAvailable() throws Exception {
ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), TEST_PKG,
AtomsProto.Atom.TIF_TUNE_CHANGED_FIELD_NUMBER, /*uidInAttributionChain=*/true);
runTvInputServiceTest("verifyCallbackVideoAvailable");
// Sorted list of events in order in which they occurred.
List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
List<AtomsProto.TifTuneStateChanged> tifTuneStateChanges = Lists.transform(data,
input -> input.getAtom().getTifTuneChanged());
AtomsProto.TifTuneStateChanged.Builder protoBuilder =
AtomsProto.TifTuneStateChanged.newBuilder().
setInputId(1).
setHdmiPort(0);
assertThat(tifTuneStateChanges).
comparingElementsUsing(TIF_TUNE_STATE_CHANGED_CORRESPONDENCE).
containsExactlyElementsIn(
createAtomsFromStateList(protoBuilder,
TifTuneState.CREATED,
TifTuneState.SURFACE_ATTACHED,
TifTuneState.TUNE_STARTED,
TifTuneState.VIDEO_AVAILABLE,
TifTuneState.SURFACE_DETACHED,
TifTuneState.RELEASED)).
inOrder();
}
@Test
public void verifyCallbackVideoUnavailable() throws Exception {
ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), TEST_PKG,
AtomsProto.Atom.TIF_TUNE_CHANGED_FIELD_NUMBER, /*uidInAttributionChain=*/true);
runTvInputServiceTest("verifyCallbackVideoUnavailable");
// Sorted list of events in order in which they occurred.
List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
List<AtomsProto.TifTuneStateChanged> tifTuneStateChanges = Lists.transform(data,
input -> input.getAtom().getTifTuneChanged());
AtomsProto.TifTuneStateChanged.Builder protoBuilder =
AtomsProto.TifTuneStateChanged.newBuilder().
setInputId(1).
setHdmiPort(0);
assertThat(tifTuneStateChanges).
comparingElementsUsing(TIF_TUNE_STATE_CHANGED_CORRESPONDENCE).
containsExactlyElementsIn(
createAtomsFromStateList(protoBuilder,
TifTuneState.CREATED,
TifTuneState.SURFACE_ATTACHED,
TifTuneState.TUNE_STARTED,
TifTuneState.VIDEO_UNAVAILABLE_REASON_TUNING,
TifTuneState.SURFACE_DETACHED,
TifTuneState.RELEASED)).
inOrder();
}
@Test
public void verifyCommandTune() throws Exception {
ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), TEST_PKG,
AtomsProto.Atom.TIF_TUNE_CHANGED_FIELD_NUMBER, /*uidInAttributionChain=*/true);
runTvInputServiceTest("verifyCommandTune");
// Sorted list of events in order in which they occurred.
List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
List<AtomsProto.TifTuneStateChanged> tifTuneStateChanges = Lists.transform(data,
input -> input.getAtom().getTifTuneChanged());
AtomsProto.TifTuneStateChanged.Builder protoBuilder =
AtomsProto.TifTuneStateChanged.newBuilder().
setInputId(1).
setHdmiPort(0);
assertThat(tifTuneStateChanges).
comparingElementsUsing(TIF_TUNE_STATE_CHANGED_CORRESPONDENCE).
containsExactlyElementsIn(
createAtomsFromStateList(protoBuilder,
TifTuneState.CREATED,
TifTuneState.SURFACE_ATTACHED,
TifTuneState.TUNE_STARTED,
TifTuneState.SURFACE_DETACHED,
TifTuneState.RELEASED)).
inOrder();
}
private void runTvInputServiceTest(String testMethodName)
throws DeviceNotAvailableException {
DeviceUtils.runDeviceTests(getDevice(), TEST_PKG,
"android.media.tv.cts.TvInputServiceTest", testMethodName);
}
private static Iterable<AtomsProto.TifTuneStateChanged> createAtomsFromStateList(
AtomsProto.TifTuneStateChanged.Builder protoBuilder, TifTuneState... tuneStates) {
ImmutableList.Builder<AtomsProto.TifTuneStateChanged> atoms = ImmutableList.builder();
for (int i = 0; i < tuneStates.length; i++) {
atoms.add(protoBuilder.setState(tuneStates[i]).build());
}
return atoms.build();
}
private static class CorrespondenceFieldChainBuilder<T> {
private static class GetterName<T> {
private final Function<T, Object> getter;
private final String name;
private GetterName(Function<T, Object> getter, String name) {
this.getter = getter;
this.name = name;
}
boolean isEqual(T actual, T expected) {
return getter.apply(actual).equals(getter.apply(expected));
}
String diff(T actual, T expected) {
return isEqual(actual, expected) ? ""
: String.format(
Locale.ENGLISH, "%s Expected: %s Actual: %s",
name, getter.apply(expected),
getter.apply(actual));
}
}
private static <T> CorrespondenceFieldChainBuilder<T> builder(
Class<T> unused) {
return new CorrespondenceFieldChainBuilder<T>();
}
List<GetterName<T>> getterNames = new ArrayList<>();
CorrespondenceFieldChainBuilder<T> addField(Function<T, Object> getter, String name) {
getterNames.add(new GetterName(getter, name));
return this;
}
Correspondence<T, T> build() {
Correspondence.BinaryPredicate<T, T> predicate = (actual, expected) ->
Iterables.all(getterNames, input -> input.isEqual(actual, expected));
Correspondence.DiffFormatter<T, T> formatter =
(actual, expected) -> Joiner.on("\n").join(
Iterables.transform(getterNames,
input -> input.diff(actual, expected)));
return Correspondence.from(predicate,
"matches fields " + Lists.transform(getterNames,
input -> input.name)).formattingDiffsUsing(
formatter);
}
}
// TODO(b/180429722): Move to a common CTS location.
public static class RequiredFeatureRule implements TestRule {
private final BaseHostJUnit4Test mTest;
private final String mFeature;
public RequiredFeatureRule(BaseHostJUnit4Test test, String feature) {
mTest = test;
mFeature = feature;
}
@Override
public Statement apply(final Statement base, Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
ITestDevice testDevice = mTest.getDevice();
// Checks if the device is available.
assumeTrue("Test device is not available", testDevice != null);
// Checks if the requested feature is available on the device.
assumeTrue(mFeature + " not present in DUT " + testDevice.getSerialNumber(),
testDevice.hasFeature(mFeature));
base.evaluate();
}
};
}
}
}