1 // Copyright 2021 The ANGLE Project Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 // 5 // AngleNativeTest: 6 // Helper to run Angle tests inside NativeActivity. 7 8 package com.android.angle.test; 9 10 import android.app.Activity; 11 import android.content.Context; 12 import android.content.Intent; 13 import android.os.Build; 14 import android.os.Bundle; 15 import android.os.Environment; 16 import android.os.Handler; 17 import android.os.Process; 18 import android.system.Os; 19 import android.util.Log; 20 import android.view.View; 21 22 import org.chromium.build.gtest_apk.NativeTestIntent; 23 24 import java.io.File; 25 26 public class AngleNativeTest 27 { 28 private static final String TAG = "NativeTest"; 29 30 private String mCommandLineFilePath; 31 private StringBuilder mCommandLineFlags = new StringBuilder(); 32 private TestStatusReporter mReporter; 33 private String mStdoutFilePath; 34 35 private static class ReportingUncaughtExceptionHandler 36 implements Thread.UncaughtExceptionHandler 37 { 38 39 private TestStatusReporter mReporter; 40 private Thread.UncaughtExceptionHandler mWrappedHandler; 41 ReportingUncaughtExceptionHandler( TestStatusReporter reporter, Thread.UncaughtExceptionHandler wrappedHandler)42 public ReportingUncaughtExceptionHandler( 43 TestStatusReporter reporter, Thread.UncaughtExceptionHandler wrappedHandler) 44 { 45 mReporter = reporter; 46 mWrappedHandler = wrappedHandler; 47 } 48 49 @Override uncaughtException(Thread thread, Throwable ex)50 public void uncaughtException(Thread thread, Throwable ex) 51 { 52 mReporter.uncaughtException(Process.myPid(), ex); 53 if (mWrappedHandler != null) mWrappedHandler.uncaughtException(thread, ex); 54 } 55 } 56 postCreate(Activity activity)57 public void postCreate(Activity activity) 58 { 59 parseArgumentsFromIntent(activity, activity.getIntent()); 60 mReporter = new TestStatusReporter(activity); 61 mReporter.testRunStarted(Process.myPid()); 62 Thread.setDefaultUncaughtExceptionHandler(new ReportingUncaughtExceptionHandler( 63 mReporter, Thread.getDefaultUncaughtExceptionHandler())); 64 65 // Enable fullscreen mode 66 // https://developer.android.com/training/system-ui/immersive#java 67 View decorView = activity.getWindow().getDecorView(); 68 if (decorView != null) 69 { 70 decorView.setSystemUiVisibility( 71 View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN); 72 } 73 } 74 parseArgumentsFromIntent(Activity activity, Intent intent)75 private void parseArgumentsFromIntent(Activity activity, Intent intent) 76 { 77 Log.i(TAG, "Extras:"); 78 Bundle extras = intent.getExtras(); 79 if (extras != null) 80 { 81 for (String s : extras.keySet()) 82 { 83 Log.i(TAG, " " + s); 84 } 85 } 86 87 mCommandLineFilePath = intent.getStringExtra(NativeTestIntent.EXTRA_COMMAND_LINE_FILE); 88 if (mCommandLineFilePath == null) 89 { 90 mCommandLineFilePath = ""; 91 } 92 else 93 { 94 File commandLineFile = new File(mCommandLineFilePath); 95 if (!commandLineFile.isAbsolute()) 96 { 97 mCommandLineFilePath = 98 Environment.getExternalStorageDirectory() + "/" + mCommandLineFilePath; 99 } 100 Log.i(TAG, "command line file path: " + mCommandLineFilePath); 101 } 102 103 String commandLineFlags = intent.getStringExtra(NativeTestIntent.EXTRA_COMMAND_LINE_FLAGS); 104 if (commandLineFlags != null) mCommandLineFlags.append(commandLineFlags); 105 106 String gtestFilter = intent.getStringExtra(NativeTestIntent.EXTRA_GTEST_FILTER); 107 if (gtestFilter != null) 108 { 109 appendCommandLineFlags("--gtest_filter=" + gtestFilter); 110 } 111 112 mStdoutFilePath = intent.getStringExtra(NativeTestIntent.EXTRA_STDOUT_FILE); 113 } 114 appendCommandLineFlags(String flags)115 private void appendCommandLineFlags(String flags) 116 { 117 mCommandLineFlags.append(" ").append(flags); 118 } 119 postStart(final Activity activity)120 public void postStart(final Activity activity) 121 { 122 final Runnable runTestsTask = new Runnable() { 123 @Override 124 public void run() 125 { 126 runTests(activity); 127 } 128 }; 129 130 // Post a task that posts a task that creates a new thread and runs tests on it. 131 // This is needed because NativeActivity processes Looper messages in native code code, 132 // which makes invoking the test runner Handler problematic. 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 = new Runnable() { 142 @Override 143 public void run() 144 { 145 new Thread(runTestsTask).start(); 146 } 147 }; 148 final Runnable postTestStarterTask = new Runnable() { 149 @Override 150 public void run() 151 { 152 handler.post(startTestThreadTask); 153 } 154 }; 155 handler.post(postTestStarterTask); 156 } 157 runTests(Activity activity)158 private void runTests(Activity activity) 159 { 160 Log.i(TAG, "runTests: " + mCommandLineFlags.toString()); 161 nativeRunTests(mCommandLineFlags.toString(), mCommandLineFilePath, mStdoutFilePath); 162 Log.i(TAG, "runTests finished"); 163 activity.finish(); 164 mReporter.testRunFinished(Process.myPid()); 165 } 166 167 // Signal a failure of the native test loader to python scripts 168 // which run tests. For example, we look for 169 // RUNNER_FAILED build/android/test_package.py. nativeTestFailed()170 private void nativeTestFailed() 171 { 172 Log.e(TAG, "[ RUNNER_FAILED ] could not load native library"); 173 } 174 nativeRunTests( String commandLineFlags, String commandLineFilePath, String stdoutFilePath)175 private native void nativeRunTests( 176 String commandLineFlags, String commandLineFilePath, String stdoutFilePath); 177 } 178