/*
* Copyright (C) 2020 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 androidx.media3.test.utils;
import static com.google.common.truth.Truth.assertWithMessage;
import static java.lang.annotation.ElementType.TYPE_USE;
import android.content.Context;
import androidx.annotation.IntDef;
import androidx.media3.common.util.Assertions;
import androidx.media3.common.util.UnstableApi;
import com.google.common.base.StandardSystemProperty;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Helper class to enable assertions based on golden-data dump files.
*
* <p>Allows the golden files to be easily updated with new data (see more info in the docs on
* {@link #DUMP_FILE_ACTION}).
*
* <p>Compatible with {@link Dumper.Dumpable} but can also be used directly with Strings generated
* through different means.
*/
@UnstableApi
public class DumpFileAsserts {
/** The default test asset directory used if no other directory is specified. */
public static final String DEFAULT_TEST_ASSET_DIRECTORY = "../test_data/src/test/assets";
private static final String DUMP_UPDATE_INSTRUCTIONS =
"To update the dump file, change DumpFileAsserts#DUMP_FILE_ACTION to WRITE_TO_LOCAL (for"
+ " Robolectric tests) or WRITE_TO_DEVICE (for instrumentation tests) and re-run the"
+ " test.";
/** Possible actions to take with the dumps passed to {@link #assertOutput}. */
@Documented
@Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef(
flag = true,
value = {COMPARE_WITH_EXISTING, WRITE_TO_LOCAL, WRITE_TO_DEVICE})
private @interface DumpFilesAction {}
/** Compare output with existing dump file. */
private static final int COMPARE_WITH_EXISTING = 0;
/**
* Write output to the project folder {@code testdata/src/test}.
*
* <p>Enabling this option works when tests are run in Android Studio. It may not work when the
* tests are run in another environment.
*/
private static final int WRITE_TO_LOCAL = 1;
/** Write output to folder {@code /storage/emulated/0/Android/data} of device. */
private static final int WRITE_TO_DEVICE = 1 << 1;
private static final @DumpFilesAction int DUMP_FILE_ACTION = COMPARE_WITH_EXISTING;
private DumpFileAsserts() {}
/**
* Asserts that the dump output of {@code actual} is equal to the contents of {@code dumpFile} in
* the {@link #DEFAULT_TEST_ASSET_DIRECTORY}.
*
* <p>If the assertion fails because of an intended change in the output or a new dump file needs
* to be created, set {@link #DUMP_FILE_ACTION} to {@link #WRITE_TO_LOCAL} for local tests and to
* {@link #WRITE_TO_DEVICE} for instrumentation tests, and run the test again. Instead of
* assertion, {@code actual} will be written to {@code dumpFile}. For instrumentation tests, this
* new dump file needs to be copied to the project asset folder manually.
*
* @param context A context.
* @param actual The actual data.
* @param dumpFile The file path of the dump file in the assets directory.
*/
public static void assertOutput(Context context, Dumper.Dumpable actual, String dumpFile)
throws IOException {
assertOutput(
context, new Dumper().add(actual).toString(), DEFAULT_TEST_ASSET_DIRECTORY, dumpFile);
}
/**
* Asserts that the dump output of {@code actual} is equal to the contents of {@code dumpFile} in
* the {@code assetDirectory}.
*
* <p>If the assertion fails because of an intended change in the output or a new dump file needs
* to be created, set {@link #DUMP_FILE_ACTION} to {@link #WRITE_TO_LOCAL} for local tests and to
* {@link #WRITE_TO_DEVICE} for instrumentation tests, and run the test again. Instead of
* assertion, {@code actual} will be written to {@code dumpFile}. For instrumentation tests, this
* new dump file needs to be copied to the project asset folder manually.
*
* @param context A context.
* @param actual The actual data.
* @param assetDirectory The directory of the assets relative to the project working directory.
* Only used when {@link #DUMP_FILE_ACTION} is set to {@link #WRITE_TO_LOCAL}.
* @param dumpFile The file path of the dump file in the assets directory.
*/
public static void assertOutput(
Context context, Dumper.Dumpable actual, String assetDirectory, String dumpFile)
throws IOException {
assertOutput(context, new Dumper().add(actual).toString(), assetDirectory, dumpFile);
}
/**
* Asserts that {@code actual} is equal to the contents of {@code dumpFile} in the {@link
* #DEFAULT_TEST_ASSET_DIRECTORY}.
*
* <p>If the assertion fails because of an intended change in the output or a new dump file needs
* to be created, set {@link #DUMP_FILE_ACTION} to {@link #WRITE_TO_LOCAL} for local tests and to
* {@link #WRITE_TO_DEVICE} for instrumentation tests, and run the test again. Instead of
* assertion, {@code actual} will be written to {@code dumpFile}. For instrumentation tests, this
* new dump file needs to be copied to the project asset folder manually.
*
* @param context A context.
* @param actual The actual data.
* @param dumpFile The file path of the dump file in the assets directory.
*/
public static void assertOutput(Context context, String actual, String dumpFile)
throws IOException {
assertOutput(context, actual, DEFAULT_TEST_ASSET_DIRECTORY, dumpFile);
}
/**
* Asserts that {@code actual} is equal to the contents of {@code dumpFile} in {@code
* assetDirectory}.
*
* <p>If the assertion fails because of an intended change in the output or a new dump file needs
* to be created, set {@link #DUMP_FILE_ACTION} to {@link #WRITE_TO_LOCAL} for local tests and to
* {@link #WRITE_TO_DEVICE} for instrumentation tests, and run the test again. Instead of
* assertion, {@code actual} will be written to {@code dumpFile}. For instrumentation tests, this
* new dump file needs to be copied to the project asset folder manually.
*
* @param context A context.
* @param actual The actual data.
* @param assetDirectory The directory of the assets relative to the project working directory.
* Only used when {@link #DUMP_FILE_ACTION} is set to {@link #WRITE_TO_LOCAL}.
* @param dumpFile The file path of the dump file in the assets directory.
*/
public static void assertOutput(
Context context, String actual, String assetDirectory, String dumpFile) throws IOException {
if (DUMP_FILE_ACTION == COMPARE_WITH_EXISTING) {
String expected;
try {
expected = TestUtil.getString(context, dumpFile);
} catch (FileNotFoundException e) {
throw new IOException("Dump file not found. " + DUMP_UPDATE_INSTRUCTIONS, e);
}
assertWithMessage(
"Actual data doesn't match dump file: %s\n%s", dumpFile, DUMP_UPDATE_INSTRUCTIONS)
.that(actual)
.isEqualTo(expected);
} else {
File file =
DUMP_FILE_ACTION == WRITE_TO_LOCAL
? new File(StandardSystemProperty.USER_DIR.value(), assetDirectory)
: context.getExternalFilesDir(null);
file = new File(file, dumpFile);
Assertions.checkStateNotNull(file.getParentFile()).mkdirs();
PrintWriter out = new PrintWriter(file);
out.print(actual);
out.close();
}
}
}