Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bc6e650b authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "More precise test summary" into main

parents 77754502 384da4bc
Loading
Loading
Loading
Loading
+3 −17
Original line number Diff line number Diff line
@@ -26,12 +26,10 @@ import android.annotation.Nullable;
import android.os.Bundle;
import android.platform.test.annotations.RavenwoodTestRunnerInitializing;
import android.platform.test.annotations.internal.InnerRunner;
import android.platform.test.ravenwood.RavenwoodTestStats.Result;
import android.util.Log;

import androidx.test.platform.app.InstrumentationRegistry;

import org.junit.AssumptionViolatedException;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runner.Runner;
@@ -171,10 +169,11 @@ public final class RavenwoodAwareTestRunner extends RavenwoodAwareTestRunnerBase
        final var notifier = new RavenwoodRunNotifier(realNotifier);
        final var description = getDescription();

        RavenwoodTestStats.getInstance().attachToRunNotifier(notifier);

        if (mRealRunner instanceof ClassSkippingTestRunner) {
            mRealRunner.run(notifier);
            Log.i(TAG, "onClassSkipped: description=" + description);
            RavenwoodTestStats.getInstance().onClassSkipped(description);
            mRealRunner.run(notifier);
            return;
        }

@@ -205,7 +204,6 @@ public final class RavenwoodAwareTestRunner extends RavenwoodAwareTestRunnerBase

            if (!skipRunnerHook) {
                try {
                    RavenwoodTestStats.getInstance().onClassFinished(description);
                    mState.exitTestClass();
                } catch (Throwable th) {
                    notifier.reportAfterTestFailure(th);
@@ -295,8 +293,6 @@ public final class RavenwoodAwareTestRunner extends RavenwoodAwareTestRunnerBase
        // method-level annotations here.
        if (scope == Scope.Instance && order == Order.Outer) {
            if (!RavenwoodEnablementChecker.shouldEnableOnRavenwood(description, true)) {
                RavenwoodTestStats.getInstance().onTestFinished(
                        classDescription, description, Result.Skipped);
                return false;
            }
        }
@@ -317,16 +313,6 @@ public final class RavenwoodAwareTestRunner extends RavenwoodAwareTestRunnerBase
            // End of a test method.
            mState.exitTestMethod();

            final Result result;
            if (th == null) {
                result = Result.Passed;
            } else if (th instanceof AssumptionViolatedException) {
                result = Result.Skipped;
            } else {
                result = Result.Failed;
            }

            RavenwoodTestStats.getInstance().onTestFinished(classDescription, description, result);
        }

        // If RUN_DISABLED_TESTS is set, and the method did _not_ throw, make it an error.
+2 −2
Original line number Diff line number Diff line
@@ -201,7 +201,7 @@ public class RavenwoodRuntimeEnvironmentController {
     */
    public static void init(RavenwoodAwareTestRunner runner) {
        if (RAVENWOOD_VERBOSE_LOGGING) {
            Log.i(TAG, "init() called here: " + runner, new RuntimeException("STACKTRACE"));
            Log.v(TAG, "init() called here: " + runner, new RuntimeException("STACKTRACE"));
        }
        if (sRunner == runner) {
            return;
@@ -314,7 +314,7 @@ public class RavenwoodRuntimeEnvironmentController {
     */
    public static void reset() {
        if (RAVENWOOD_VERBOSE_LOGGING) {
            Log.i(TAG, "reset() called here", new RuntimeException("STACKTRACE"));
            Log.v(TAG, "reset() called here", new RuntimeException("STACKTRACE"));
        }
        if (sRunner == null) {
            throw new RavenwoodRuntimeException("Internal error: reset() already called");
+109 −52
Original line number Diff line number Diff line
@@ -18,6 +18,9 @@ package android.platform.test.ravenwood;
import android.util.Log;

import org.junit.runner.Description;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunListener;
import org.junit.runner.notification.RunNotifier;

import java.io.File;
import java.io.IOException;
@@ -27,7 +30,7 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

/**
@@ -39,7 +42,7 @@ import java.util.Map;
 */
public class RavenwoodTestStats {
    private static final String TAG = "RavenwoodTestStats";
    private static final String HEADER = "Module,Class,ClassDesc,Passed,Failed,Skipped";
    private static final String HEADER = "Module,Class,OuterClass,Passed,Failed,Skipped";

    private static RavenwoodTestStats sInstance;

@@ -66,7 +69,7 @@ public class RavenwoodTestStats {
    private final PrintWriter mOutputWriter;
    private final String mTestModuleName;

    public final Map<Description, Map<Description, Result>> mStats = new HashMap<>();
    public final Map<String, Map<String, Result>> mStats = new LinkedHashMap<>();

    /** Ctor */
    public RavenwoodTestStats() {
@@ -115,75 +118,129 @@ public class RavenwoodTestStats {
        return cwd.getName();
    }

    private void addResult(Description classDescription, Description methodDescription,
    private void addResult(String className, String methodName,
            Result result) {
        mStats.compute(classDescription, (classDesc, value) -> {
        mStats.compute(className, (className_, value) -> {
            if (value == null) {
                value = new HashMap<>();
                value = new LinkedHashMap<>();
            }
            // If the result is already set, don't overwrite it.
            if (!value.containsKey(methodName)) {
                value.put(methodName, result);
            }
            value.put(methodDescription, result);
            return value;
        });
    }

    /**
     * Call it when a test class is skipped.
     */
    public void onClassSkipped(Description classDescription) {
        addResult(classDescription, Description.EMPTY, Result.Skipped);
        onClassFinished(classDescription);
    }

    /**
     * Call it when a test method is finished.
     */
    public void onTestFinished(Description classDescription, Description testDescription,
            Result result) {
        addResult(classDescription, testDescription, result);
    private void onTestFinished(String className, String testName, Result result) {
        addResult(className, testName, result);
    }

    /**
     * Call it when a test class is finished.
     * Dump all the results and clear it.
     */
    public void onClassFinished(Description classDescription) {
    private void dumpAllAndClear() {
        for (var entry : mStats.entrySet()) {
            int passed = 0;
            int skipped = 0;
            int failed = 0;
        var stats = mStats.get(classDescription);
        if (stats == null) {
            return;
        }
        for (var e : stats.values()) {
            var className = entry.getKey();

            for (var e : entry.getValue().values()) {
                switch (e) {
                case Passed: passed++; break;
                case Skipped: skipped++; break;
                case Failed: failed++; break;
                    case Passed:
                        passed++;
                        break;
                    case Skipped:
                        skipped++;
                        break;
                    case Failed:
                        failed++;
                        break;
                }
            }

        var testClass = extractTestClass(classDescription);

            mOutputWriter.printf("%s,%s,%s,%d,%d,%d\n",
                mTestModuleName, (testClass == null ? "?" : testClass.getCanonicalName()),
                classDescription, passed, failed, skipped);
                    mTestModuleName, className, getOuterClassName(className),
                    passed, failed, skipped);
        }
        mOutputWriter.flush();
        mStats.clear();
    }

    /**
     * Try to extract the class from a description, which is needed because
     * ParameterizedAndroidJunit4's description doesn't contain a class.
     */
    private Class<?> extractTestClass(Description desc) {
        if (desc.getTestClass() != null) {
            return desc.getTestClass();
    private static String getOuterClassName(String className) {
        // Just delete the '$', because I'm not sure if the className we get here is actaully a
        // valid class name that does exist. (it might have a parameter name, etc?)
        int p = className.indexOf('$');
        if (p < 0) {
            return className;
        }
        return className.substring(0, p);
    }
        // Look into the children.
        for (var child : desc.getChildren()) {
            var fromChild = extractTestClass(child);
            if (fromChild != null) {
                return fromChild;

    public void attachToRunNotifier(RunNotifier notifier) {
        notifier.addListener(mRunListener);
    }

    private final RunListener mRunListener = new RunListener() {
        @Override
        public void testSuiteStarted(Description description) {
            Log.d(TAG, "testSuiteStarted: " + description);
        }

        @Override
        public void testSuiteFinished(Description description) {
            Log.d(TAG, "testSuiteFinished: " + description);
        }
        return null;

        @Override
        public void testRunStarted(Description description) {
            Log.d(TAG, "testRunStarted: " + description);
        }

        @Override
        public void testRunFinished(org.junit.runner.Result result) {
            Log.d(TAG, "testRunFinished: " + result);

            dumpAllAndClear();
        }

        @Override
        public void testStarted(Description description) {
            Log.d(TAG, "  testStarted: " + description);
        }

        @Override
        public void testFinished(Description description) {
            Log.d(TAG, "  testFinished: " + description);

            // Send "Passed", but if there's already another result sent for this, this won't
            // override it.
            onTestFinished(description.getClassName(), description.getMethodName(), Result.Passed);
        }

        @Override
        public void testFailure(Failure failure) {
            Log.d(TAG, "    testFailure: " + failure);

            var description = failure.getDescription();
            onTestFinished(description.getClassName(), description.getMethodName(), Result.Failed);
        }

        @Override
        public void testAssumptionFailure(Failure failure) {
            Log.d(TAG, "    testAssumptionFailure: " + failure);
            var description = failure.getDescription();
            onTestFinished(description.getClassName(), description.getMethodName(), Result.Skipped);
        }

        @Override
        public void testIgnored(Description description) {
            Log.d(TAG, "    testIgnored: " + description);
            onTestFinished(description.getClassName(), description.getMethodName(), Result.Skipped);
        }
    };
}