Kuan-Tung Pan | fbd59a0 | 2017-08-02 19:50:04 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2017 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 | package com.android.media.tests; |
| 17 | |
| 18 | import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; |
| 19 | |
| 20 | import com.android.ddmlib.NullOutputReceiver; |
Hakan Lindh | 3e04541 | 2017-08-15 16:20:18 -0700 | [diff] [blame] | 21 | import com.android.media.tests.AudioLoopbackImageAnalyzer.Result; |
Kuan-Tung Pan | fbd59a0 | 2017-08-02 19:50:04 +0000 | [diff] [blame] | 22 | import com.android.media.tests.AudioLoopbackTestHelper.LogFileType; |
| 23 | import com.android.media.tests.AudioLoopbackTestHelper.ResultData; |
| 24 | import com.android.tradefed.config.Option; |
| 25 | import com.android.tradefed.device.DeviceNotAvailableException; |
| 26 | import com.android.tradefed.device.ITestDevice; |
| 27 | import com.android.tradefed.log.LogUtil.CLog; |
| 28 | import com.android.tradefed.result.FileInputStreamSource; |
| 29 | import com.android.tradefed.result.ITestInvocationListener; |
| 30 | import com.android.tradefed.result.InputStreamSource; |
| 31 | import com.android.tradefed.result.LogDataType; |
jdesprez | 607ef1a | 2018-02-07 16:34:53 -0800 | [diff] [blame^] | 32 | import com.android.tradefed.result.TestDescription; |
Kuan-Tung Pan | fbd59a0 | 2017-08-02 19:50:04 +0000 | [diff] [blame] | 33 | import com.android.tradefed.testtype.IDeviceTest; |
| 34 | import com.android.tradefed.testtype.IRemoteTest; |
| 35 | import com.android.tradefed.util.FileUtil; |
| 36 | import com.android.tradefed.util.RunUtil; |
| 37 | |
| 38 | import java.io.File; |
| 39 | import java.io.IOException; |
| 40 | import java.nio.file.Files; |
| 41 | import java.util.Arrays; |
| 42 | import java.util.Collections; |
| 43 | import java.util.Date; |
| 44 | import java.util.HashMap; |
| 45 | import java.util.List; |
| 46 | import java.util.Map; |
| 47 | import java.util.concurrent.TimeUnit; |
| 48 | |
| 49 | /** |
| 50 | * Runs Audio Latency and Audio Glitch test and reports result. |
| 51 | * |
| 52 | * <p>Strategy for Audio Latency Stress test: RUN test 1000 times. In each iteration, collect result |
| 53 | * files from device, parse and collect data in a ResultData object that also keeps track of |
| 54 | * location to test files for a particular iteration. |
| 55 | * |
| 56 | * <p>ANALYZE test results to produce statistics for 1. Latency and Confidence (Min, Max, Mean, |
| 57 | * Median) 2. Create CSV file with test run data 3. Print bad test data to host log file 4. Get |
| 58 | * number of test runs with valid data to send to dashboard 5. Produce histogram in host log file; |
| 59 | * count number of test results that fall into 1 ms wide buckets. |
| 60 | * |
| 61 | * <p>UPLOAD test results + log files from “bad” runs; i.e. runs that is missing some or all result |
| 62 | * data. |
| 63 | */ |
| 64 | public class AudioLoopbackTest implements IDeviceTest, IRemoteTest { |
| 65 | |
| 66 | //=================================================================== |
| 67 | // TEST OPTIONS |
| 68 | //=================================================================== |
| 69 | @Option(name = "run-key", description = "Run key for the test") |
| 70 | private String mRunKey = "AudioLoopback"; |
| 71 | |
| 72 | @Option(name = "sampling-freq", description = "Sampling Frequency for Loopback app") |
| 73 | private String mSamplingFreq = "48000"; |
| 74 | |
| 75 | @Option(name = "mic-source", description = "Mic Source for Loopback app") |
| 76 | private String mMicSource = "3"; |
| 77 | |
| 78 | @Option(name = "audio-thread", description = "Audio Thread for Loopback app") |
| 79 | private String mAudioThread = "1"; |
| 80 | |
| 81 | @Option( |
| 82 | name = "audio-level", |
| 83 | description = |
| 84 | "Audio Level for Loopback app. A device specific" |
| 85 | + "param which makes waveform in loopback test hit 60% to 80% range" |
| 86 | ) |
| 87 | private String mAudioLevel = "-1"; |
| 88 | |
| 89 | @Option(name = "test-type", description = "Test type to be executed") |
| 90 | private String mTestType = TESTTYPE_LATENCY_STR; |
| 91 | |
| 92 | @Option(name = "buffer-test-duration", description = "Buffer test duration in seconds") |
| 93 | private String mBufferTestDuration = "10"; |
| 94 | |
| 95 | @Option(name = "key-prefix", description = "Key Prefix for reporting") |
| 96 | private String mKeyPrefix = "48000_Mic3_"; |
| 97 | |
| 98 | @Option(name = "iterations", description = "Number of test iterations") |
| 99 | private int mIterations = 1; |
| 100 | |
| 101 | @Option(name = "baseline_latency", description = "") |
| 102 | private float mBaselineLatency = 0f; |
| 103 | |
| 104 | //=================================================================== |
| 105 | // CLASS VARIABLES |
| 106 | //=================================================================== |
| 107 | private static final Map<String, String> METRICS_KEY_MAP = createMetricsKeyMap(); |
| 108 | private Map<LogFileType, LogFileData> mFileDataKeyMap; |
| 109 | private ITestDevice mDevice; |
| 110 | private TestRunHelper mTestRunHelper; |
| 111 | private AudioLoopbackTestHelper mLoopbackTestHelper; |
| 112 | |
| 113 | //=================================================================== |
| 114 | // CONSTANTS |
| 115 | //=================================================================== |
| 116 | private static final String TESTTYPE_LATENCY_STR = "222"; |
| 117 | private static final String TESTTYPE_GLITCH_STR = "223"; |
| 118 | private static final long TIMEOUT_MS = 5 * 60 * 1000; // 5 min |
| 119 | private static final long DEVICE_SYNC_MS = 5 * 60 * 1000; // 5 min |
| 120 | private static final long POLLING_INTERVAL_MS = 5 * 1000; |
| 121 | private static final int MAX_ATTEMPTS = 3; |
| 122 | private static final int MAX_NR_OF_LOG_UPLOADS = 100; |
| 123 | |
| 124 | private static final int LATENCY_ITERATIONS_LOWER_BOUND = 1; |
| 125 | private static final int LATENCY_ITERATIONS_UPPER_BOUND = 10000; |
| 126 | private static final int GLITCH_ITERATIONS_LOWER_BOUND = 1; |
| 127 | private static final int GLITCH_ITERATIONS_UPPER_BOUND = 1; |
| 128 | |
| 129 | private static final String DEVICE_TEMP_DIR_PATH = "/sdcard/"; |
| 130 | private static final String FMT_OUTPUT_PREFIX = "output_%1$d_" + System.currentTimeMillis(); |
| 131 | private static final String FMT_DEVICE_FILENAME = FMT_OUTPUT_PREFIX + "%2$s"; |
| 132 | private static final String FMT_DEVICE_PATH = DEVICE_TEMP_DIR_PATH + FMT_DEVICE_FILENAME; |
| 133 | |
| 134 | private static final String AM_CMD = |
| 135 | "am start -n org.drrickorang.loopback/.LoopbackActivity" |
| 136 | + " --ei SF %s --es FileName %s --ei MicSource %s --ei AudioThread %s" |
| 137 | + " --ei AudioLevel %s --ei TestType %s --ei BufferTestDuration %s"; |
| 138 | |
| 139 | private static final String ERR_PARAMETER_OUT_OF_BOUNDS = |
| 140 | "Test parameter '%1$s' is out of bounds. Lower limit = %2$d, upper limit = %3$d"; |
| 141 | |
| 142 | private static final String KEY_RESULT_LATENCY_MS = "latency_ms"; |
| 143 | private static final String KEY_RESULT_LATENCY_CONFIDENCE = "latency_confidence"; |
| 144 | private static final String KEY_RESULT_RECORDER_BENCHMARK = "recorder_benchmark"; |
| 145 | private static final String KEY_RESULT_RECORDER_OUTLIER = "recorder_outliers"; |
| 146 | private static final String KEY_RESULT_PLAYER_BENCHMARK = "player_benchmark"; |
| 147 | private static final String KEY_RESULT_PLAYER_OUTLIER = "player_outliers"; |
| 148 | private static final String KEY_RESULT_NUMBER_OF_GLITCHES = "number_of_glitches"; |
| 149 | private static final String KEY_RESULT_RECORDER_BUFFER_CALLBACK = "late_recorder_callbacks"; |
| 150 | private static final String KEY_RESULT_PLAYER_BUFFER_CALLBACK = "late_player_callbacks"; |
| 151 | private static final String KEY_RESULT_GLITCHES_PER_HOUR = "glitches_per_hour"; |
| 152 | private static final String KEY_RESULT_TEST_STATUS = "test_status"; |
| 153 | private static final String KEY_RESULT_AUDIO_LEVEL = "audio_level"; |
| 154 | private static final String KEY_RESULT_RMS = "rms"; |
| 155 | private static final String KEY_RESULT_RMS_AVERAGE = "rms_average"; |
| 156 | private static final String KEY_RESULT_SAMPLING_FREQUENCY_CONFIDENCE = "sampling_frequency"; |
| 157 | private static final String KEY_RESULT_PERIOD_CONFIDENCE = "period_confidence"; |
| 158 | private static final String KEY_RESULT_SAMPLING_BLOCK_SIZE = "block_size"; |
| 159 | |
Hakan Lindh | 3e04541 | 2017-08-15 16:20:18 -0700 | [diff] [blame] | 160 | private static final String REDUCED_GLITCHES_TEST_DURATION = "600"; // 10 min |
| 161 | |
Kuan-Tung Pan | fbd59a0 | 2017-08-02 19:50:04 +0000 | [diff] [blame] | 162 | private static final LogFileType[] LATENCY_TEST_LOGS = { |
| 163 | LogFileType.RESULT, |
| 164 | LogFileType.GRAPH, |
| 165 | LogFileType.WAVE, |
| 166 | LogFileType.PLAYER_BUFFER, |
| 167 | LogFileType.PLAYER_BUFFER_HISTOGRAM, |
| 168 | LogFileType.PLAYER_BUFFER_PERIOD_TIMES, |
| 169 | LogFileType.RECORDER_BUFFER, |
| 170 | LogFileType.RECORDER_BUFFER_HISTOGRAM, |
| 171 | LogFileType.RECORDER_BUFFER_PERIOD_TIMES, |
| 172 | LogFileType.LOGCAT |
| 173 | }; |
| 174 | |
| 175 | private static final LogFileType[] GLITCH_TEST_LOGS = { |
| 176 | LogFileType.RESULT, |
| 177 | LogFileType.GRAPH, |
| 178 | LogFileType.WAVE, |
| 179 | LogFileType.PLAYER_BUFFER, |
| 180 | LogFileType.PLAYER_BUFFER_HISTOGRAM, |
| 181 | LogFileType.PLAYER_BUFFER_PERIOD_TIMES, |
| 182 | LogFileType.RECORDER_BUFFER, |
| 183 | LogFileType.RECORDER_BUFFER_HISTOGRAM, |
| 184 | LogFileType.RECORDER_BUFFER_PERIOD_TIMES, |
| 185 | LogFileType.GLITCHES_MILLIS, |
| 186 | LogFileType.HEAT_MAP, |
| 187 | LogFileType.LOGCAT |
| 188 | }; |
| 189 | |
| 190 | /** |
| 191 | * The Audio Latency and Audio Glitch test deals with many various types of log files. To be |
| 192 | * able to generate log files in a generic manner, this map is provided to get access to log |
| 193 | * file properties like log name prefix, log name file extension and log type (leveraging |
| 194 | * tradefed class LogDataType, used when uploading log). |
| 195 | */ |
| 196 | private final synchronized Map<LogFileType, LogFileData> getLogFileDataKeyMap() { |
| 197 | if (mFileDataKeyMap != null) { |
| 198 | return mFileDataKeyMap; |
| 199 | } |
| 200 | |
| 201 | final Map<LogFileType, LogFileData> result = new HashMap<LogFileType, LogFileData>(); |
| 202 | |
| 203 | // Populate dictionary with info about various types of logfiles |
| 204 | LogFileData l = new LogFileData(".txt", "result", LogDataType.TEXT); |
| 205 | result.put(LogFileType.RESULT, l); |
| 206 | |
| 207 | l = new LogFileData(".png", "graph", LogDataType.PNG); |
| 208 | result.put(LogFileType.GRAPH, l); |
| 209 | |
| 210 | l = new LogFileData(".wav", "wave", LogDataType.UNKNOWN); |
| 211 | result.put(LogFileType.WAVE, l); |
| 212 | |
| 213 | l = new LogFileData("_playerBufferPeriod.txt", "player_buffer", LogDataType.TEXT); |
| 214 | result.put(LogFileType.PLAYER_BUFFER, l); |
| 215 | |
| 216 | l = new LogFileData("_playerBufferPeriod.png", "player_buffer_histogram", LogDataType.PNG); |
| 217 | result.put(LogFileType.PLAYER_BUFFER_HISTOGRAM, l); |
| 218 | |
| 219 | String fileExtension = "_playerBufferPeriodTimes.txt"; |
| 220 | String uploadName = "player_buffer_period_times"; |
| 221 | l = new LogFileData(fileExtension, uploadName, LogDataType.TEXT); |
| 222 | result.put(LogFileType.PLAYER_BUFFER_PERIOD_TIMES, l); |
| 223 | |
| 224 | l = new LogFileData("_recorderBufferPeriod.txt", "recorder_buffer", LogDataType.TEXT); |
| 225 | result.put(LogFileType.RECORDER_BUFFER, l); |
| 226 | |
| 227 | fileExtension = "_recorderBufferPeriod.png"; |
| 228 | uploadName = "recorder_buffer_histogram"; |
| 229 | l = new LogFileData(fileExtension, uploadName, LogDataType.PNG); |
| 230 | result.put(LogFileType.RECORDER_BUFFER_HISTOGRAM, l); |
| 231 | |
| 232 | fileExtension = "_recorderBufferPeriodTimes.txt"; |
| 233 | uploadName = "recorder_buffer_period_times"; |
| 234 | l = new LogFileData(fileExtension, uploadName, LogDataType.TEXT); |
| 235 | result.put(LogFileType.RECORDER_BUFFER_PERIOD_TIMES, l); |
| 236 | |
| 237 | l = new LogFileData("_glitchMillis.txt", "glitches_millis", LogDataType.TEXT); |
| 238 | result.put(LogFileType.GLITCHES_MILLIS, l); |
| 239 | |
| 240 | |
| 241 | l = new LogFileData("_heatMap.png", "heat_map", LogDataType.PNG); |
| 242 | result.put(LogFileType.HEAT_MAP, l); |
| 243 | |
| 244 | l = new LogFileData(".txt", "logcat", LogDataType.TEXT); |
| 245 | result.put(LogFileType.LOGCAT, l); |
| 246 | |
| 247 | mFileDataKeyMap = Collections.unmodifiableMap(result); |
| 248 | return mFileDataKeyMap; |
| 249 | } |
| 250 | |
| 251 | private static final Map<String, String> createMetricsKeyMap() { |
| 252 | final Map<String, String> result = new HashMap<String, String>(); |
| 253 | |
| 254 | result.put("LatencyMs", KEY_RESULT_LATENCY_MS); |
| 255 | result.put("LatencyConfidence", KEY_RESULT_LATENCY_CONFIDENCE); |
| 256 | result.put("SF", KEY_RESULT_SAMPLING_FREQUENCY_CONFIDENCE); |
| 257 | result.put("Recorder Benchmark", KEY_RESULT_RECORDER_BENCHMARK); |
| 258 | result.put("Recorder Number of Outliers", KEY_RESULT_RECORDER_OUTLIER); |
| 259 | result.put("Player Benchmark", KEY_RESULT_PLAYER_BENCHMARK); |
| 260 | result.put("Player Number of Outliers", KEY_RESULT_PLAYER_OUTLIER); |
| 261 | result.put("Total Number of Glitches", KEY_RESULT_NUMBER_OF_GLITCHES); |
| 262 | result.put("kth% Late Recorder Buffer Callbacks", KEY_RESULT_RECORDER_BUFFER_CALLBACK); |
| 263 | result.put("kth% Late Player Buffer Callbacks", KEY_RESULT_PLAYER_BUFFER_CALLBACK); |
| 264 | result.put("Glitches Per Hour", KEY_RESULT_GLITCHES_PER_HOUR); |
| 265 | result.put("Test Status", KEY_RESULT_TEST_STATUS); |
| 266 | result.put("AudioLevel", KEY_RESULT_AUDIO_LEVEL); |
| 267 | result.put("RMS", KEY_RESULT_RMS); |
| 268 | result.put("Average", KEY_RESULT_RMS_AVERAGE); |
| 269 | result.put("PeriodConfidence", KEY_RESULT_PERIOD_CONFIDENCE); |
| 270 | result.put("BS", KEY_RESULT_SAMPLING_BLOCK_SIZE); |
| 271 | |
| 272 | return Collections.unmodifiableMap(result); |
| 273 | } |
| 274 | |
| 275 | //=================================================================== |
| 276 | // ENUMS |
| 277 | //=================================================================== |
| 278 | public enum TestType { |
| 279 | GLITCH, |
| 280 | LATENCY, |
| 281 | LATENCY_STRESS, |
| 282 | NONE |
| 283 | } |
| 284 | |
| 285 | //=================================================================== |
| 286 | // INNER CLASSES |
| 287 | //=================================================================== |
| 288 | public final class LogFileData { |
| 289 | private String fileExtension; |
| 290 | private String filePrefix; |
| 291 | private LogDataType logDataType; |
| 292 | |
| 293 | private LogFileData(String fileExtension, String filePrefix, LogDataType logDataType) { |
| 294 | this.fileExtension = fileExtension; |
| 295 | this.filePrefix = filePrefix; |
| 296 | this.logDataType = logDataType; |
| 297 | } |
| 298 | } |
| 299 | |
| 300 | //=================================================================== |
| 301 | // FUNCTIONS |
| 302 | //=================================================================== |
| 303 | |
| 304 | /** {@inheritDoc} */ |
| 305 | @Override |
| 306 | public void setDevice(ITestDevice device) { |
| 307 | mDevice = device; |
| 308 | } |
| 309 | |
| 310 | /** {@inheritDoc} */ |
| 311 | @Override |
| 312 | public ITestDevice getDevice() { |
| 313 | return mDevice; |
| 314 | } |
| 315 | |
| 316 | /** |
| 317 | * Test Entry Point |
| 318 | * |
| 319 | * <p>{@inheritDoc} |
| 320 | */ |
| 321 | @Override |
| 322 | public void run(ITestInvocationListener listener) throws DeviceNotAvailableException { |
| 323 | |
| 324 | initializeTest(listener); |
| 325 | |
| 326 | mTestRunHelper.startTest(1); |
| 327 | |
Hakan Lindh | 3e04541 | 2017-08-15 16:20:18 -0700 | [diff] [blame] | 328 | Map<String, String> metrics = null; |
Kuan-Tung Pan | fbd59a0 | 2017-08-02 19:50:04 +0000 | [diff] [blame] | 329 | try { |
| 330 | if (!verifyTestParameters()) { |
| 331 | return; |
| 332 | } |
| 333 | |
| 334 | // Stop logcat logging so we can record one logcat log per iteration |
| 335 | getDevice().stopLogcat(); |
| 336 | |
Hakan Lindh | 3e04541 | 2017-08-15 16:20:18 -0700 | [diff] [blame] | 337 | switch (getTestType()) { |
| 338 | case GLITCH: |
| 339 | runGlitchesTest(mTestRunHelper, mLoopbackTestHelper); |
| 340 | break; |
| 341 | case LATENCY: |
| 342 | case LATENCY_STRESS: |
| 343 | // Run test iterations |
| 344 | runLatencyTest(mLoopbackTestHelper, mIterations); |
| 345 | break; |
| 346 | default: |
| 347 | break; |
Kuan-Tung Pan | fbd59a0 | 2017-08-02 19:50:04 +0000 | [diff] [blame] | 348 | } |
| 349 | |
| 350 | mLoopbackTestHelper.processTestData(); |
Hakan Lindh | 3e04541 | 2017-08-15 16:20:18 -0700 | [diff] [blame] | 351 | metrics = uploadLogsReturnMetrics(listener); |
Kuan-Tung Pan | fbd59a0 | 2017-08-02 19:50:04 +0000 | [diff] [blame] | 352 | CLog.i("Uploading metrics values:\n" + Arrays.toString(metrics.entrySet().toArray())); |
| 353 | mTestRunHelper.endTest(metrics); |
Hakan Lindh | 3e04541 | 2017-08-15 16:20:18 -0700 | [diff] [blame] | 354 | } catch (TestFailureException e) { |
| 355 | CLog.i("TestRunHelper.reportFailure triggered"); |
| 356 | } finally { |
| 357 | CLog.i("Test ended - cleanup"); |
Kuan-Tung Pan | fbd59a0 | 2017-08-02 19:50:04 +0000 | [diff] [blame] | 358 | deleteAllTempFiles(); |
| 359 | getDevice().startLogcat(); |
| 360 | } |
| 361 | } |
| 362 | |
Hakan Lindh | 3e04541 | 2017-08-15 16:20:18 -0700 | [diff] [blame] | 363 | private void runLatencyTest(AudioLoopbackTestHelper loopbackTestHelper, int iterations) |
| 364 | throws DeviceNotAvailableException, TestFailureException { |
| 365 | for (int i = 0; i < iterations; i++) { |
| 366 | CLog.i("---- Iteration " + i + " of " + (iterations - 1) + " -----"); |
| 367 | |
| 368 | final ResultData d = new ResultData(); |
| 369 | d.setIteration(i); |
| 370 | Map<String, String> resultsDictionary = null; |
| 371 | resultsDictionary = runTest(d, getSingleTestTimeoutValue()); |
| 372 | loopbackTestHelper.addTestData(d, resultsDictionary, true); |
| 373 | } |
| 374 | } |
| 375 | |
| 376 | /** |
| 377 | * Glitches test, strategy: |
| 378 | * <p> |
| 379 | * |
| 380 | * <ul> |
| 381 | * <li>1. Calibrate Audio level |
| 382 | * <li>2. Run Audio Latency test until seeing good waveform |
| 383 | * <li>3. Run small Glitches test, 5-10 seconds |
| 384 | * <li>4. If numbers look good, run long Glitches test, else run reduced Glitches test |
| 385 | * </ul> |
| 386 | * |
| 387 | * @param testRunHelper |
| 388 | * @param loopbackTestHelper |
| 389 | * @throws DeviceNotAvailableException |
| 390 | * @throws TestFailureException |
| 391 | */ |
| 392 | private void runGlitchesTest(TestRunHelper testRunHelper, |
| 393 | AudioLoopbackTestHelper loopbackTestHelper) |
| 394 | throws DeviceNotAvailableException, TestFailureException { |
| 395 | final int MAX_RETRIES = 3; |
| 396 | int nrOfSuccessfulTests; |
| 397 | int counter = 0; |
| 398 | AudioLoopbackTestHelper tempTestHelper = null; |
| 399 | boolean runningReducedGlitchesTest = false; |
| 400 | |
| 401 | // Step 1: Calibrate Audio level |
| 402 | // Step 2: Run Audio Latency test until seeing good waveform |
| 403 | final int LOOPBACK_ITERATIONS = 4; |
| 404 | final String originalTestType = mTestType; |
| 405 | final String originalBufferTestDuration = mBufferTestDuration; |
| 406 | mTestType = TESTTYPE_LATENCY_STR; |
| 407 | do { |
| 408 | nrOfSuccessfulTests = 0; |
| 409 | tempTestHelper = new AudioLoopbackTestHelper(LOOPBACK_ITERATIONS); |
| 410 | runLatencyTest(tempTestHelper, LOOPBACK_ITERATIONS); |
| 411 | nrOfSuccessfulTests = tempTestHelper.processTestData(); |
| 412 | counter++; |
| 413 | } while (nrOfSuccessfulTests <= 0 && counter <= MAX_RETRIES); |
| 414 | |
| 415 | if (nrOfSuccessfulTests <= 0) { |
| 416 | testRunHelper.reportFailure("Glitch Setup failed: Latency test"); |
| 417 | } |
| 418 | |
| 419 | // Retrieve audio level from successful test |
| 420 | int audioLevel = -1; |
| 421 | List<ResultData> results = tempTestHelper.getAllTestData(); |
| 422 | for (ResultData rd : results) { |
| 423 | // Check if test passed |
| 424 | if (rd.getImageAnalyzerResult() == Result.PASS && rd.getConfidence() == 1.0) { |
| 425 | audioLevel = rd.getAudioLevel(); |
| 426 | break; |
| 427 | } |
| 428 | } |
| 429 | |
| 430 | if (audioLevel < 6) { |
| 431 | testRunHelper.reportFailure("Glitch Setup failed: Audio level not valid"); |
| 432 | } |
| 433 | |
| 434 | CLog.i("Audio Glitch: Audio level is " + audioLevel); |
| 435 | |
| 436 | // Step 3: Run small Glitches test, 5-10 seconds |
| 437 | mTestType = originalTestType; |
| 438 | mBufferTestDuration = "10"; |
| 439 | mAudioLevel = Integer.toString(audioLevel); |
| 440 | |
| 441 | counter = 0; |
| 442 | int glitches = -1; |
| 443 | do { |
| 444 | tempTestHelper = new AudioLoopbackTestHelper(1); |
| 445 | runLatencyTest(tempTestHelper, 1); |
| 446 | Map<String, String> resultsDictionary = |
| 447 | tempTestHelper.getResultDictionaryForIteration(0); |
| 448 | final String nrOfGlitches = |
| 449 | resultsDictionary.get(getMetricsKey(KEY_RESULT_NUMBER_OF_GLITCHES)); |
| 450 | glitches = Integer.parseInt(nrOfGlitches); |
| 451 | CLog.i("10 s glitch test produced " + glitches + " glitches"); |
| 452 | counter++; |
| 453 | } while (glitches > 10 || glitches < 0 && counter <= MAX_RETRIES); |
| 454 | |
| 455 | // Step 4: If numbers look good, run long Glitches test |
| 456 | if (glitches > 10 || glitches < 0) { |
| 457 | // Reduce test time and set some values to 0 once test completes |
| 458 | runningReducedGlitchesTest = true; |
| 459 | mBufferTestDuration = REDUCED_GLITCHES_TEST_DURATION; |
| 460 | } else { |
| 461 | mBufferTestDuration = originalBufferTestDuration; |
| 462 | } |
| 463 | |
| 464 | final ResultData d = new ResultData(); |
| 465 | d.setIteration(0); |
| 466 | Map<String, String> resultsDictionary = null; |
| 467 | resultsDictionary = runTest(d, getSingleTestTimeoutValue()); |
| 468 | if (runningReducedGlitchesTest) { |
| 469 | // Special treatment, we want to upload values, but also indicate that pre-test |
| 470 | // conditions failed. We will set the glitches count and zero out the rest. |
| 471 | String[] testValuesToChangeArray = new String[] { |
| 472 | KEY_RESULT_RECORDER_BENCHMARK, |
| 473 | KEY_RESULT_RECORDER_OUTLIER, |
| 474 | KEY_RESULT_PLAYER_BENCHMARK, |
| 475 | KEY_RESULT_PLAYER_OUTLIER, |
| 476 | KEY_RESULT_RECORDER_BUFFER_CALLBACK, |
| 477 | KEY_RESULT_PLAYER_BUFFER_CALLBACK |
| 478 | }; |
| 479 | |
| 480 | for (String key : testValuesToChangeArray) { |
| 481 | final String metricsKey = getMetricsKey(key); |
| 482 | if (resultsDictionary.containsKey(metricsKey)) { |
| 483 | resultsDictionary.put(metricsKey, "0"); |
| 484 | } |
| 485 | } |
| 486 | } |
| 487 | |
| 488 | loopbackTestHelper.addTestData(d, resultsDictionary, false); |
| 489 | } |
| 490 | |
Kuan-Tung Pan | fbd59a0 | 2017-08-02 19:50:04 +0000 | [diff] [blame] | 491 | private void initializeTest(ITestInvocationListener listener) |
| 492 | throws UnsupportedOperationException, DeviceNotAvailableException { |
| 493 | |
| 494 | mFileDataKeyMap = getLogFileDataKeyMap(); |
jdesprez | 607ef1a | 2018-02-07 16:34:53 -0800 | [diff] [blame^] | 495 | TestDescription testId = new TestDescription(getClass().getCanonicalName(), mRunKey); |
Kuan-Tung Pan | fbd59a0 | 2017-08-02 19:50:04 +0000 | [diff] [blame] | 496 | |
| 497 | // Allocate helpers |
| 498 | mTestRunHelper = new TestRunHelper(listener, testId); |
| 499 | mLoopbackTestHelper = new AudioLoopbackTestHelper(mIterations); |
| 500 | |
| 501 | getDevice().disableKeyguard(); |
| 502 | getDevice().waitForDeviceAvailable(DEVICE_SYNC_MS); |
| 503 | |
| 504 | getDevice().setDate(new Date()); |
| 505 | CLog.i("syncing device time to host time"); |
| 506 | } |
| 507 | |
| 508 | private Map<String, String> runTest(ResultData data, final long timeout) |
Hakan Lindh | 3e04541 | 2017-08-15 16:20:18 -0700 | [diff] [blame] | 509 | throws DeviceNotAvailableException, TestFailureException { |
Kuan-Tung Pan | fbd59a0 | 2017-08-02 19:50:04 +0000 | [diff] [blame] | 510 | |
| 511 | // start measurement and wait for result file |
| 512 | final NullOutputReceiver receiver = new NullOutputReceiver(); |
| 513 | |
| 514 | final String loopbackCmd = getTestCommand(data.getIteration()); |
| 515 | CLog.i("Loopback cmd: " + loopbackCmd); |
| 516 | |
| 517 | // Clear logcat |
| 518 | // Seems like getDevice().clearLogcat(); doesn't do anything? |
| 519 | // Do it through ADB |
| 520 | getDevice().executeAdbCommand("logcat", "-c"); |
| 521 | final long deviceTestStartTime = getDevice().getDeviceDate(); |
| 522 | |
| 523 | getDevice() |
| 524 | .executeShellCommand( |
| 525 | loopbackCmd, receiver, TIMEOUT_MS, TimeUnit.MILLISECONDS, MAX_ATTEMPTS); |
| 526 | |
| 527 | final long loopbackStartTime = System.currentTimeMillis(); |
| 528 | File loopbackReport = null; |
| 529 | |
| 530 | data.setDeviceTestStartTime(deviceTestStartTime); |
| 531 | |
| 532 | // Try to retrieve result file from device. |
| 533 | final String resultFilename = getDeviceFilename(LogFileType.RESULT, data.getIteration()); |
| 534 | do { |
| 535 | RunUtil.getDefault().sleep(POLLING_INTERVAL_MS); |
| 536 | if (getDevice().doesFileExist(resultFilename)) { |
| 537 | // Store device log files in tmp directory on Host and add to ResultData object |
| 538 | storeDeviceFilesOnHost(data); |
| 539 | final String reportFilename = data.getLogFile(LogFileType.RESULT); |
| 540 | if (reportFilename != null && !reportFilename.isEmpty()) { |
| 541 | loopbackReport = new File(reportFilename); |
| 542 | if (loopbackReport.length() > 0) { |
| 543 | break; |
| 544 | } |
| 545 | } |
| 546 | } |
| 547 | |
| 548 | data.setIsTimedOut(System.currentTimeMillis() - loopbackStartTime >= timeout); |
| 549 | } while (!data.hasLogFile(LogFileType.RESULT) && !data.isTimedOut()); |
| 550 | |
| 551 | // Grab logcat for iteration |
jdesprez | 73f529d | 2017-08-15 13:03:22 -0700 | [diff] [blame] | 552 | try (final InputStreamSource lc = getDevice().getLogcatSince(deviceTestStartTime)) { |
| 553 | saveLogcatForIteration(data, lc, data.getIteration()); |
| 554 | } |
Kuan-Tung Pan | fbd59a0 | 2017-08-02 19:50:04 +0000 | [diff] [blame] | 555 | |
| 556 | // Check if test timed out. If so, don't fail the test, but return to upper logic. |
| 557 | // We accept certain number of individual test timeouts. |
| 558 | if (data.isTimedOut()) { |
| 559 | // No device result files retrieved, so no need to parse |
| 560 | return null; |
| 561 | } |
| 562 | |
| 563 | // parse result |
| 564 | Map<String, String> loopbackResult = null; |
| 565 | |
| 566 | try { |
| 567 | loopbackResult = |
| 568 | AudioLoopbackTestHelper.parseKeyValuePairFromFile( |
| 569 | loopbackReport, METRICS_KEY_MAP, mKeyPrefix, "=", "%s: %s"); |
| 570 | populateResultData(loopbackResult, data); |
| 571 | |
| 572 | // Trust but verify, so get Audio Level from ADB and compare to value from app |
| 573 | final int adbAudioLevel = |
Hakan Lindh | 3e04541 | 2017-08-15 16:20:18 -0700 | [diff] [blame] | 574 | AudioLevelUtility.extractDeviceHeadsetLevelFromAdbShell(getDevice()); |
Hakan Lindh | 83157c8 | 2017-08-23 10:12:26 -0700 | [diff] [blame] | 575 | if (adbAudioLevel > -1 && data.getAudioLevel() != adbAudioLevel) { |
Kuan-Tung Pan | fbd59a0 | 2017-08-02 19:50:04 +0000 | [diff] [blame] | 576 | final String errMsg = |
| 577 | String.format( |
Hakan Lindh | 83157c8 | 2017-08-23 10:12:26 -0700 | [diff] [blame] | 578 | "App Audio Level (%1$d) differs from ADB level (%2$d)", |
Kuan-Tung Pan | fbd59a0 | 2017-08-02 19:50:04 +0000 | [diff] [blame] | 579 | data.getAudioLevel(), adbAudioLevel); |
| 580 | mTestRunHelper.reportFailure(errMsg); |
| 581 | } |
| 582 | } catch (final IOException ioe) { |
| 583 | CLog.e(ioe); |
| 584 | mTestRunHelper.reportFailure("I/O error while parsing Loopback result."); |
| 585 | } catch (final NumberFormatException ne) { |
| 586 | CLog.e(ne); |
| 587 | mTestRunHelper.reportFailure("Number format error parsing Loopback result."); |
| 588 | } |
| 589 | |
| 590 | return loopbackResult; |
| 591 | } |
| 592 | |
| 593 | private String getMetricsKey(final String key) { |
| 594 | return mKeyPrefix + key; |
| 595 | } |
| 596 | private final long getSingleTestTimeoutValue() { |
| 597 | return Long.parseLong(mBufferTestDuration) * 1000 + TIMEOUT_MS; |
| 598 | } |
| 599 | |
| 600 | private Map<String, String> uploadLogsReturnMetrics(ITestInvocationListener listener) |
Hakan Lindh | 3e04541 | 2017-08-15 16:20:18 -0700 | [diff] [blame] | 601 | throws DeviceNotAvailableException, TestFailureException { |
Kuan-Tung Pan | fbd59a0 | 2017-08-02 19:50:04 +0000 | [diff] [blame] | 602 | |
| 603 | // "resultDictionary" is used to post results to dashboards like BlackBox |
| 604 | // "results" contains test logs to be uploaded; i.e. to Sponge |
| 605 | |
| 606 | List<ResultData> results = null; |
| 607 | Map<String, String> resultDictionary = new HashMap<String, String>(); |
| 608 | |
| 609 | switch (getTestType()) { |
| 610 | case GLITCH: |
| 611 | resultDictionary = mLoopbackTestHelper.getResultDictionaryForIteration(0); |
| 612 | // Upload all test files to be backward compatible with old test |
| 613 | results = mLoopbackTestHelper.getAllTestData(); |
| 614 | break; |
| 615 | case LATENCY: |
| 616 | { |
| 617 | final int nrOfValidResults = mLoopbackTestHelper.processTestData(); |
| 618 | if (nrOfValidResults == 0) { |
| 619 | mTestRunHelper.reportFailure("No good data was collected"); |
| 620 | } else { |
| 621 | // use dictionary collected from single test run |
| 622 | resultDictionary = mLoopbackTestHelper.getResultDictionaryForIteration(0); |
| 623 | } |
| 624 | |
| 625 | // Upload all test files to be backward compatible with old test |
| 626 | results = mLoopbackTestHelper.getAllTestData(); |
| 627 | } |
| 628 | break; |
| 629 | case LATENCY_STRESS: |
| 630 | { |
| 631 | final int nrOfValidResults = mLoopbackTestHelper.processTestData(); |
| 632 | if (nrOfValidResults == 0) { |
| 633 | mTestRunHelper.reportFailure("No good data was collected"); |
| 634 | } else { |
| 635 | mLoopbackTestHelper.populateStressTestMetrics(resultDictionary, mKeyPrefix); |
| 636 | } |
| 637 | |
| 638 | results = mLoopbackTestHelper.getWorstResults(MAX_NR_OF_LOG_UPLOADS); |
| 639 | |
| 640 | // Save all test data in a spreadsheet style csv file for post test analysis |
| 641 | try { |
| 642 | saveResultsAsCSVFile(listener); |
| 643 | } catch (final IOException e) { |
| 644 | CLog.e(e); |
| 645 | } |
| 646 | } |
| 647 | break; |
| 648 | default: |
| 649 | break; |
| 650 | } |
| 651 | |
| 652 | // Upload relevant logs |
| 653 | for (final ResultData d : results) { |
| 654 | final LogFileType[] logFileTypes = getLogFileTypesForCurrentTest(); |
| 655 | for (final LogFileType logType : logFileTypes) { |
| 656 | uploadLog(listener, logType, d); |
| 657 | } |
| 658 | } |
| 659 | |
| 660 | return resultDictionary; |
| 661 | } |
| 662 | |
| 663 | private TestType getTestType() { |
| 664 | if (mTestType.equals(TESTTYPE_GLITCH_STR)) { |
| 665 | if (GLITCH_ITERATIONS_LOWER_BOUND <= mIterations |
| 666 | && mIterations <= GLITCH_ITERATIONS_UPPER_BOUND) { |
| 667 | return TestType.GLITCH; |
| 668 | } |
| 669 | } |
| 670 | |
| 671 | if (mTestType.equals(TESTTYPE_LATENCY_STR)) { |
| 672 | if (mIterations == 1) { |
| 673 | return TestType.LATENCY; |
| 674 | } |
| 675 | |
| 676 | if (LATENCY_ITERATIONS_LOWER_BOUND <= mIterations |
| 677 | && mIterations <= LATENCY_ITERATIONS_UPPER_BOUND) { |
| 678 | return TestType.LATENCY_STRESS; |
| 679 | } |
| 680 | } |
| 681 | |
| 682 | return TestType.NONE; |
| 683 | } |
| 684 | |
Hakan Lindh | 3e04541 | 2017-08-15 16:20:18 -0700 | [diff] [blame] | 685 | private boolean verifyTestParameters() throws TestFailureException { |
Kuan-Tung Pan | fbd59a0 | 2017-08-02 19:50:04 +0000 | [diff] [blame] | 686 | if (getTestType() != TestType.NONE) { |
| 687 | return true; |
| 688 | } |
| 689 | |
| 690 | if (mTestType.equals(TESTTYPE_GLITCH_STR) |
| 691 | && (mIterations < GLITCH_ITERATIONS_LOWER_BOUND |
| 692 | || mIterations > GLITCH_ITERATIONS_UPPER_BOUND)) { |
| 693 | final String error = |
| 694 | String.format( |
| 695 | ERR_PARAMETER_OUT_OF_BOUNDS, |
| 696 | "iterations", |
| 697 | GLITCH_ITERATIONS_LOWER_BOUND, |
| 698 | GLITCH_ITERATIONS_UPPER_BOUND); |
| 699 | mTestRunHelper.reportFailure(error); |
| 700 | return false; |
| 701 | } |
| 702 | |
| 703 | if (mTestType.equals(TESTTYPE_LATENCY_STR) |
| 704 | && (mIterations < LATENCY_ITERATIONS_LOWER_BOUND |
| 705 | || mIterations > LATENCY_ITERATIONS_UPPER_BOUND)) { |
| 706 | final String error = |
| 707 | String.format( |
| 708 | ERR_PARAMETER_OUT_OF_BOUNDS, |
| 709 | "iterations", |
| 710 | LATENCY_ITERATIONS_LOWER_BOUND, |
| 711 | LATENCY_ITERATIONS_UPPER_BOUND); |
| 712 | mTestRunHelper.reportFailure(error); |
| 713 | return false; |
| 714 | } |
| 715 | |
| 716 | return true; |
| 717 | } |
| 718 | |
| 719 | private void populateResultData(final Map<String, String> results, ResultData data) { |
| 720 | if (results == null || results.isEmpty()) { |
| 721 | return; |
| 722 | } |
| 723 | |
| 724 | String key = getMetricsKey(KEY_RESULT_LATENCY_MS); |
| 725 | if (results.containsKey(key)) { |
| 726 | data.setLatency(Float.parseFloat(results.get(key))); |
| 727 | } |
| 728 | |
| 729 | key = getMetricsKey(KEY_RESULT_LATENCY_CONFIDENCE); |
| 730 | if (results.containsKey(key)) { |
| 731 | data.setConfidence(Float.parseFloat(results.get(key))); |
| 732 | } |
| 733 | |
| 734 | key = getMetricsKey(KEY_RESULT_AUDIO_LEVEL); |
| 735 | if (results.containsKey(key)) { |
| 736 | data.setAudioLevel(Integer.parseInt(results.get(key))); |
| 737 | } |
| 738 | |
| 739 | key = getMetricsKey(KEY_RESULT_RMS); |
| 740 | if (results.containsKey(key)) { |
| 741 | data.setRMS(Float.parseFloat(results.get(key))); |
| 742 | } |
| 743 | |
| 744 | key = getMetricsKey(KEY_RESULT_RMS_AVERAGE); |
| 745 | if (results.containsKey(key)) { |
| 746 | data.setRMSAverage(Float.parseFloat(results.get(key))); |
| 747 | } |
| 748 | |
| 749 | key = getMetricsKey(KEY_RESULT_PERIOD_CONFIDENCE); |
| 750 | if (results.containsKey(key)) { |
| 751 | data.setPeriodConfidence(Float.parseFloat(results.get(key))); |
| 752 | } |
| 753 | |
| 754 | key = getMetricsKey(KEY_RESULT_SAMPLING_BLOCK_SIZE); |
| 755 | if (results.containsKey(key)) { |
| 756 | data.setBlockSize(Integer.parseInt(results.get(key))); |
| 757 | } |
| 758 | } |
| 759 | |
| 760 | private void storeDeviceFilesOnHost(ResultData data) throws DeviceNotAvailableException { |
| 761 | final int iteration = data.getIteration(); |
| 762 | for (final LogFileType log : getLogFileTypesForCurrentTest()) { |
| 763 | if (getDevice().doesFileExist(getDeviceFilename(log, iteration))) { |
| 764 | final String deviceFileName = getDeviceFilename(log, iteration); |
| 765 | final File logFile = getDevice().pullFile(deviceFileName); |
| 766 | data.setLogFile(log, logFile.getAbsolutePath()); |
| 767 | CLog.i("Delete file from device: " + deviceFileName); |
| 768 | deleteFileFromDevice(deviceFileName); |
| 769 | } |
| 770 | } |
| 771 | } |
| 772 | |
| 773 | private void deleteAllTempFiles() { |
| 774 | for (final ResultData d : mLoopbackTestHelper.getAllTestData()) { |
| 775 | final LogFileType[] logFileTypes = getLogFileTypesForCurrentTest(); |
| 776 | for (final LogFileType logType : logFileTypes) { |
| 777 | final String logFilename = d.getLogFile(logType); |
| 778 | if (logFilename == null || logFilename.isEmpty()) { |
| 779 | CLog.e("Logfile not found for LogFileType=" + logType.name()); |
| 780 | } else { |
| 781 | FileUtil.deleteFile(new File(logFilename)); |
| 782 | } |
| 783 | } |
| 784 | } |
| 785 | } |
| 786 | |
| 787 | private void deleteFileFromDevice(String deviceFileName) throws DeviceNotAvailableException { |
| 788 | getDevice().executeShellCommand("rm -f " + deviceFileName); |
| 789 | } |
| 790 | |
| 791 | private final LogFileType[] getLogFileTypesForCurrentTest() { |
| 792 | switch (getTestType()) { |
| 793 | case GLITCH: |
| 794 | return GLITCH_TEST_LOGS; |
| 795 | case LATENCY: |
| 796 | case LATENCY_STRESS: |
| 797 | return LATENCY_TEST_LOGS; |
| 798 | default: |
| 799 | return null; |
| 800 | } |
| 801 | } |
| 802 | |
| 803 | private String getKeyPrefixForIteration(int iteration) { |
| 804 | if (mIterations == 1) { |
| 805 | // If only one run, skip the iteration number |
| 806 | return mKeyPrefix; |
| 807 | } |
| 808 | return mKeyPrefix + iteration + "_"; |
| 809 | } |
| 810 | |
| 811 | private String getDeviceFilename(LogFileType key, int iteration) { |
| 812 | final Map<LogFileType, LogFileData> map = getLogFileDataKeyMap(); |
| 813 | if (map.containsKey(key)) { |
| 814 | final LogFileData data = map.get(key); |
| 815 | return String.format(FMT_DEVICE_PATH, iteration, data.fileExtension); |
| 816 | } |
| 817 | return null; |
| 818 | } |
| 819 | |
| 820 | private void uploadLog(ITestInvocationListener listener, LogFileType key, ResultData data) { |
| 821 | final Map<LogFileType, LogFileData> map = getLogFileDataKeyMap(); |
| 822 | if (!map.containsKey(key)) { |
| 823 | return; |
| 824 | } |
| 825 | |
| 826 | final LogFileData logInfo = map.get(key); |
| 827 | final String prefix = getKeyPrefixForIteration(data.getIteration()) + logInfo.filePrefix; |
| 828 | final LogDataType logDataType = logInfo.logDataType; |
| 829 | final String logFilename = data.getLogFile(key); |
| 830 | if (logFilename == null || logFilename.isEmpty()) { |
| 831 | CLog.e("Logfile not found for LogFileType=" + key.name()); |
| 832 | } else { |
| 833 | File logFile = new File(logFilename); |
jdesprez | 73f529d | 2017-08-15 13:03:22 -0700 | [diff] [blame] | 834 | try (InputStreamSource iss = new FileInputStreamSource(logFile)) { |
| 835 | listener.testLog(prefix, logDataType, iss); |
| 836 | } |
Kuan-Tung Pan | fbd59a0 | 2017-08-02 19:50:04 +0000 | [diff] [blame] | 837 | } |
| 838 | } |
| 839 | |
| 840 | private void saveLogcatForIteration(ResultData data, InputStreamSource logcat, int iteration) { |
| 841 | if (logcat == null) { |
| 842 | CLog.i("Logcat could not be saved for iteration " + iteration); |
| 843 | return; |
| 844 | } |
| 845 | |
| 846 | //create a temp file |
| 847 | File temp; |
| 848 | try { |
| 849 | temp = FileUtil.createTempFile("logcat_" + iteration + "_", ".txt"); |
| 850 | data.setLogFile(LogFileType.LOGCAT, temp.getAbsolutePath()); |
| 851 | |
| 852 | // Copy logcat data into temp file |
| 853 | Files.copy(logcat.createInputStream(), temp.toPath(), REPLACE_EXISTING); |
jdesprez | 73f529d | 2017-08-15 13:03:22 -0700 | [diff] [blame] | 854 | logcat.close(); |
Kuan-Tung Pan | fbd59a0 | 2017-08-02 19:50:04 +0000 | [diff] [blame] | 855 | } catch (final IOException e) { |
| 856 | CLog.i("Error when saving logcat for iteration=" + iteration); |
| 857 | CLog.e(e); |
| 858 | } |
| 859 | } |
| 860 | |
| 861 | private void saveResultsAsCSVFile(ITestInvocationListener listener) |
| 862 | throws DeviceNotAvailableException, IOException { |
| 863 | final File csvTmpFile = File.createTempFile("audio_test_data", "csv"); |
| 864 | mLoopbackTestHelper.writeAllResultsToCSVFile(csvTmpFile, getDevice()); |
jdesprez | 73f529d | 2017-08-15 13:03:22 -0700 | [diff] [blame] | 865 | try (InputStreamSource iss = new FileInputStreamSource(csvTmpFile)) { |
| 866 | listener.testLog("audio_test_data", LogDataType.JACOCO_CSV, iss); |
| 867 | } |
Kuan-Tung Pan | fbd59a0 | 2017-08-02 19:50:04 +0000 | [diff] [blame] | 868 | // cleanup |
Kuan-Tung Pan | fbd59a0 | 2017-08-02 19:50:04 +0000 | [diff] [blame] | 869 | csvTmpFile.delete(); |
| 870 | } |
| 871 | |
| 872 | private String getTestCommand(int currentIteration) { |
| 873 | return String.format( |
| 874 | AM_CMD, |
| 875 | mSamplingFreq, |
| 876 | String.format(FMT_OUTPUT_PREFIX, currentIteration), |
| 877 | mMicSource, |
| 878 | mAudioThread, |
| 879 | mAudioLevel, |
| 880 | mTestType, |
| 881 | mBufferTestDuration); |
| 882 | } |
| 883 | } |