blob: 79dc2f9d05f36341bae13583210733fdab5fc660 [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 com.google.common.truth.Truth.assertWithMessage;
import android.car.telemetry.MetricsConfigKey;
import android.os.PersistableBundle;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.concurrent.TimeUnit;
@RunWith(MockitoJUnitRunner.class)
public class ResultStoreTest {
private static final PersistableBundle TEST_INTERIM_BUNDLE = new PersistableBundle();
private static final PersistableBundle TEST_FINAL_BUNDLE = new PersistableBundle();
private static final TelemetryProto.TelemetryError TEST_TELEMETRY_ERROR =
TelemetryProto.TelemetryError.newBuilder().setMessage("test error").build();
private File mTestRootDir;
private File mTestInterimResultDir;
private File mTestErrorResultDir;
private File mTestFinalResultDir;
private ResultStore mResultStore;
@Before
public void setUp() throws Exception {
TEST_INTERIM_BUNDLE.putString("test key", "interim value");
TEST_FINAL_BUNDLE.putString("test key", "final value");
mTestRootDir = Files.createTempDirectory("car_telemetry_test").toFile();
mTestInterimResultDir = new File(mTestRootDir, ResultStore.INTERIM_RESULT_DIR);
mTestErrorResultDir = new File(mTestRootDir, ResultStore.ERROR_RESULT_DIR);
mTestFinalResultDir = new File(mTestRootDir, ResultStore.FINAL_RESULT_DIR);
mResultStore = new ResultStore(mTestRootDir);
}
@Test
public void testConstructor_shouldCreateResultsFolder() {
// constructor is called in setUp()
assertThat(mTestInterimResultDir.exists()).isTrue();
assertThat(mTestFinalResultDir.exists()).isTrue();
}
@Test
public void testConstructor_shouldLoadInterimResultsIntoMemory() throws Exception {
String testInterimFileName = "test_file_1";
writeBundleToFile(mTestInterimResultDir, testInterimFileName, TEST_INTERIM_BUNDLE);
mResultStore = new ResultStore(mTestRootDir);
// should compare value instead of reference
assertThat(mResultStore.getInterimResult(testInterimFileName).toString())
.isEqualTo(TEST_INTERIM_BUNDLE.toString());
}
@Test
public void testFlushToDisk_shouldRemoveStaleData() throws Exception {
File staleTestFile1 = new File(mTestInterimResultDir, "stale_test_file_1");
File staleTestFile2 = new File(mTestFinalResultDir, "stale_test_file_2");
File activeTestFile3 = new File(mTestInterimResultDir, "active_test_file_3");
writeBundleToFile(staleTestFile1, TEST_INTERIM_BUNDLE);
writeBundleToFile(staleTestFile2, TEST_FINAL_BUNDLE);
writeBundleToFile(activeTestFile3, TEST_INTERIM_BUNDLE);
long currTimeMs = System.currentTimeMillis();
staleTestFile1.setLastModified(0L); // stale
staleTestFile2.setLastModified(
currTimeMs - TimeUnit.MILLISECONDS.convert(31, TimeUnit.DAYS)); // stale
activeTestFile3.setLastModified(
currTimeMs - TimeUnit.MILLISECONDS.convert(29, TimeUnit.DAYS)); // active
mResultStore.flushToDisk();
assertThat(staleTestFile1.exists()).isFalse();
assertThat(staleTestFile2.exists()).isFalse();
assertThat(activeTestFile3.exists()).isTrue();
}
@Test
public void testPutInterimResultAndFlushToDisk_shouldReplaceExistingFile() throws Exception {
String newKey = "new key";
String newValue = "new value";
String metricsConfigName = "my_metrics_config";
writeBundleToFile(mTestInterimResultDir, metricsConfigName, TEST_INTERIM_BUNDLE);
TEST_INTERIM_BUNDLE.putString(newKey, newValue);
mResultStore.putInterimResult(metricsConfigName, TEST_INTERIM_BUNDLE);
mResultStore.flushToDisk();
PersistableBundle bundle = readBundleFromFile(mTestInterimResultDir, metricsConfigName);
assertThat(bundle.getString(newKey)).isEqualTo(newValue);
assertThat(bundle.toString()).isEqualTo(TEST_INTERIM_BUNDLE.toString());
}
@Test
public void testPutInterimResultAndFlushToDisk_shouldWriteDirtyResultsOnly() throws Exception {
File fileFoo = new File(mTestInterimResultDir, "foo");
File fileBar = new File(mTestInterimResultDir, "bar");
writeBundleToFile(fileFoo, TEST_INTERIM_BUNDLE);
writeBundleToFile(fileBar, TEST_INTERIM_BUNDLE);
mResultStore = new ResultStore(mTestRootDir); // re-load data
PersistableBundle newData = new PersistableBundle();
newData.putDouble("pi", 3.1415926);
mResultStore.putInterimResult("bar", newData); // make bar dirty
fileFoo.delete(); // delete the clean file from the file system
mResultStore.flushToDisk(); // write dirty data
// foo is a clean file that should not be written in flushToDisk()
assertThat(fileFoo.exists()).isFalse();
assertThat(readBundleFromFile(fileBar).toString()).isEqualTo(newData.toString());
}
@Test
public void testGetFinalResult_whenNoData_shouldReceiveNull() throws Exception {
String metricsConfigName = "my_metrics_config";
PersistableBundle bundle = mResultStore.getFinalResult(metricsConfigName, true);
assertThat(bundle).isNull();
}
@Test
public void testGetFinalResult_whenDataCorrupt_shouldReceiveNull() throws Exception {
String metricsConfigName = "my_metrics_config";
Files.write(new File(mTestFinalResultDir, metricsConfigName).toPath(),
"not a bundle".getBytes(StandardCharsets.UTF_8));
PersistableBundle bundle = mResultStore.getFinalResult(metricsConfigName, true);
assertThat(bundle).isNull();
}
@Test
public void testGetFinalResult_whenDeleteFlagTrue_shouldDeleteData() throws Exception {
String testFinalFileName = "my_metrics_config";
writeBundleToFile(mTestFinalResultDir, testFinalFileName, TEST_FINAL_BUNDLE);
PersistableBundle bundle = mResultStore.getFinalResult(testFinalFileName, true);
// should compare value instead of reference
assertThat(bundle.toString()).isEqualTo(TEST_FINAL_BUNDLE.toString());
assertThat(new File(mTestFinalResultDir, testFinalFileName).exists()).isFalse();
}
@Test
public void testGetFinalResult_whenDeleteFlagFalse_shouldNotDeleteData() throws Exception {
String testFinalFileName = "my_metrics_config";
writeBundleToFile(mTestFinalResultDir, testFinalFileName, TEST_FINAL_BUNDLE);
PersistableBundle bundle = mResultStore.getFinalResult(testFinalFileName, false);
// should compare value instead of reference
assertThat(bundle.toString()).isEqualTo(TEST_FINAL_BUNDLE.toString());
assertThat(new File(mTestFinalResultDir, testFinalFileName).exists()).isTrue();
}
@Test
public void testGetErrorResult_whenNoError_shouldReceiveNull() {
String metricsConfigName = "my_metrics_config";
TelemetryProto.TelemetryError error = mResultStore.getErrorResult(metricsConfigName, true);
assertThat(error).isNull();
}
@Test
public void testGetErrorResult_shouldReceiveError() throws Exception {
String metricsConfigName = "my_metrics_config";
// write serialized error object to file
Files.write(
new File(mTestErrorResultDir, metricsConfigName).toPath(),
TEST_TELEMETRY_ERROR.toByteArray());
TelemetryProto.TelemetryError error = mResultStore.getErrorResult(metricsConfigName, true);
assertThat(error).isEqualTo(TEST_TELEMETRY_ERROR);
}
@Test
public void testPutFinalResult_shouldWriteResultAndRemoveInterim() throws Exception {
String metricsConfigName = "my_metrics_config";
writeBundleToFile(mTestInterimResultDir, metricsConfigName, TEST_INTERIM_BUNDLE);
mResultStore.putFinalResult(metricsConfigName, TEST_FINAL_BUNDLE);
assertThat(mResultStore.getInterimResult(metricsConfigName)).isNull();
assertThat(new File(mTestInterimResultDir, metricsConfigName).exists()).isFalse();
assertThat(new File(mTestFinalResultDir, metricsConfigName).exists()).isTrue();
}
@Test
public void testPutErrorResult_shouldWriteErrorAndRemoveInterimResultFile() throws Exception {
String metricsConfigName = "my_metrics_config";
writeBundleToFile(mTestInterimResultDir, metricsConfigName, TEST_INTERIM_BUNDLE);
mResultStore.putErrorResult(metricsConfigName, TEST_TELEMETRY_ERROR);
assertThat(new File(mTestInterimResultDir, metricsConfigName).exists()).isFalse();
assertThat(new File(mTestErrorResultDir, metricsConfigName).exists()).isTrue();
}
@Test
public void testRemoveResult_whenInterimResult_shouldDelete() throws Exception {
String metricsConfigName = "my_metrics_config";
MetricsConfigKey key = new MetricsConfigKey(metricsConfigName, 1);
writeBundleToFile(mTestInterimResultDir, metricsConfigName, TEST_INTERIM_BUNDLE);
mResultStore.removeResult(key);
assertThat(new File(mTestInterimResultDir, metricsConfigName).exists()).isFalse();
}
@Test
public void testRemoveResult_whenFinalResult_shouldDelete() throws Exception {
String metricsConfigName = "my_metrics_config";
MetricsConfigKey key = new MetricsConfigKey(metricsConfigName, 1);
writeBundleToFile(mTestFinalResultDir, metricsConfigName, TEST_FINAL_BUNDLE);
mResultStore.removeResult(key);
assertThat(new File(mTestFinalResultDir, metricsConfigName).exists()).isFalse();
}
@Test
public void testRemoveResult_whenErrorResult_shouldDelete() throws Exception {
String metricsConfigName = "my_metrics_config";
MetricsConfigKey key = new MetricsConfigKey(metricsConfigName, 1);
writeBundleToFile(mTestErrorResultDir, metricsConfigName, TEST_FINAL_BUNDLE);
mResultStore.removeResult(key);
assertThat(new File(mTestErrorResultDir, metricsConfigName).exists()).isFalse();
}
@Test
public void testRemoveAllResults_shouldDeleteAll() throws Exception {
mResultStore.putInterimResult("config 1", TEST_INTERIM_BUNDLE);
mResultStore.putFinalResult("config 2", TEST_FINAL_BUNDLE);
mResultStore.putErrorResult("config 3", TEST_TELEMETRY_ERROR);
mResultStore.flushToDisk();
mResultStore.removeAllResults();
assertThat(mTestInterimResultDir.listFiles()).isEmpty();
assertThat(mTestFinalResultDir.listFiles()).isEmpty();
assertThat(mTestErrorResultDir.listFiles()).isEmpty();
}
private void writeBundleToFile(
File dir, String fileName, PersistableBundle persistableBundle) throws Exception {
writeBundleToFile(new File(dir, fileName), persistableBundle);
}
/**
* Writes a persistable bundle to the result directory with the given directory and file name,
* and verifies that it was successfully written.
*/
private void writeBundleToFile(
File file, PersistableBundle persistableBundle) throws Exception {
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
persistableBundle.writeToStream(byteArrayOutputStream);
Files.write(file.toPath(), byteArrayOutputStream.toByteArray());
}
assertWithMessage("bundle is not written to the result directory")
.that(file.exists()).isTrue();
}
private PersistableBundle readBundleFromFile(File dir, String fileName) throws Exception {
return readBundleFromFile(new File(dir, fileName));
}
/** Reads a persistable bundle from the given path. */
private PersistableBundle readBundleFromFile(File file) throws Exception {
try (FileInputStream fis = new FileInputStream(file)) {
return PersistableBundle.readFromStream(fis);
}
}
}