• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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