1 // Copyright 2012 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.native_test; 6 7 import android.app.Activity; 8 import android.content.Context; 9 import android.content.Intent; 10 import android.os.Bundle; 11 import android.os.Environment; 12 import android.os.Handler; 13 import android.os.Process; 14 import android.system.ErrnoException; 15 import android.system.Os; 16 17 import org.jni_zero.JNINamespace; 18 import org.jni_zero.NativeMethods; 19 20 import org.chromium.base.Log; 21 import org.chromium.base.test.util.UrlUtils; 22 import org.chromium.build.gtest_apk.NativeTestIntent; 23 import org.chromium.test.reporter.TestStatusReporter; 24 25 import java.io.File; 26 27 /** Helper to run tests inside Activity or NativeActivity. */ 28 @JNINamespace("testing::android") 29 public class NativeTest { 30 private static final String TAG = "NativeTest"; 31 32 private String mCommandLineFilePath; 33 private StringBuilder mCommandLineFlags = new StringBuilder(); 34 private TestStatusReporter mReporter; 35 private boolean mRunInSubThread; 36 private String mStdoutFilePath; 37 38 private static class ReportingUncaughtExceptionHandler 39 implements Thread.UncaughtExceptionHandler { 40 41 private TestStatusReporter mReporter; 42 private Thread.UncaughtExceptionHandler mWrappedHandler; 43 ReportingUncaughtExceptionHandler( TestStatusReporter reporter, Thread.UncaughtExceptionHandler wrappedHandler)44 public ReportingUncaughtExceptionHandler( 45 TestStatusReporter reporter, Thread.UncaughtExceptionHandler wrappedHandler) { 46 mReporter = reporter; 47 mWrappedHandler = wrappedHandler; 48 } 49 50 @Override uncaughtException(Thread thread, Throwable ex)51 public void uncaughtException(Thread thread, Throwable ex) { 52 mReporter.uncaughtException(Process.myPid(), ex); 53 if (mWrappedHandler != null) mWrappedHandler.uncaughtException(thread, ex); 54 } 55 } 56 preCreate(Activity activity)57 public void preCreate(Activity activity) { 58 String coverageDeviceFile = 59 activity.getIntent().getStringExtra(NativeTestIntent.EXTRA_COVERAGE_DEVICE_FILE); 60 if (coverageDeviceFile != null) { 61 try { 62 Os.setenv("LLVM_PROFILE_FILE", coverageDeviceFile, true); 63 } catch (ErrnoException e) { 64 Log.w(TAG, "failed to set LLVM_PROFILE_FILE", e); 65 } 66 } 67 // Set TMPDIR to make perfetto_unittests not to use /data/local/tmp as a tmp directory. 68 try { 69 Os.setenv("TMPDIR", activity.getApplicationContext().getCacheDir().getPath(), false); 70 } catch (ErrnoException e) { 71 Log.w(TAG, "failed to set TMPDIR", e); 72 } 73 } 74 postCreate(Activity activity)75 public void postCreate(Activity activity) { 76 parseArgumentsFromIntent(activity, activity.getIntent()); 77 mReporter = new TestStatusReporter(activity); 78 mReporter.testRunStarted(Process.myPid()); 79 Thread.setDefaultUncaughtExceptionHandler( 80 new ReportingUncaughtExceptionHandler( 81 mReporter, Thread.getDefaultUncaughtExceptionHandler())); 82 } 83 parseArgumentsFromIntent(Activity activity, Intent intent)84 private void parseArgumentsFromIntent(Activity activity, Intent intent) { 85 Log.i(TAG, "Extras:"); 86 Bundle extras = intent.getExtras(); 87 if (extras != null) { 88 for (String s : extras.keySet()) { 89 Log.i(TAG, " %s", s); 90 } 91 } 92 93 mCommandLineFilePath = intent.getStringExtra(NativeTestIntent.EXTRA_COMMAND_LINE_FILE); 94 if (mCommandLineFilePath == null) { 95 mCommandLineFilePath = ""; 96 } else { 97 File commandLineFile = new File(mCommandLineFilePath); 98 if (!commandLineFile.isAbsolute()) { 99 mCommandLineFilePath = 100 Environment.getExternalStorageDirectory() + "/" + mCommandLineFilePath; 101 } 102 Log.i(TAG, "command line file path: %s", mCommandLineFilePath); 103 } 104 105 String commandLineFlags = intent.getStringExtra(NativeTestIntent.EXTRA_COMMAND_LINE_FLAGS); 106 if (commandLineFlags != null) mCommandLineFlags.append(commandLineFlags); 107 108 mRunInSubThread = intent.hasExtra(NativeTestIntent.EXTRA_RUN_IN_SUB_THREAD); 109 110 String gtestFilter = intent.getStringExtra(NativeTestIntent.EXTRA_GTEST_FILTER); 111 if (gtestFilter != null) { 112 appendCommandLineFlags("--gtest_filter=" + gtestFilter); 113 } 114 115 mStdoutFilePath = intent.getStringExtra(NativeTestIntent.EXTRA_STDOUT_FILE); 116 } 117 appendCommandLineFlags(String flags)118 public void appendCommandLineFlags(String flags) { 119 mCommandLineFlags.append(" ").append(flags); 120 } 121 postStart(final Activity activity, boolean forceRunInSubThread)122 public void postStart(final Activity activity, boolean forceRunInSubThread) { 123 final Runnable runTestsTask = 124 new Runnable() { 125 @Override 126 public void run() { 127 runTests(activity); 128 } 129 }; 130 131 if (mRunInSubThread || forceRunInSubThread) { 132 // Post a task that posts a task that creates a new thread and runs tests on it. 133 134 // On L and M, the system posts a task to the main thread that prints to stdout 135 // from android::Layout (https://goo.gl/vZA38p). Chaining the subthread creation 136 // through multiple tasks executed on the main thread ensures that this task 137 // runs before we start running tests s.t. its output doesn't interfere with 138 // the test output. See crbug.com/678146 for additional context. 139 140 final Handler handler = new Handler(); 141 final Runnable startTestThreadTask = 142 new Runnable() { 143 @Override 144 public void run() { 145 new Thread(runTestsTask).start(); 146 } 147 }; 148 final Runnable postTestStarterTask = 149 new Runnable() { 150 @Override 151 public void run() { 152 handler.post(startTestThreadTask); 153 } 154 }; 155 handler.post(postTestStarterTask); 156 } else { 157 // Post a task to run the tests. This allows us to not block 158 // onCreate and still run tests on the main thread. 159 new Handler().post(runTestsTask); 160 } 161 } 162 runTests(Activity activity)163 private void runTests(Activity activity) { 164 NativeTestJni.get() 165 .runTests( 166 mCommandLineFlags.toString(), 167 mCommandLineFilePath, 168 mStdoutFilePath, 169 activity.getApplicationContext(), 170 UrlUtils.getIsolatedTestRoot()); 171 activity.finish(); 172 mReporter.testRunFinished(Process.myPid()); 173 } 174 175 // Signal a failure of the native test loader to python scripts 176 // which run tests. For example, we look for 177 // RUNNER_FAILED build/android/test_package.py. nativeTestFailed()178 private void nativeTestFailed() { 179 Log.e(TAG, "[ RUNNER_FAILED ] could not load native library"); 180 } 181 182 @NativeMethods 183 interface Natives { runTests( String commandLineFlags, String commandLineFilePath, String stdoutFilePath, Context appContext, String testDataDir)184 void runTests( 185 String commandLineFlags, 186 String commandLineFilePath, 187 String stdoutFilePath, 188 Context appContext, 189 String testDataDir); 190 } 191 } 192