blob: 71db54d3fe67dfd16349f3784403d153b33cea85 [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.car.telemetry;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.car.telemetry.CarTelemetryManager;
import android.car.telemetry.ICarTelemetryServiceListener;
import android.content.Context;
import android.os.Handler;
import android.os.PersistableBundle;
import android.os.ResultReceiver;
import androidx.test.filters.SmallTest;
import com.android.car.CarLocalServices;
import com.android.car.CarPropertyService;
import com.android.car.CarServiceUtils;
import com.android.car.systeminterface.SystemInterface;
import com.android.car.systeminterface.SystemStateInterface;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.junit.MockitoRule;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.nio.file.Files;
@RunWith(MockitoJUnitRunner.class)
@SmallTest
public class CarTelemetryServiceTest {
private static final String METRICS_CONFIG_NAME = "my_metrics_config";
private static final TelemetryProto.MetricsConfig METRICS_CONFIG_V1 =
TelemetryProto.MetricsConfig.newBuilder()
.setName(METRICS_CONFIG_NAME).setVersion(1).setScript("no-op").build();
private static final TelemetryProto.MetricsConfig METRICS_CONFIG_V2 =
TelemetryProto.MetricsConfig.newBuilder()
.setName(METRICS_CONFIG_NAME).setVersion(2).setScript("no-op").build();
private CarTelemetryService mService;
private File mTempSystemCarDir;
private Handler mTelemetryHandler;
private MetricsConfigStore mMetricsConfigStore;
private ResultStore mResultStore;
@Rule
public MockitoRule mMockitoRule = MockitoJUnit.rule();
@Mock
private ActivityManager mMockActivityManager;
@Mock
private CarPropertyService mMockCarPropertyService;
@Mock
private Context mMockContext;
@Mock
private ICarTelemetryServiceListener mMockListener;
@Mock
private SystemInterface mMockSystemInterface;
@Mock
private SystemStateInterface mMockSystemStateInterface;
@Mock
private ResultReceiver mMockAddMetricsConfigCallback;
@Before
public void setUp() throws Exception {
CarLocalServices.removeServiceForTest(SystemInterface.class);
CarLocalServices.addService(SystemInterface.class, mMockSystemInterface);
// ActivityManager is used by SystemMonitor
doAnswer(i -> {
ActivityManager.MemoryInfo mi = i.getArgument(0);
mi.availMem = 5_000_000L; // memory usage is at 50%
mi.totalMem = 10_000_000;
return null;
}).when(mMockActivityManager).getMemoryInfo(any(ActivityManager.MemoryInfo.class));
when(mMockContext.getSystemService(ActivityManager.class))
.thenReturn(mMockActivityManager);
mTempSystemCarDir = Files.createTempDirectory("telemetry_test").toFile();
when(mMockSystemInterface.getSystemCarDir()).thenReturn(mTempSystemCarDir);
when(mMockSystemInterface.getSystemStateInterface()).thenReturn(mMockSystemStateInterface);
mService = new CarTelemetryService(mMockContext, mMockCarPropertyService);
mService.init();
mService.setListener(mMockListener);
mTelemetryHandler = mService.getTelemetryHandler();
CarServiceUtils.runOnLooperSync(mTelemetryHandler.getLooper(), () -> { });
mMetricsConfigStore = mService.getMetricsConfigStore();
mResultStore = mService.getResultStore();
}
@Test
public void testAddMetricsConfig_newMetricsConfig_shouldSucceed() {
mService.addMetricsConfig(METRICS_CONFIG_NAME, METRICS_CONFIG_V1.toByteArray(),
mMockAddMetricsConfigCallback);
CarServiceUtils.runOnLooperSync(mTelemetryHandler.getLooper(), () -> { });
verify(mMockAddMetricsConfigCallback).send(
eq(CarTelemetryManager.STATUS_METRICS_CONFIG_SUCCESS), isNull());
}
@Test
public void testAddMetricsConfig_duplicateMetricsConfig_shouldFail() {
mService.addMetricsConfig(METRICS_CONFIG_NAME, METRICS_CONFIG_V1.toByteArray(),
mMockAddMetricsConfigCallback);
CarServiceUtils.runOnLooperSync(mTelemetryHandler.getLooper(), () -> { });
verify(mMockAddMetricsConfigCallback).send(
eq(CarTelemetryManager.STATUS_METRICS_CONFIG_SUCCESS), isNull());
mService.addMetricsConfig(METRICS_CONFIG_NAME, METRICS_CONFIG_V1.toByteArray(),
mMockAddMetricsConfigCallback);
CarServiceUtils.runOnLooperSync(mTelemetryHandler.getLooper(), () -> { });
verify(mMockAddMetricsConfigCallback).send(
eq(CarTelemetryManager.STATUS_METRICS_CONFIG_ALREADY_EXISTS), isNull());
}
@Test
public void testAddMetricsConfig_invalidMetricsConfig_shouldFail() {
mService.addMetricsConfig(METRICS_CONFIG_NAME, "bad config".getBytes(),
mMockAddMetricsConfigCallback);
CarServiceUtils.runOnLooperSync(mTelemetryHandler.getLooper(), () -> { });
verify(mMockAddMetricsConfigCallback).send(
eq(CarTelemetryManager.STATUS_METRICS_CONFIG_PARSE_FAILED), isNull());
}
@Test
public void testAddMetricsConfig_olderMetricsConfig_shouldFail() {
mService.addMetricsConfig(METRICS_CONFIG_NAME, METRICS_CONFIG_V2.toByteArray(),
mMockAddMetricsConfigCallback);
CarServiceUtils.runOnLooperSync(mTelemetryHandler.getLooper(), () -> {
});
verify(mMockAddMetricsConfigCallback).send(
eq(CarTelemetryManager.STATUS_METRICS_CONFIG_SUCCESS), isNull());
mService.addMetricsConfig(METRICS_CONFIG_NAME, METRICS_CONFIG_V1.toByteArray(),
mMockAddMetricsConfigCallback);
CarServiceUtils.runOnLooperSync(mTelemetryHandler.getLooper(), () -> { });
verify(mMockAddMetricsConfigCallback).send(
eq(CarTelemetryManager.STATUS_METRICS_CONFIG_VERSION_TOO_OLD), isNull());
}
@Test
public void testAddMetricsConfig_newerMetricsConfig_shouldReplaceAndDeleteOldResult() {
mService.addMetricsConfig(METRICS_CONFIG_NAME, METRICS_CONFIG_V1.toByteArray(),
mMockAddMetricsConfigCallback);
mResultStore.putInterimResult(METRICS_CONFIG_NAME, new PersistableBundle());
mService.addMetricsConfig(METRICS_CONFIG_NAME, METRICS_CONFIG_V2.toByteArray(),
mMockAddMetricsConfigCallback);
CarServiceUtils.runOnLooperSync(mTelemetryHandler.getLooper(), () -> { });
verify(mMockAddMetricsConfigCallback, atLeastOnce()).send(
eq(CarTelemetryManager.STATUS_METRICS_CONFIG_SUCCESS), isNull());
assertThat(mMetricsConfigStore.getActiveMetricsConfigs())
.containsExactly(METRICS_CONFIG_V2);
assertThat(mResultStore.getInterimResult(METRICS_CONFIG_NAME)).isNull();
}
@Test
public void testAddMetricsConfig_invalidName_shouldFail() {
String wrongName = "wrong name";
mService.addMetricsConfig(wrongName, METRICS_CONFIG_V1.toByteArray(),
mMockAddMetricsConfigCallback);
CarServiceUtils.runOnLooperSync(mTelemetryHandler.getLooper(), () -> { });
verify(mMockAddMetricsConfigCallback).send(
eq(CarTelemetryManager.STATUS_METRICS_CONFIG_PARSE_FAILED), isNull());
}
@Test
public void testRemoveMetricsConfig_shouldDeleteConfigAndResult() {
mService.addMetricsConfig(METRICS_CONFIG_NAME, METRICS_CONFIG_V1.toByteArray(),
mMockAddMetricsConfigCallback);
mResultStore.putInterimResult(METRICS_CONFIG_NAME, new PersistableBundle());
mService.removeMetricsConfig(METRICS_CONFIG_NAME);
CarServiceUtils.runOnLooperSync(mTelemetryHandler.getLooper(), () -> { });
assertThat(mMetricsConfigStore.getActiveMetricsConfigs()).isEmpty();
assertThat(mResultStore.getInterimResult(METRICS_CONFIG_NAME)).isNull();
}
@Test
public void testRemoveAllMetricsConfigs_shouldRemoveConfigsAndResults() {
String testConfigName = "test config";
TelemetryProto.MetricsConfig config =
TelemetryProto.MetricsConfig.newBuilder().setName(testConfigName).build();
mService.addMetricsConfig(testConfigName, config.toByteArray(),
mMockAddMetricsConfigCallback);
mService.addMetricsConfig(METRICS_CONFIG_NAME, METRICS_CONFIG_V1.toByteArray(),
mMockAddMetricsConfigCallback);
mResultStore.putInterimResult(METRICS_CONFIG_NAME, new PersistableBundle());
mResultStore.putFinalResult(testConfigName, new PersistableBundle());
mService.removeAllMetricsConfigs();
CarServiceUtils.runOnLooperSync(mTelemetryHandler.getLooper(), () -> { });
assertThat(mMetricsConfigStore.getActiveMetricsConfigs()).isEmpty();
assertThat(mResultStore.getInterimResult(METRICS_CONFIG_NAME)).isNull();
assertThat(mResultStore.getFinalResult(testConfigName, /* deleteResult = */ false))
.isNull();
}
@Test
public void testSendFinishedReports_whenNoReport_shouldNotReceiveResponse() throws Exception {
mService.sendFinishedReports(METRICS_CONFIG_NAME);
CarServiceUtils.runOnLooperSync(mTelemetryHandler.getLooper(), () -> { });
verify(mMockListener, never()).onResult(any(), any());
verify(mMockListener, never()).onError(any(), any());
}
@Test
public void testSendFinishedReports_whenFinalResult_shouldReceiveResult() throws Exception {
PersistableBundle finalResult = new PersistableBundle();
finalResult.putBoolean("finished", true);
mResultStore.putFinalResult(METRICS_CONFIG_NAME, finalResult);
mService.sendFinishedReports(METRICS_CONFIG_NAME);
CarServiceUtils.runOnLooperSync(mTelemetryHandler.getLooper(), () -> { });
ByteArrayOutputStream bos = new ByteArrayOutputStream();
finalResult.writeToStream(bos);
verify(mMockListener).onResult(eq(METRICS_CONFIG_NAME), eq(bos.toByteArray()));
// result should have been deleted
assertThat(mResultStore.getFinalResult(METRICS_CONFIG_NAME, false)).isNull();
}
@Test
public void testSendFinishedReports_whenError_shouldReceiveError() throws Exception {
TelemetryProto.TelemetryError error = TelemetryProto.TelemetryError.newBuilder()
.setErrorType(TelemetryProto.TelemetryError.ErrorType.LUA_RUNTIME_ERROR)
.setMessage("test error")
.build();
mResultStore.putErrorResult(METRICS_CONFIG_NAME, error);
mService.sendFinishedReports(METRICS_CONFIG_NAME);
CarServiceUtils.runOnLooperSync(mTelemetryHandler.getLooper(), () -> { });
verify(mMockListener).onError(eq(METRICS_CONFIG_NAME), eq(error.toByteArray()));
// error should have been deleted
assertThat(mResultStore.getErrorResult(METRICS_CONFIG_NAME, false)).isNull();
}
@Test
public void testSendFinishedReports_whenListenerNotSet_shouldDoNothing() {
PersistableBundle finalResult = new PersistableBundle();
finalResult.putBoolean("finished", true);
mResultStore.putFinalResult(METRICS_CONFIG_NAME, finalResult);
mService.clearListener(); // no listener = no way to send back results
mService.sendFinishedReports(METRICS_CONFIG_NAME);
CarServiceUtils.runOnLooperSync(mTelemetryHandler.getLooper(), () -> { });
// if listener is null, nothing should be done, result should still be in result store
assertThat(mResultStore.getFinalResult(METRICS_CONFIG_NAME, false).toString())
.isEqualTo(finalResult.toString());
}
}