1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.car.telemetry; 18 19 import static android.car.telemetry.CarTelemetryManager.STATUS_ADD_METRICS_CONFIG_ALREADY_EXISTS; 20 import static android.car.telemetry.CarTelemetryManager.STATUS_ADD_METRICS_CONFIG_PARSE_FAILED; 21 import static android.car.telemetry.CarTelemetryManager.STATUS_ADD_METRICS_CONFIG_SUCCEEDED; 22 import static android.car.telemetry.CarTelemetryManager.STATUS_ADD_METRICS_CONFIG_VERSION_TOO_OLD; 23 import static android.car.telemetry.CarTelemetryManager.STATUS_GET_METRICS_CONFIG_DOES_NOT_EXIST; 24 25 import static com.google.common.truth.Truth.assertThat; 26 27 import static org.junit.Assert.assertThrows; 28 import static org.junit.Assume.assumeTrue; 29 import static org.mockito.ArgumentMatchers.any; 30 import static org.mockito.ArgumentMatchers.anyInt; 31 import static org.mockito.ArgumentMatchers.eq; 32 import static org.mockito.ArgumentMatchers.isNull; 33 import static org.mockito.Mockito.doAnswer; 34 import static org.mockito.Mockito.mock; 35 import static org.mockito.Mockito.verify; 36 37 import android.annotation.NonNull; 38 import android.car.Car; 39 import android.car.telemetry.CarTelemetryManager; 40 import android.car.telemetry.TelemetryProto; 41 import android.util.ArrayMap; 42 43 import androidx.test.ext.junit.runners.AndroidJUnit4; 44 import androidx.test.filters.MediumTest; 45 46 import com.android.car.MockedCarTestBase; 47 48 import org.junit.Test; 49 import org.junit.runner.RunWith; 50 51 import java.util.Map; 52 import java.util.concurrent.Executor; 53 import java.util.concurrent.Executors; 54 import java.util.concurrent.Semaphore; 55 56 /** 57 * Tests the public entry points for the CarTelemetryManager. It uses a real instance of 58 * CarTelemetryService, however the service cannot find ScriptExecutor package so this class 59 * cannot test script execution and report retrieval. 60 * Tests that use a real CarTelemetryService should be in CarTelemetryManagerTest. 61 * Tests that use a spied CarTelemetryService should be in CarTelemetryManagerSpyServiceTest. 62 */ 63 @RunWith(AndroidJUnit4.class) 64 @MediumTest 65 public class CarTelemetryManagerTest extends MockedCarTestBase { 66 private static final byte[] INVALID_METRICS_CONFIG = "bad config".getBytes(); 67 private static final Executor CALLBACK_EXECUTOR = Executors.newSingleThreadExecutor(); 68 private static final String CONFIG_NAME = "my_metrics_config"; 69 private static final TelemetryProto.MetricsConfig METRICS_CONFIG_V1 = 70 TelemetryProto.MetricsConfig.newBuilder() 71 .setName(CONFIG_NAME).setVersion(1).setScript("no-op").build(); 72 private static final TelemetryProto.MetricsConfig METRICS_CONFIG_V2 = 73 METRICS_CONFIG_V1.toBuilder().setVersion(2).build(); 74 75 private final AddMetricsConfigCallbackImpl mAddMetricsConfigCallback = 76 new AddMetricsConfigCallbackImpl(); 77 78 private CarTelemetryManager mCarTelemetryManager; 79 80 @Override setUp()81 public void setUp() throws Exception { 82 super.setUp(); 83 assumeTrue(getCar().isFeatureEnabled(Car.CAR_TELEMETRY_SERVICE)); 84 85 mCarTelemetryManager = (CarTelemetryManager) getCar().getCarManager( 86 Car.CAR_TELEMETRY_SERVICE); 87 } 88 89 @Override createCarTelemetryService()90 protected CarTelemetryService createCarTelemetryService() { 91 // Forces the base class implementation to instantiate real instance of 92 // CarTelemetryService in ICarImpl. 93 return null; 94 } 95 96 @Test testAddMetricsConfig()97 public void testAddMetricsConfig() throws Exception { 98 // invalid config, should fail 99 mCarTelemetryManager.addMetricsConfig(CONFIG_NAME, INVALID_METRICS_CONFIG, 100 CALLBACK_EXECUTOR, mAddMetricsConfigCallback); 101 mAddMetricsConfigCallback.mSemaphore.acquire(); 102 assertThat(mAddMetricsConfigCallback.mAddConfigStatusMap.get(CONFIG_NAME)).isEqualTo( 103 STATUS_ADD_METRICS_CONFIG_PARSE_FAILED); 104 105 // new valid config, should succeed 106 mCarTelemetryManager.addMetricsConfig(CONFIG_NAME, METRICS_CONFIG_V1.toByteArray(), 107 CALLBACK_EXECUTOR, mAddMetricsConfigCallback); 108 mAddMetricsConfigCallback.mSemaphore.acquire(); 109 assertThat(mAddMetricsConfigCallback.mAddConfigStatusMap.get(CONFIG_NAME)).isEqualTo( 110 STATUS_ADD_METRICS_CONFIG_SUCCEEDED); 111 112 // duplicate config, should fail 113 mCarTelemetryManager.addMetricsConfig(CONFIG_NAME, METRICS_CONFIG_V1.toByteArray(), 114 CALLBACK_EXECUTOR, mAddMetricsConfigCallback); 115 mAddMetricsConfigCallback.mSemaphore.acquire(); 116 assertThat(mAddMetricsConfigCallback.mAddConfigStatusMap.get(CONFIG_NAME)).isEqualTo( 117 STATUS_ADD_METRICS_CONFIG_ALREADY_EXISTS); 118 119 // newer version of the config should replace older version 120 mCarTelemetryManager.addMetricsConfig(CONFIG_NAME, METRICS_CONFIG_V2.toByteArray(), 121 CALLBACK_EXECUTOR, mAddMetricsConfigCallback); 122 mAddMetricsConfigCallback.mSemaphore.acquire(); 123 assertThat(mAddMetricsConfigCallback.mAddConfigStatusMap.get(CONFIG_NAME)).isEqualTo( 124 STATUS_ADD_METRICS_CONFIG_SUCCEEDED); 125 126 // older version of the config should not be accepted 127 mCarTelemetryManager.addMetricsConfig(CONFIG_NAME, METRICS_CONFIG_V1.toByteArray(), 128 CALLBACK_EXECUTOR, mAddMetricsConfigCallback); 129 mAddMetricsConfigCallback.mSemaphore.acquire(); 130 assertThat(mAddMetricsConfigCallback.mAddConfigStatusMap.get(CONFIG_NAME)).isEqualTo( 131 STATUS_ADD_METRICS_CONFIG_VERSION_TOO_OLD); 132 } 133 134 @Test testAddMetricsConfig_invalidFieldInConfig_shouldFail()135 public void testAddMetricsConfig_invalidFieldInConfig_shouldFail() throws Exception { 136 // configure a bad publisher, read interval is not allowed to be less than 1 137 TelemetryProto.Publisher.Builder badPublisher = 138 TelemetryProto.Publisher.newBuilder().setMemory( 139 TelemetryProto.MemoryPublisher.newBuilder().setReadIntervalSec(-1)); 140 TelemetryProto.Subscriber.Builder badSubscriber = 141 TelemetryProto.Subscriber.newBuilder() 142 .setHandler("handler_fn_1") 143 .setPublisher(badPublisher); 144 TelemetryProto.MetricsConfig config = 145 METRICS_CONFIG_V1.toBuilder().addSubscribers(badSubscriber).build(); 146 147 mCarTelemetryManager.addMetricsConfig( 148 CONFIG_NAME, config.toByteArray(), CALLBACK_EXECUTOR, mAddMetricsConfigCallback); 149 150 mAddMetricsConfigCallback.mSemaphore.acquire(); 151 assertThat(mAddMetricsConfigCallback.mAddConfigStatusMap.get(CONFIG_NAME)).isEqualTo( 152 STATUS_ADD_METRICS_CONFIG_PARSE_FAILED); 153 } 154 155 @Test testSetClearListener()156 public void testSetClearListener() { 157 CarTelemetryManager.ReportReadyListener listener = metricsConfigName -> { }; 158 159 // test clearReportReadyListener, should not error 160 mCarTelemetryManager.setReportReadyListener(CALLBACK_EXECUTOR, listener); 161 162 // setListener multiple times should fail 163 assertThrows(IllegalStateException.class, 164 () -> mCarTelemetryManager.setReportReadyListener(CALLBACK_EXECUTOR, listener)); 165 166 // test clearReportReadyListener, should not error 167 mCarTelemetryManager.clearReportReadyListener(); 168 mCarTelemetryManager.setReportReadyListener(CALLBACK_EXECUTOR, listener); 169 } 170 171 @Test testGetFinishedReport_noSuchConfig()172 public void testGetFinishedReport_noSuchConfig() throws Exception { 173 Semaphore semaphore = new Semaphore(0); 174 CarTelemetryManager.MetricsReportCallback callback = mock( 175 CarTelemetryManager.MetricsReportCallback.class); 176 doAnswer((invocation) -> { 177 semaphore.release(); 178 return null; 179 }).when(callback).onResult(any(), any(), any(), anyInt()); 180 181 mCarTelemetryManager.getFinishedReport(CONFIG_NAME, CALLBACK_EXECUTOR, callback); 182 183 semaphore.acquire(); 184 verify(callback).onResult( 185 eq(CONFIG_NAME), 186 isNull(), 187 isNull(), 188 eq(STATUS_GET_METRICS_CONFIG_DOES_NOT_EXIST)); 189 } 190 191 private static final class AddMetricsConfigCallbackImpl 192 implements CarTelemetryManager.AddMetricsConfigCallback { 193 194 private Semaphore mSemaphore = new Semaphore(0); 195 private Map<String, Integer> mAddConfigStatusMap = new ArrayMap<>(); 196 197 @Override onAddMetricsConfigStatus(@onNull String metricsConfigName, int statusCode)198 public void onAddMetricsConfigStatus(@NonNull String metricsConfigName, int statusCode) { 199 mAddConfigStatusMap.put(metricsConfigName, statusCode); 200 mSemaphore.release(); 201 } 202 } 203 } 204