diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/libwebrtc/build/android/gtest_apk | |
parent | Initial commit. (diff) | |
download | firefox-esr-upstream.tar.xz firefox-esr-upstream.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/libwebrtc/build/android/gtest_apk')
5 files changed, 428 insertions, 0 deletions
diff --git a/third_party/libwebrtc/build/android/gtest_apk/BUILD.gn b/third_party/libwebrtc/build/android/gtest_apk/BUILD.gn new file mode 100644 index 0000000000..2a72bc47ed --- /dev/null +++ b/third_party/libwebrtc/build/android/gtest_apk/BUILD.gn @@ -0,0 +1,15 @@ +# Copyright 2020 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/config/android/rules.gni") + +android_library("native_test_instrumentation_test_runner_java") { + testonly = true + sources = [ + "java/src/org/chromium/build/gtest_apk/NativeTestInstrumentationTestRunner.java", + "java/src/org/chromium/build/gtest_apk/NativeTestIntent.java", + "java/src/org/chromium/build/gtest_apk/TestStatusIntent.java", + "java/src/org/chromium/build/gtest_apk/TestStatusReceiver.java", + ] +} diff --git a/third_party/libwebrtc/build/android/gtest_apk/java/src/org/chromium/build/gtest_apk/NativeTestInstrumentationTestRunner.java b/third_party/libwebrtc/build/android/gtest_apk/java/src/org/chromium/build/gtest_apk/NativeTestInstrumentationTestRunner.java new file mode 100644 index 0000000000..652333bdd8 --- /dev/null +++ b/third_party/libwebrtc/build/android/gtest_apk/java/src/org/chromium/build/gtest_apk/NativeTestInstrumentationTestRunner.java @@ -0,0 +1,281 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.build.gtest_apk; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.app.ActivityManager; +import android.app.Instrumentation; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.Environment; +import android.os.Handler; +import android.os.Process; +import android.text.TextUtils; +import android.util.Log; +import android.util.SparseArray; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Queue; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * An Instrumentation that runs tests based on NativeTest. + */ +public class NativeTestInstrumentationTestRunner extends Instrumentation { + private static final String EXTRA_NATIVE_TEST_ACTIVITY = + "org.chromium.native_test.NativeTestInstrumentationTestRunner.NativeTestActivity"; + private static final String EXTRA_SHARD_NANO_TIMEOUT = + "org.chromium.native_test.NativeTestInstrumentationTestRunner.ShardNanoTimeout"; + private static final String EXTRA_SHARD_SIZE_LIMIT = + "org.chromium.native_test.NativeTestInstrumentationTestRunner.ShardSizeLimit"; + private static final String EXTRA_STDOUT_FILE = + "org.chromium.native_test.NativeTestInstrumentationTestRunner.StdoutFile"; + private static final String EXTRA_TEST_LIST_FILE = + "org.chromium.native_test.NativeTestInstrumentationTestRunner.TestList"; + private static final String EXTRA_TEST = + "org.chromium.native_test.NativeTestInstrumentationTestRunner.Test"; + + private static final String TAG = "NativeTest"; + + private static final long DEFAULT_SHARD_NANO_TIMEOUT = 60 * 1000000000L; + // Default to no size limit. + private static final int DEFAULT_SHARD_SIZE_LIMIT = 0; + + private Handler mHandler = new Handler(); + private Bundle mLogBundle = new Bundle(); + private SparseArray<ShardMonitor> mMonitors = new SparseArray<ShardMonitor>(); + private String mNativeTestActivity; + private TestStatusReceiver mReceiver; + private Queue<String> mShards = new ArrayDeque<String>(); + private long mShardNanoTimeout = DEFAULT_SHARD_NANO_TIMEOUT; + private int mShardSizeLimit = DEFAULT_SHARD_SIZE_LIMIT; + private File mStdoutFile; + private Bundle mTransparentArguments; + + @Override + public void onCreate(Bundle arguments) { + Context context = getContext(); + mTransparentArguments = new Bundle(arguments); + + mNativeTestActivity = arguments.getString(EXTRA_NATIVE_TEST_ACTIVITY); + if (mNativeTestActivity == null) { + Log.e(TAG, + "Unable to find org.chromium.native_test.NativeUnitTestActivity extra on " + + "NativeTestInstrumentationTestRunner launch intent."); + finish(Activity.RESULT_CANCELED, new Bundle()); + return; + } + mTransparentArguments.remove(EXTRA_NATIVE_TEST_ACTIVITY); + + String shardNanoTimeout = arguments.getString(EXTRA_SHARD_NANO_TIMEOUT); + if (shardNanoTimeout != null) mShardNanoTimeout = Long.parseLong(shardNanoTimeout); + mTransparentArguments.remove(EXTRA_SHARD_NANO_TIMEOUT); + + String shardSizeLimit = arguments.getString(EXTRA_SHARD_SIZE_LIMIT); + if (shardSizeLimit != null) mShardSizeLimit = Integer.parseInt(shardSizeLimit); + mTransparentArguments.remove(EXTRA_SHARD_SIZE_LIMIT); + + String stdoutFile = arguments.getString(EXTRA_STDOUT_FILE); + if (stdoutFile != null) { + mStdoutFile = new File(stdoutFile); + } else { + try { + mStdoutFile = File.createTempFile( + ".temp_stdout_", ".txt", Environment.getExternalStorageDirectory()); + Log.i(TAG, "stdout file created: " + mStdoutFile.getAbsolutePath()); + } catch (IOException e) { + Log.e(TAG, "Unable to create temporary stdout file.", e); + finish(Activity.RESULT_CANCELED, new Bundle()); + return; + } + } + + mTransparentArguments.remove(EXTRA_STDOUT_FILE); + + String singleTest = arguments.getString(EXTRA_TEST); + if (singleTest != null) { + mShards.add(singleTest); + } + + String testListFilePath = arguments.getString(EXTRA_TEST_LIST_FILE); + if (testListFilePath != null) { + File testListFile = new File(testListFilePath); + try { + BufferedReader testListFileReader = + new BufferedReader(new FileReader(testListFile)); + + String test; + ArrayList<String> workingShard = new ArrayList<String>(); + while ((test = testListFileReader.readLine()) != null) { + workingShard.add(test); + if (workingShard.size() == mShardSizeLimit) { + mShards.add(TextUtils.join(":", workingShard)); + workingShard = new ArrayList<String>(); + } + } + + if (!workingShard.isEmpty()) { + mShards.add(TextUtils.join(":", workingShard)); + } + + testListFileReader.close(); + } catch (IOException e) { + Log.e(TAG, "Error reading " + testListFile.getAbsolutePath(), e); + } + } + mTransparentArguments.remove(EXTRA_TEST_LIST_FILE); + + start(); + } + + @Override + @SuppressLint("DefaultLocale") + public void onStart() { + super.onStart(); + + mReceiver = new TestStatusReceiver(); + mReceiver.register(getContext()); + mReceiver.registerCallback(new TestStatusReceiver.TestRunCallback() { + @Override + public void testRunStarted(int pid) { + if (pid != Process.myPid()) { + ShardMonitor m = new ShardMonitor(pid, System.nanoTime() + mShardNanoTimeout); + mMonitors.put(pid, m); + mHandler.post(m); + } + } + + @Override + public void testRunFinished(int pid) { + ShardMonitor m = mMonitors.get(pid); + if (m != null) { + m.stopped(); + mMonitors.remove(pid); + } + mHandler.post(new ShardEnder(pid)); + } + + @Override + public void uncaughtException(int pid, String stackTrace) { + mLogBundle.putString(Instrumentation.REPORT_KEY_STREAMRESULT, + String.format("Uncaught exception in test process (pid: %d)%n%s%n", pid, + stackTrace)); + sendStatus(0, mLogBundle); + } + }); + + mHandler.post(new ShardStarter()); + } + + /** Monitors a test shard's execution. */ + private class ShardMonitor implements Runnable { + private static final int MONITOR_FREQUENCY_MS = 1000; + + private long mExpirationNanoTime; + private int mPid; + private AtomicBoolean mStopped; + + public ShardMonitor(int pid, long expirationNanoTime) { + mPid = pid; + mExpirationNanoTime = expirationNanoTime; + mStopped = new AtomicBoolean(false); + } + + public void stopped() { + mStopped.set(true); + } + + @Override + public void run() { + if (mStopped.get()) { + return; + } + + if (isAppProcessAlive(getContext(), mPid)) { + if (System.nanoTime() > mExpirationNanoTime) { + Log.e(TAG, String.format("Test process %d timed out.", mPid)); + mHandler.post(new ShardEnder(mPid)); + return; + } else { + mHandler.postDelayed(this, MONITOR_FREQUENCY_MS); + return; + } + } + + Log.e(TAG, String.format("Test process %d died unexpectedly.", mPid)); + mHandler.post(new ShardEnder(mPid)); + } + } + + private static boolean isAppProcessAlive(Context context, int pid) { + ActivityManager activityManager = + (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + for (ActivityManager.RunningAppProcessInfo processInfo : + activityManager.getRunningAppProcesses()) { + if (processInfo.pid == pid) return true; + } + return false; + } + + protected Intent createShardMainIntent() { + Intent i = new Intent(Intent.ACTION_MAIN); + i.setComponent(new ComponentName(getContext().getPackageName(), mNativeTestActivity)); + i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + i.putExtras(mTransparentArguments); + if (mShards != null && !mShards.isEmpty()) { + String gtestFilter = mShards.remove(); + i.putExtra(NativeTestIntent.EXTRA_GTEST_FILTER, gtestFilter); + } + i.putExtra(NativeTestIntent.EXTRA_STDOUT_FILE, mStdoutFile.getAbsolutePath()); + return i; + } + + /** + * Starts the NativeTest Activity. + */ + private class ShardStarter implements Runnable { + @Override + public void run() { + getContext().startActivity(createShardMainIntent()); + } + } + + private class ShardEnder implements Runnable { + private static final int WAIT_FOR_DEATH_MILLIS = 10; + + private int mPid; + + public ShardEnder(int pid) { + mPid = pid; + } + + @Override + public void run() { + if (mPid != Process.myPid()) { + Process.killProcess(mPid); + try { + while (isAppProcessAlive(getContext(), mPid)) { + Thread.sleep(WAIT_FOR_DEATH_MILLIS); + } + } catch (InterruptedException e) { + Log.e(TAG, String.format("%d may still be alive.", mPid), e); + } + } + if (mShards != null && !mShards.isEmpty()) { + mHandler.post(new ShardStarter()); + } else { + finish(Activity.RESULT_OK, new Bundle()); + } + } + } +} diff --git a/third_party/libwebrtc/build/android/gtest_apk/java/src/org/chromium/build/gtest_apk/NativeTestIntent.java b/third_party/libwebrtc/build/android/gtest_apk/java/src/org/chromium/build/gtest_apk/NativeTestIntent.java new file mode 100644 index 0000000000..a875e9740e --- /dev/null +++ b/third_party/libwebrtc/build/android/gtest_apk/java/src/org/chromium/build/gtest_apk/NativeTestIntent.java @@ -0,0 +1,22 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.build.gtest_apk; + +/** + * Extras for intent sent by NativeTestInstrumentationTestRunner. + */ +public class NativeTestIntent { + public static final String EXTRA_COMMAND_LINE_FILE = + "org.chromium.native_test.NativeTest.CommandLineFile"; + public static final String EXTRA_COMMAND_LINE_FLAGS = + "org.chromium.native_test.NativeTest.CommandLineFlags"; + public static final String EXTRA_RUN_IN_SUB_THREAD = + "org.chromium.native_test.NativeTest.RunInSubThread"; + public static final String EXTRA_GTEST_FILTER = + "org.chromium.native_test.NativeTest.GtestFilter"; + public static final String EXTRA_STDOUT_FILE = "org.chromium.native_test.NativeTest.StdoutFile"; + public static final String EXTRA_COVERAGE_DEVICE_FILE = + "org.chromium.native_test.NativeTest.CoverageDeviceFile"; +} diff --git a/third_party/libwebrtc/build/android/gtest_apk/java/src/org/chromium/build/gtest_apk/TestStatusIntent.java b/third_party/libwebrtc/build/android/gtest_apk/java/src/org/chromium/build/gtest_apk/TestStatusIntent.java new file mode 100644 index 0000000000..520b7485b7 --- /dev/null +++ b/third_party/libwebrtc/build/android/gtest_apk/java/src/org/chromium/build/gtest_apk/TestStatusIntent.java @@ -0,0 +1,21 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.build.gtest_apk; + +/** + * Intent action and extras of broadcasts intercepted by TestStatusReceiver. + */ +public class TestStatusIntent { + public static final String ACTION_TEST_RUN_STARTED = + "org.chromium.test.reporter.TestStatusReporter.TEST_RUN_STARTED"; + public static final String ACTION_TEST_RUN_FINISHED = + "org.chromium.test.reporter.TestStatusReporter.TEST_RUN_FINISHED"; + public static final String ACTION_UNCAUGHT_EXCEPTION = + "org.chromium.test.reporter.TestStatusReporter.UNCAUGHT_EXCEPTION"; + public static final String DATA_TYPE_RESULT = "org.chromium.test.reporter/result"; + public static final String EXTRA_PID = "org.chromium.test.reporter.TestStatusReporter.PID"; + public static final String EXTRA_STACK_TRACE = + "org.chromium.test.reporter.TestStatusReporter.STACK_TRACE"; +} diff --git a/third_party/libwebrtc/build/android/gtest_apk/java/src/org/chromium/build/gtest_apk/TestStatusReceiver.java b/third_party/libwebrtc/build/android/gtest_apk/java/src/org/chromium/build/gtest_apk/TestStatusReceiver.java new file mode 100644 index 0000000000..e53900944e --- /dev/null +++ b/third_party/libwebrtc/build/android/gtest_apk/java/src/org/chromium/build/gtest_apk/TestStatusReceiver.java @@ -0,0 +1,89 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.build.gtest_apk; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; + +/** + Receives test status broadcasts sent from + {@link org.chromium.test.reporter.TestStatusReporter}. + */ +public class TestStatusReceiver extends BroadcastReceiver { + private static final String TAG = "test_reporter"; + + private final List<TestRunCallback> mTestRunCallbacks = new ArrayList<TestRunCallback>(); + + /** An IntentFilter that matches the intents that this class can receive. */ + private static final IntentFilter INTENT_FILTER; + static { + IntentFilter filter = new IntentFilter(); + filter.addAction(TestStatusIntent.ACTION_TEST_RUN_STARTED); + filter.addAction(TestStatusIntent.ACTION_TEST_RUN_FINISHED); + filter.addAction(TestStatusIntent.ACTION_UNCAUGHT_EXCEPTION); + try { + filter.addDataType(TestStatusIntent.DATA_TYPE_RESULT); + } catch (IntentFilter.MalformedMimeTypeException e) { + Log.wtf(TAG, "Invalid MIME type", e); + } + INTENT_FILTER = filter; + } + + /** A callback used when a test run has started or finished. */ + public interface TestRunCallback { + void testRunStarted(int pid); + void testRunFinished(int pid); + void uncaughtException(int pid, String stackTrace); + } + + /** Register a callback for when a test run has started or finished. */ + public void registerCallback(TestRunCallback c) { + mTestRunCallbacks.add(c); + } + + /** Register this receiver using the provided context. */ + public void register(Context c) { + c.registerReceiver(this, INTENT_FILTER); + } + + /** + * Receive a broadcast intent. + * + * @param context The Context in which the receiver is running. + * @param intent The intent received. + */ + @Override + public void onReceive(Context context, Intent intent) { + int pid = intent.getIntExtra(TestStatusIntent.EXTRA_PID, 0); + String stackTrace = intent.getStringExtra(TestStatusIntent.EXTRA_STACK_TRACE); + + switch (intent.getAction()) { + case TestStatusIntent.ACTION_TEST_RUN_STARTED: + for (TestRunCallback c : mTestRunCallbacks) { + c.testRunStarted(pid); + } + break; + case TestStatusIntent.ACTION_TEST_RUN_FINISHED: + for (TestRunCallback c : mTestRunCallbacks) { + c.testRunFinished(pid); + } + break; + case TestStatusIntent.ACTION_UNCAUGHT_EXCEPTION: + for (TestRunCallback c : mTestRunCallbacks) { + c.uncaughtException(pid, stackTrace); + } + break; + default: + Log.e(TAG, "Unrecognized intent received: " + intent.toString()); + break; + } + } +} |