Add exception information to AndroidTest analysis file.

Tested:
  Verified that the additional information is available through
  instrumentation tests, as well as via manual testing.

#mse-bug-week

PiperOrigin-RevId: 429038695
This commit is contained in:
samrobinson 2022-02-16 15:10:32 +00:00 committed by Ian Baker
parent 677c5dc6d4
commit 1af841698f

View File

@ -15,8 +15,8 @@
*/ */
package androidx.media3.transformer; package androidx.media3.transformer;
import static androidx.media3.common.util.Assertions.checkNotNull;
import static androidx.media3.common.util.Assertions.checkState; import static androidx.media3.common.util.Assertions.checkState;
import static com.google.common.truth.Truth.assertWithMessage;
import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.concurrent.TimeUnit.SECONDS;
import android.content.Context; import android.content.Context;
@ -25,11 +25,13 @@ import android.os.Build;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.media3.common.C; import androidx.media3.common.C;
import androidx.media3.common.MediaItem; import androidx.media3.common.MediaItem;
import androidx.media3.common.util.Log;
import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.platform.app.InstrumentationRegistry;
import java.io.File; import java.io.File;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import org.checkerframework.checker.nullness.compatqual.NullableType; import org.checkerframework.checker.nullness.compatqual.NullableType;
import org.json.JSONException; import org.json.JSONException;
@ -43,21 +45,42 @@ public final class AndroidTestUtil {
"https://storage.googleapis.com/exoplayer-test-media-1/mp4/android-screens-10s.mp4"; "https://storage.googleapis.com/exoplayer-test-media-1/mp4/android-screens-10s.mp4";
/** /**
* Transforms the {@code uriString} with the {@link Transformer}. * Transforms the {@code uriString} with the {@link Transformer}, saving a summary of the
* transformation to the application cache.
* *
* @param context The {@link Context}. * @param context The {@link Context}.
* @param testId An identifier for the test. * @param testId An identifier for the test.
* @param transformer The {@link Transformer} that performs the transformation. * @param transformer The {@link Transformer} that performs the transformation.
* @param uriString The uri (as a {@link String}) that will be transformed. * @param uriString The uri (as a {@link String}) that will be transformed.
* @param timeoutSeconds The transformer timeout. An assertion confirms this is not exceeded. * @param timeoutSeconds The transformer timeout. An exception is thrown if this is exceeded.
* @return The {@link TransformationResult}. * @return The {@link TransformationResult}.
* @throws Exception The cause of the transformation not completing. * @throws Exception The cause of the transformation not completing.
*/ */
public static TransformationResult runTransformer( public static TransformationResult runTransformer(
Context context, String testId, Transformer transformer, String uriString, int timeoutSeconds) Context context, String testId, Transformer transformer, String uriString, int timeoutSeconds)
throws Exception { throws Exception {
AtomicReference<@NullableType Exception> exceptionReference = new AtomicReference<>(); JSONObject resultJson = new JSONObject();
AtomicReference<TransformationResult> resultReference = new AtomicReference<>(); try {
TransformationResult transformationResult =
runTransformerInternal(context, testId, transformer, uriString, timeoutSeconds);
resultJson.put("transformationResult", getTransformationResultJson(transformationResult));
return transformationResult;
} catch (Exception e) {
resultJson.put("exception", getExceptionJson(e));
throw e;
} finally {
writeTestSummaryToFile(context, testId, resultJson);
}
}
private static TransformationResult runTransformerInternal(
Context context, String testId, Transformer transformer, String uriString, int timeoutSeconds)
throws Exception {
AtomicReference<@NullableType TransformationException> transformationExceptionReference =
new AtomicReference<>();
AtomicReference<@NullableType Exception> unexpectedExceptionReference = new AtomicReference<>();
AtomicReference<@NullableType TransformationResult> transformationResultReference =
new AtomicReference<>();
CountDownLatch countDownLatch = new CountDownLatch(1); CountDownLatch countDownLatch = new CountDownLatch(1);
Transformer testTransformer = Transformer testTransformer =
@ -68,14 +91,14 @@ public final class AndroidTestUtil {
@Override @Override
public void onTransformationCompleted( public void onTransformationCompleted(
MediaItem inputMediaItem, TransformationResult result) { MediaItem inputMediaItem, TransformationResult result) {
resultReference.set(result); transformationResultReference.set(result);
countDownLatch.countDown(); countDownLatch.countDown();
} }
@Override @Override
public void onTransformationError( public void onTransformationError(
MediaItem inputMediaItem, TransformationException exception) { MediaItem inputMediaItem, TransformationException exception) {
exceptionReference.set(exception); transformationExceptionReference.set(exception);
countDownLatch.countDown(); countDownLatch.countDown();
} }
}) })
@ -89,38 +112,49 @@ public final class AndroidTestUtil {
try { try {
testTransformer.startTransformation( testTransformer.startTransformation(
MediaItem.fromUri(uri), outputVideoFile.getAbsolutePath()); MediaItem.fromUri(uri), outputVideoFile.getAbsolutePath());
} catch (IOException e) { // Catch all exceptions to report. Exceptions thrown here and not caught will NOT
exceptionReference.set(e); // propagate.
} catch (Exception e) {
unexpectedExceptionReference.set(e);
countDownLatch.countDown();
} }
}); });
assertWithMessage("Transformer timed out after " + timeoutSeconds + " seconds.") if (!countDownLatch.await(timeoutSeconds, SECONDS)) {
.that(countDownLatch.await(timeoutSeconds, SECONDS)) throw new TimeoutException("Transformer timed out after " + timeoutSeconds + " seconds.");
.isTrue();
@Nullable Exception exception = exceptionReference.get();
if (exception != null) {
throw exception;
} }
TransformationResult result = @Nullable Exception unexpectedException = unexpectedExceptionReference.get();
resultReference.get().buildUpon().setFileSizeBytes(outputVideoFile.length()).build(); if (unexpectedException != null) {
throw unexpectedException;
writeResultToFile(context, testId, result);
return result;
} }
private static void writeResultToFile( @Nullable
Context context, String testId, TransformationResult transformationResult) TransformationException transformationException = transformationExceptionReference.get();
if (transformationException != null) {
throw transformationException;
}
// If both exceptions are null, the Transformation must have succeeded, and a
// transformationResult will be available.
return checkNotNull(transformationResultReference.get())
.buildUpon()
.setFileSizeBytes(outputVideoFile.length())
.build();
}
private static void writeTestSummaryToFile(Context context, String testId, JSONObject resultJson)
throws IOException, JSONException { throws IOException, JSONException {
resultJson.put("testId", testId).put("device", getDeviceJson());
String analysisContents = resultJson.toString(/* indentSpaces= */ 2);
// Log contents as well as writing to file, for easier visibility on individual device testing.
Log.i("TransformerAndroidTest_" + testId, analysisContents);
File analysisFile = createExternalCacheFile(context, /* fileName= */ testId + "-result.txt"); File analysisFile = createExternalCacheFile(context, /* fileName= */ testId + "-result.txt");
String analysisContent =
new JSONObject()
.put("testId", testId)
.put("device", getDeviceJson())
.put("transformationResult", getTransformationResultJson(transformationResult))
.toString(/* indentSpaces= */ 2);
try (FileWriter fileWriter = new FileWriter(analysisFile)) { try (FileWriter fileWriter = new FileWriter(analysisFile)) {
fileWriter.write(analysisContent); fileWriter.write(analysisContents);
} }
} }
@ -154,5 +188,16 @@ public final class AndroidTestUtil {
return transformationResultJson; return transformationResultJson;
} }
private static JSONObject getExceptionJson(Exception exception) throws JSONException {
JSONObject exceptionJson = new JSONObject();
exceptionJson.put("message", exception.getMessage());
exceptionJson.put("type", exception.getClass());
if (exception instanceof TransformationException) {
exceptionJson.put("errorCode", ((TransformationException) exception).errorCode);
}
exceptionJson.put("stackTrace", Log.getThrowableString(exception));
return exceptionJson;
}
private AndroidTestUtil() {} private AndroidTestUtil() {}
} }