Adding additional reporting to tests and features
Implement MetricsReportLog to generate reportlog.json.
results/latest/report-log-files/StsTestCases.reportlog.json
see json prettified with:
StsTestCases.reportlog.json | python3 -m json.tool | less
Bug: 157920496
Test: run sts
Change-Id: I23d4a50673067b15e26b20dcdbee3dfe585b0331
Merged-In: I23d4a50673067b15e26b20dcdbee3dfe585b0331
diff --git a/hostsidetests/securitybulletin/AndroidTest.xml b/hostsidetests/securitybulletin/AndroidTest.xml
index cbfee03..2690382 100644
--- a/hostsidetests/securitybulletin/AndroidTest.xml
+++ b/hostsidetests/securitybulletin/AndroidTest.xml
@@ -320,4 +320,11 @@
<option name="jar" value="CtsSecurityBulletinHostTestCases.jar" />
<option name="runtime-hint" value="15m54s" />
</test>
+
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.ReportLogCollector">
+ <option name="src-dir" value="/sdcard/report-log-files/"/>
+ <option name="dest-dir" value="report-log-files/"/>
+ <option name="temp-dir" value="temp-report-logs/"/>
+ <option name="device-dir" value="true"/>
+ </target_preparer>
</configuration>
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/AdbUtils.java b/hostsidetests/securitybulletin/src/android/security/cts/AdbUtils.java
index a3efcd9..e48fc61 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/AdbUtils.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/AdbUtils.java
@@ -17,6 +17,9 @@
package android.security.cts;
import com.android.compatibility.common.util.CrashUtils;
+import com.android.compatibility.common.util.MetricsReportLog;
+import com.android.compatibility.common.util.ResultType;
+import com.android.compatibility.common.util.ResultUnit;
import com.android.ddmlib.IShellOutputReceiver;
import com.android.ddmlib.NullOutputReceiver;
import com.android.ddmlib.CollectingOutputReceiver;
@@ -144,8 +147,30 @@
if (arguments == null) {
arguments = "";
}
- device.executeShellCommand(TMP_PATH + pocName + " " + arguments,
+
+ // since we have to return the exit status AND the poc stdout+stderr we redirect the exit
+ // status to a file temporarily
+ String exitStatusFilepath = TMP_PATH + "exit_status";
+ runCommandLine("rm " + exitStatusFilepath, device); // remove any old exit status
+ device.executeShellCommand(TMP_PATH + pocName + " " + arguments +
+ "; echo $? > " + exitStatusFilepath, // echo exit status to file
receiver, timeout, TimeUnit.SECONDS, 0);
+
+ // cat the exit status
+ String exitStatusString = runCommandLine("cat " + exitStatusFilepath, device).trim();
+
+ MetricsReportLog reportLog = SecurityTestCase.buildMetricsReportLog(device);
+ reportLog.addValue("poc_name", pocName, ResultType.NEUTRAL, ResultUnit.NONE);
+ try {
+ int exitStatus = Integer.parseInt(exitStatusString);
+ reportLog.addValue("exit_status", exitStatus, ResultType.NEUTRAL, ResultUnit.NONE);
+ } catch (NumberFormatException e) {
+ // Getting the exit status is a bonus. We can continue without it.
+ CLog.w("Could not parse exit status to int: %s", exitStatusString);
+ }
+ reportLog.submit();
+
+ runCommandLine("rm " + exitStatusFilepath, device);
}
/**
@@ -300,15 +325,21 @@
*/
public static int runCommandGetExitCode(String cmd, ITestDevice device) throws Exception {
long time = System.currentTimeMillis();
- String exitStatus = runCommandLine(
+ String exitStatusString = runCommandLine(
"(" + cmd + ") > /dev/null 2>&1; echo $?", device).trim();
time = System.currentTimeMillis() - time;
+
try {
- return Integer.parseInt(exitStatus);
+ int exitStatus = Integer.parseInt(exitStatusString);
+ MetricsReportLog reportLog = SecurityTestCase.buildMetricsReportLog(device);
+ reportLog.addValue("command", cmd, ResultType.NEUTRAL, ResultUnit.NONE);
+ reportLog.addValue("exit_status", exitStatus, ResultType.NEUTRAL, ResultUnit.NONE);
+ reportLog.submit();
+ return exitStatus;
} catch (NumberFormatException e) {
throw new IllegalArgumentException(String.format(
"Could not get the exit status (%s) for '%s' (%d ms).",
- exitStatus, cmd, time));
+ exitStatusString, cmd, time));
}
}
@@ -355,13 +386,19 @@
long time = System.currentTimeMillis();
device.executeShellCommand(cmd, receiver, timeout, TimeUnit.SECONDS, 0);
time = System.currentTimeMillis() - time;
- String exitStatus = receiver.getOutput().trim();
+ String exitStatusString = receiver.getOutput().trim();
+
try {
- return Integer.parseInt(exitStatus);
+ int exitStatus = Integer.parseInt(exitStatusString);
+ MetricsReportLog reportLog = SecurityTestCase.buildMetricsReportLog(device);
+ reportLog.addValue("poc_name", pocName, ResultType.NEUTRAL, ResultUnit.NONE);
+ reportLog.addValue("exit_status", exitStatus, ResultType.NEUTRAL, ResultUnit.NONE);
+ reportLog.submit();
+ return exitStatus;
} catch (NumberFormatException e) {
throw new IllegalArgumentException(String.format(
"Could not get the exit status (%s) for '%s' (%d ms).",
- exitStatus, cmd, time));
+ exitStatusString, cmd, time));
}
}
@@ -565,6 +602,12 @@
JSONArray crashes = CrashUtils.addAllCrashes(logcat, new JSONArray());
JSONArray securityCrashes = CrashUtils.matchSecurityCrashes(crashes, config);
+ MetricsReportLog reportLog = SecurityTestCase.buildMetricsReportLog(device);
+ reportLog.addValue("all_crashes", crashes.toString(), ResultType.NEUTRAL, ResultUnit.NONE);
+ reportLog.addValue("security_crashes", securityCrashes.toString(),
+ ResultType.NEUTRAL, ResultUnit.NONE);
+ reportLog.submit();
+
if (securityCrashes.length() == 0) {
return; // no security crashes detected
}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/SecurityTestCase.java b/hostsidetests/securitybulletin/src/android/security/cts/SecurityTestCase.java
index 2b77079..935d0fb 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/SecurityTestCase.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/SecurityTestCase.java
@@ -17,6 +17,13 @@
package android.security.cts;
import com.android.compatibility.common.tradefed.testtype.CompatibilityHostTestBase;
+import com.android.compatibility.common.util.MetricsReportLog;
+import com.android.compatibility.common.util.ResultType;
+import com.android.compatibility.common.util.ResultUnit;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.testtype.IBuildReceiver;
+import com.android.tradefed.testtype.IAbi;
+import com.android.tradefed.testtype.IAbiReceiver;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.device.NativeDevice;
@@ -24,10 +31,14 @@
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.ddmlib.Log;
+import org.junit.rules.TestName;
+import org.junit.Rule;
import org.junit.After;
import org.junit.Before;
import org.junit.runner.RunWith;
+import java.util.Map;
+import java.util.HashMap;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.util.concurrent.Callable;
@@ -49,6 +60,12 @@
private HostsideOomCatcher oomCatcher = new HostsideOomCatcher(this);
+ @Rule public TestName testName = new TestName();
+
+ private static Map<ITestDevice, IBuildInfo> sBuildInfo = new HashMap<>();
+ private static Map<ITestDevice, IAbi> sAbi = new HashMap<>();
+ private static Map<ITestDevice, String> sTestName = new HashMap<>();
+
/**
* Waits for device to be online, marks the most recent boottime of the device
*/
@@ -61,6 +78,9 @@
// Specifically time when app framework starts
oomCatcher.start();
+ sBuildInfo.put(getDevice(), mBuild);
+ sAbi.put(getDevice(), mAbi);
+ sTestName.put(getDevice(), testName.getMethodName());
}
/**
@@ -102,6 +122,19 @@
}
}
+
+ public static IBuildInfo getBuildInfo(ITestDevice device) {
+ return sBuildInfo.get(device);
+ }
+
+ public static IAbi getAbi(ITestDevice device) {
+ return sAbi.get(device);
+ }
+
+ public static String getTestName(ITestDevice device) {
+ return sTestName.get(device);
+ }
+
// TODO convert existing assertMatches*() to RegexUtils.assertMatches*()
// b/123237827
@Deprecated
@@ -189,7 +222,39 @@
* Check if a driver is present on a machine.
*/
protected boolean containsDriver(ITestDevice device, String driver) throws Exception {
- return AdbUtils.runCommandGetExitCode("test -r " + driver, device) == 0;
+ boolean containsDriver = AdbUtils.runCommandGetExitCode("test -r " + driver, device) == 0;
+
+ MetricsReportLog reportLog = buildMetricsReportLog(getDevice());
+ reportLog.addValue("path", driver, ResultType.NEUTRAL, ResultUnit.NONE);
+ reportLog.addValue("exists", containsDriver, ResultType.NEUTRAL, ResultUnit.NONE);
+ reportLog.submit();
+
+ return containsDriver;
+ }
+
+ protected static MetricsReportLog buildMetricsReportLog(ITestDevice device) {
+ IBuildInfo buildInfo = getBuildInfo(device);
+ IAbi abi = getAbi(device);
+ String testName = getTestName(device);
+
+ StackTraceElement[] stacktraces = Thread.currentThread().getStackTrace();
+ int stackDepth = 2; // 0: getStackTrace(), 1: buildMetricsReportLog, 2: caller
+ String className = stacktraces[stackDepth].getClassName();
+ String methodName = stacktraces[stackDepth].getMethodName();
+ String classMethodName = String.format("%s#%s", className, methodName);
+
+ // The stream name must be snake_case or else json formatting breaks
+ String streamName = methodName.replaceAll("(\\p{Upper})", "_$1").toLowerCase();
+
+ MetricsReportLog reportLog = new MetricsReportLog(
+ buildInfo,
+ abi.getName(),
+ classMethodName,
+ "CtsSecurityBulletinHostTestCases",
+ streamName,
+ true);
+ reportLog.addValue("test_name", testName, ResultType.NEUTRAL, ResultUnit.NONE);
+ return reportLog;
}
private long getDeviceUptime() throws DeviceNotAvailableException {