summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorandroid-build-team Robot <android-build-team-robot@google.com>2017-09-06 07:34:42 +0000
committerandroid-build-team Robot <android-build-team-robot@google.com>2017-09-06 07:34:42 +0000
commitb7aa7cbf4b7e61ff6a12bc9f727e6b257253026a (patch)
tree800b763ebead4f650c4d7dc31e9038f00cf8dc2a
parentf989f1456af552202feeefff3689e061f9c16d12 (diff)
parent6d4aa95e1f962057c0af52be2cdf3afe296f5f44 (diff)
downloadplatform_testing-b7aa7cbf4b7e61ff6a12bc9f727e6b257253026a.tar.gz
release-request-ee600ee1-fadc-4a92-9feb-e22548c84a75-for-git_oc-mr1-release-4318546 snap-temp-L23800000099760172
Change-Id: I5533c2b2928e72dc93081ee762794637031792e3
-rw-r--r--libraries/longevity/Android.mk30
-rw-r--r--libraries/longevity/samples/Android.mk27
-rw-r--r--libraries/longevity/samples/AndroidManifest.xml26
-rw-r--r--libraries/longevity/samples/src/android/platform/longevity/samples/SimpleSuite.java47
-rw-r--r--libraries/longevity/src/android/platform/longevity/LongevitySuite.java90
-rw-r--r--libraries/longevity/src/android/platform/longevity/listeners/BatteryTerminator.java64
-rw-r--r--libraries/longevity/src/android/platform/longevity/listeners/ErrorTerminator.java34
-rw-r--r--libraries/longevity/src/android/platform/longevity/listeners/RunTerminator.java43
-rw-r--r--libraries/longevity/src/android/platform/longevity/listeners/TimeoutTerminator.java63
-rw-r--r--libraries/longevity/src/android/platform/longevity/scheduler/Iterate.java44
-rw-r--r--libraries/longevity/src/android/platform/longevity/scheduler/Scheduler.java33
-rw-r--r--libraries/longevity/src/android/platform/longevity/scheduler/Shuffle.java46
-rw-r--r--libraries/longevity/tests/Android.mk26
-rw-r--r--libraries/longevity/tests/AndroidManifest.xml26
-rw-r--r--libraries/longevity/tests/src/android/platform/longevity/LongevitySuiteTest.java47
-rw-r--r--libraries/longevity/tests/src/android/platform/longevity/listeners/BatteryTerminatorTest.java76
-rw-r--r--libraries/longevity/tests/src/android/platform/longevity/listeners/ErrorTerminatorTest.java66
-rw-r--r--libraries/longevity/tests/src/android/platform/longevity/listeners/TimeoutTerminatorTest.java70
-rw-r--r--libraries/longevity/tests/src/android/platform/longevity/scheduler/IterateTest.java79
-rw-r--r--libraries/longevity/tests/src/android/platform/longevity/scheduler/ShuffleTest.java73
20 files changed, 1010 insertions, 0 deletions
diff --git a/libraries/longevity/Android.mk b/libraries/longevity/Android.mk
new file mode 100644
index 000000000..da5530a11
--- /dev/null
+++ b/libraries/longevity/Android.mk
@@ -0,0 +1,30 @@
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := longevity-lib
+LOCAL_SDK_VERSION := 24
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_JAVA_LIBRARIES := guava
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+######################################
+
+include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/libraries/longevity/samples/Android.mk b/libraries/longevity/samples/Android.mk
new file mode 100644
index 000000000..9f93b2c85
--- /dev/null
+++ b/libraries/longevity/samples/Android.mk
@@ -0,0 +1,27 @@
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_PACKAGE_NAME := LongevityLibSamples
+LOCAL_SDK_VERSION := 24
+LOCAL_STATIC_JAVA_LIBRARIES := longevity-lib guava
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+include $(BUILD_PACKAGE)
+
diff --git a/libraries/longevity/samples/AndroidManifest.xml b/libraries/longevity/samples/AndroidManifest.xml
new file mode 100644
index 000000000..2b3bd0860
--- /dev/null
+++ b/libraries/longevity/samples/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.platform.longevity.samples">
+ <uses-sdk android:minSdkVersion="24" android:targetSdkVersion="24" />
+ <application>
+ <uses-library android:name="android.test.runner"/>
+ </application>
+ <instrumentation
+ android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.platform.longevity.samples"
+ android:label="Platform Longevity Library Samples" />
+</manifest>
diff --git a/libraries/longevity/samples/src/android/platform/longevity/samples/SimpleSuite.java b/libraries/longevity/samples/src/android/platform/longevity/samples/SimpleSuite.java
new file mode 100644
index 000000000..b16d094ad
--- /dev/null
+++ b/libraries/longevity/samples/src/android/platform/longevity/samples/SimpleSuite.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.platform.longevity.samples;
+
+import android.platform.longevity.LongevitySuite;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite.SuiteClasses;
+
+@RunWith(LongevitySuite.class)
+@SuiteClasses({
+ SimpleSuite.PassingTest.class,
+ SimpleSuite.FailingTest.class
+})
+public class SimpleSuite {
+ // no local test cases.
+
+ public static class PassingTest {
+ @Test
+ public void testAssertEquals() {
+ Assert.assertEquals(1, 1);
+ }
+ }
+
+ public static class FailingTest {
+ @Test
+ public void testAssertEquals() {
+ Assert.assertEquals(1, 2);
+ }
+ }
+}
diff --git a/libraries/longevity/src/android/platform/longevity/LongevitySuite.java b/libraries/longevity/src/android/platform/longevity/LongevitySuite.java
new file mode 100644
index 000000000..2ac984b0e
--- /dev/null
+++ b/libraries/longevity/src/android/platform/longevity/LongevitySuite.java
@@ -0,0 +1,90 @@
+package android.platform.longevity;
+
+import android.os.Bundle;
+import android.platform.longevity.listeners.BatteryTerminator;
+import android.platform.longevity.listeners.ErrorTerminator;
+import android.platform.longevity.listeners.TimeoutTerminator;
+import android.platform.longevity.scheduler.Iterate;
+import android.platform.longevity.scheduler.Shuffle;
+import android.support.annotation.VisibleForTesting;
+import android.support.test.InstrumentationRegistry;
+
+import java.util.List;
+import java.util.function.BiFunction;
+
+import org.junit.runner.Runner;
+import org.junit.runners.Suite;
+import org.junit.runners.model.InitializationError;
+import org.junit.runners.model.RunnerBuilder;
+import org.junit.runner.notification.RunNotifier;
+
+/**
+ * Using the {@code LongevitySuite} as a runner allows you to run test sequences repeatedly and with
+ * shuffling in order to simulate longevity conditions and repeated stress or exercise. For examples
+ * look at the bundled sample package.
+ *
+ * TODO(b/62445871): Provide external documentation.
+ */
+public final class LongevitySuite<T> extends Suite {
+ private static final String QUITTER_OPTION = "quitter";
+ private static final String QUITTER_DEFAULT = "false"; // don't quit
+
+ private Bundle mArguments;
+
+ /**
+ * Called reflectively on classes annotated with {@code @RunWith(LongevitySuite.class)}
+ */
+ public LongevitySuite(Class<T> klass, RunnerBuilder builder) throws InitializationError {
+ this(klass, builder, InstrumentationRegistry.getArguments());
+ }
+
+ /**
+ * Called by tests in order to pass in configurable arguments without affecting the registry.
+ */
+ @VisibleForTesting
+ LongevitySuite(Class<T> klass, RunnerBuilder builder, Bundle args)
+ throws InitializationError {
+ this(klass, constructClassRunners(klass, builder, args), args);
+ }
+
+ /**
+ * Called by this class once the suite class and runners have been determined.
+ */
+ private LongevitySuite(Class<T> klass, List<Runner> runners, Bundle args)
+ throws InitializationError {
+ super(klass, runners);
+ mArguments = args;
+ }
+
+ /**
+ * Constructs the sequence of {@link Runner}s that produce the full longevity test.
+ */
+ private static List<Runner> constructClassRunners(
+ Class<?> suite, RunnerBuilder builder, Bundle args) throws InitializationError {
+ // Retrieve annotated suite classes.
+ SuiteClasses annotation = suite.getAnnotation(SuiteClasses.class);
+ if (annotation == null) {
+ throw new InitializationError(String.format(
+ "Longevity suite, '%s', must have a SuiteClasses annotation", suite.getName()));
+ }
+ // Construct and store custom runners for the full suite.
+ BiFunction<Bundle, List<Runner>, List<Runner>> modifier =
+ new Iterate().andThen(new Shuffle());
+ return modifier.apply(args, builder.runners(suite, annotation.value()));
+ }
+
+ @Override
+ public void run(final RunNotifier notifier) {
+ // Add action terminators for custom runner logic.
+ notifier.addListener(
+ new BatteryTerminator(notifier, mArguments, InstrumentationRegistry.getContext()));
+ notifier.addListener(
+ new TimeoutTerminator(notifier, mArguments));
+ if (Boolean.parseBoolean(
+ mArguments.getString(QUITTER_OPTION, String.valueOf(QUITTER_DEFAULT)))) {
+ notifier.addListener(new ErrorTerminator(notifier));
+ }
+ // Invoke tests to run through super call.
+ super.run(notifier);
+ }
+}
diff --git a/libraries/longevity/src/android/platform/longevity/listeners/BatteryTerminator.java b/libraries/longevity/src/android/platform/longevity/listeners/BatteryTerminator.java
new file mode 100644
index 000000000..118ac14b2
--- /dev/null
+++ b/libraries/longevity/src/android/platform/longevity/listeners/BatteryTerminator.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.platform.longevity.listeners;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.support.annotation.VisibleForTesting;
+
+import org.junit.runner.Description;
+import org.junit.runner.notification.RunNotifier;
+
+/**
+ * An {@link ActionListener} for terminating early on test end due to low battery.
+ */
+public final class BatteryTerminator extends RunTerminator {
+ @VisibleForTesting
+ static final String OPTION = "min-battery";
+ private static final double DEFAULT = 0.05; // 5% battery
+
+ private final Context mContext;
+ private final double mMinBattery;
+
+ public BatteryTerminator(RunNotifier notifier, Bundle args, Context context) {
+ super(notifier);
+ mMinBattery = Double.parseDouble(args.getString(OPTION, String.valueOf(DEFAULT)));
+ mContext = context;
+ }
+
+ /**
+ * Returns the battery level of the current device, in percent format (0.05 = 5%).
+ */
+ private double getBatteryLevel() {
+ Intent batteryIntent =
+ mContext.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
+ int level = batteryIntent.getIntExtra("level", -1);
+ int scale = batteryIntent.getIntExtra("scale", -1);
+ if (level < 0 || scale <= 0) {
+ throw new RuntimeException("Failed to get proper battery levels.");
+ }
+ return (double)level / (double)scale;
+ }
+
+ @Override
+ public void testFinished(Description description) {
+ if (getBatteryLevel() < mMinBattery) {
+ kill(String.format("battery fell below %.2f%%", mMinBattery * 100.0f));
+ }
+ }
+}
diff --git a/libraries/longevity/src/android/platform/longevity/listeners/ErrorTerminator.java b/libraries/longevity/src/android/platform/longevity/listeners/ErrorTerminator.java
new file mode 100644
index 000000000..154b1f30c
--- /dev/null
+++ b/libraries/longevity/src/android/platform/longevity/listeners/ErrorTerminator.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.platform.longevity.listeners;
+
+import org.junit.runner.Description;
+import org.junit.runner.notification.Failure;
+import org.junit.runner.notification.RunNotifier;
+
+/**
+ * An {@link ActionListener} for terminating early due to test failures.
+ */
+public final class ErrorTerminator extends RunTerminator {
+ public ErrorTerminator(RunNotifier notifier) {
+ super(notifier);
+ }
+
+ @Override
+ public void testFailure(Failure failure) {
+ kill("a test failed");
+ }
+}
diff --git a/libraries/longevity/src/android/platform/longevity/listeners/RunTerminator.java b/libraries/longevity/src/android/platform/longevity/listeners/RunTerminator.java
new file mode 100644
index 000000000..74ef3b422
--- /dev/null
+++ b/libraries/longevity/src/android/platform/longevity/listeners/RunTerminator.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.platform.longevity.listeners;
+
+import android.util.Log;
+
+import org.junit.runner.notification.RunListener;
+import org.junit.runner.notification.RunNotifier;
+
+/**
+ * An extension of the {@link RunListener} class that provides hooks to the {@code RunNotifier} for
+ * killing tests early.
+ */
+public abstract class RunTerminator extends RunListener {
+ private RunNotifier mNotifier;
+
+ public RunTerminator(RunNotifier notifier) {
+ mNotifier = notifier;
+ }
+
+ /**
+ * Kills subsequent tests and logs a message for future debugging.
+ */
+ protected final void kill(String reason) {
+ Log.d(getClass().getSimpleName(),
+ String.format("Test run killed by %s because %s.",
+ getClass().getSimpleName(), reason));
+ mNotifier.pleaseStop();
+ }
+}
diff --git a/libraries/longevity/src/android/platform/longevity/listeners/TimeoutTerminator.java b/libraries/longevity/src/android/platform/longevity/listeners/TimeoutTerminator.java
new file mode 100644
index 000000000..6d1121441
--- /dev/null
+++ b/libraries/longevity/src/android/platform/longevity/listeners/TimeoutTerminator.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.platform.longevity.listeners;
+
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.support.annotation.VisibleForTesting;
+
+import java.util.concurrent.TimeUnit;
+
+import org.junit.runner.Description;
+import org.junit.runner.notification.RunNotifier;
+
+/**
+ * An {@link ActionListener} for terminating early on test end due to long duration.
+ */
+public final class TimeoutTerminator extends RunTerminator {
+ @VisibleForTesting
+ static final String OPTION = "suite-timeout_msec";
+ private static final long DEFAULT = TimeUnit.MINUTES.toMillis(30L);
+ private static final long UNSET_TIMESTAMP = -1;
+
+ private long mStartTimestamp = UNSET_TIMESTAMP;
+ private long mSuiteTimeout;
+
+ public TimeoutTerminator(RunNotifier notifier, Bundle args) {
+ super(notifier);
+ mSuiteTimeout = Long.parseLong(args.getString(OPTION, String.valueOf(DEFAULT)));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Note: this initializes the countdown timer if unset.
+ */
+ @Override
+ public void testStarted(Description description) {
+ if (mStartTimestamp == UNSET_TIMESTAMP) {
+ mStartTimestamp = SystemClock.uptimeMillis();
+ }
+ }
+
+ @Override
+ public void testFinished(Description description) {
+ if (mStartTimestamp != UNSET_TIMESTAMP &&
+ (SystemClock.uptimeMillis() - mStartTimestamp) > mSuiteTimeout) {
+ kill("the suite timed out");
+ }
+ }
+}
diff --git a/libraries/longevity/src/android/platform/longevity/scheduler/Iterate.java b/libraries/longevity/src/android/platform/longevity/scheduler/Iterate.java
new file mode 100644
index 000000000..751b4748b
--- /dev/null
+++ b/libraries/longevity/src/android/platform/longevity/scheduler/Iterate.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.platform.longevity.scheduler;
+
+import android.os.Bundle;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.junit.runner.Runner;
+
+/**
+ * A {@link Scheduler} for repeating tests a configurable number of times.
+ */
+public class Iterate implements Scheduler {
+ static final String OPTION_NAME = "iterations";
+ private static final int DEFAULT_VALUE = 1;
+
+ @Override
+ public List<Runner> apply(Bundle args, List<Runner> input) {
+ int iterations =
+ Integer.parseInt(args.getString(OPTION_NAME, String.valueOf(DEFAULT_VALUE)));
+ // TODO: Log the options selected.
+ return Collections.nCopies(iterations, input)
+ .stream()
+ .flatMap(Collection::stream)
+ .collect(Collectors.toList());
+ }
+}
diff --git a/libraries/longevity/src/android/platform/longevity/scheduler/Scheduler.java b/libraries/longevity/src/android/platform/longevity/scheduler/Scheduler.java
new file mode 100644
index 000000000..bcbb98c49
--- /dev/null
+++ b/libraries/longevity/src/android/platform/longevity/scheduler/Scheduler.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.platform.longevity.scheduler;
+
+import android.os.Bundle;
+
+import java.util.List;
+import java.util.function.BiFunction;
+
+import org.junit.runner.Runner;
+
+/**
+ * A {@code BiFunction} for modifying the execution of {@code LongevitySuite} {@code Runner}s.
+ */
+@FunctionalInterface
+public interface Scheduler extends BiFunction<Bundle, List<Runner>, List<Runner>> {
+ default Scheduler andThen(Scheduler next) {
+ return (bundle, list) -> next.apply(bundle, this.apply(bundle, list));
+ }
+}
diff --git a/libraries/longevity/src/android/platform/longevity/scheduler/Shuffle.java b/libraries/longevity/src/android/platform/longevity/scheduler/Shuffle.java
new file mode 100644
index 000000000..0ebbe233e
--- /dev/null
+++ b/libraries/longevity/src/android/platform/longevity/scheduler/Shuffle.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.platform.longevity.scheduler;
+
+import android.os.Bundle;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+
+import org.junit.runner.Runner;
+
+/**
+ * A {@link Scheduler} for shuffling the order of test execution.
+ */
+public class Shuffle implements Scheduler {
+ static final String SHUFFLE_OPTION_NAME = "shuffle";
+ private static final boolean SHUFFLE_DEFAULT_VALUE = false;
+ static final String SEED_OPTION_NAME = "seed";
+
+ @Override
+ public List<Runner> apply(Bundle args, List<Runner> input) {
+ boolean shuffle = Boolean.parseBoolean(
+ args.getString(SHUFFLE_OPTION_NAME, String.valueOf(SHUFFLE_DEFAULT_VALUE)));
+ long seed = Long.parseLong(
+ args.getString(SEED_OPTION_NAME, String.valueOf(new Random().nextLong())));
+ // TODO: Log the options selected.
+ if (shuffle) {
+ Collections.shuffle(input, new Random(seed));
+ }
+ return input;
+ }
+}
diff --git a/libraries/longevity/tests/Android.mk b/libraries/longevity/tests/Android.mk
new file mode 100644
index 000000000..4141f4d4c
--- /dev/null
+++ b/libraries/longevity/tests/Android.mk
@@ -0,0 +1,26 @@
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_PACKAGE_NAME := LongevityLibTests
+LOCAL_SDK_VERSION := 24
+LOCAL_STATIC_JAVA_LIBRARIES := longevity-lib mockito-target truth-prebuilt guava
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+include $(BUILD_PACKAGE)
diff --git a/libraries/longevity/tests/AndroidManifest.xml b/libraries/longevity/tests/AndroidManifest.xml
new file mode 100644
index 000000000..ba3de7bb9
--- /dev/null
+++ b/libraries/longevity/tests/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.platform.longevity.tests">
+ <uses-sdk android:minSdkVersion="24" android:targetSdkVersion="24" />
+ <application>
+ <uses-library android:name="android.test.runner"/>
+ </application>
+ <instrumentation
+ android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.platform.longevity.tests"
+ android:label="Platform Longevity Library Tests" />
+</manifest>
diff --git a/libraries/longevity/tests/src/android/platform/longevity/LongevitySuiteTest.java b/libraries/longevity/tests/src/android/platform/longevity/LongevitySuiteTest.java
new file mode 100644
index 000000000..ff796ce3f
--- /dev/null
+++ b/libraries/longevity/tests/src/android/platform/longevity/LongevitySuiteTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.platform.longevity;
+
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+import org.junit.internal.builders.AllDefaultPossibilitiesBuilder;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.junit.runners.model.InitializationError;
+import org.junit.runners.Suite.SuiteClasses;
+
+/**
+ * Unit tests for the {@link LongevitySuite} runner.
+ */
+@RunWith(JUnit4.class)
+public class LongevitySuiteTest {
+ /**
+ * Unit test that the {@link SuiteClasses} annotation is required.
+ */
+ @Test
+ public void testAnnotationRequired() {
+ try {
+ new LongevitySuite(NoSuiteClassesSuite.class, new AllDefaultPossibilitiesBuilder(true));
+ fail("This suite should not be possible to construct.");
+ } catch (InitializationError e) {
+ // ignore and pass.
+ }
+ }
+
+ @RunWith(LongevitySuite.class)
+ private static class NoSuiteClassesSuite { }
+}
diff --git a/libraries/longevity/tests/src/android/platform/longevity/listeners/BatteryTerminatorTest.java b/libraries/longevity/tests/src/android/platform/longevity/listeners/BatteryTerminatorTest.java
new file mode 100644
index 000000000..c1c21b837
--- /dev/null
+++ b/libraries/longevity/tests/src/android/platform/longevity/listeners/BatteryTerminatorTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.platform.longevity.listeners;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.Description;
+import org.junit.runner.RunWith;
+import org.junit.runner.notification.RunNotifier;
+import org.junit.runners.JUnit4;
+
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit test the logic for {@link BatteryTerminator}
+ */
+@RunWith(JUnit4.class)
+public class BatteryTerminatorTest {
+ private BatteryTerminator mListener;
+ @Mock private RunNotifier mNotifier;
+ @Mock private Context mContext;
+ @Mock private Intent mIntent;
+
+ @Before
+ public void setupListener() {
+ MockitoAnnotations.initMocks(this);
+ when(mContext.registerReceiver(any(), any())).thenReturn(mIntent);
+ when(mIntent.getIntExtra("scale", -1)).thenReturn(100);
+ Bundle args = new Bundle();
+ args.putString(BatteryTerminator.OPTION, String.valueOf(0.5));
+ mListener = new BatteryTerminator(mNotifier, args, mContext);
+ }
+
+ /**
+ * Unit test the listener's stops on low battery.
+ */
+ @Test
+ public void testBatteryTerminator_low() throws Exception {
+ when(mIntent.getIntExtra("level", -1)).thenReturn(25);
+ mListener.testFinished(Description.EMPTY);
+ verify(mNotifier).pleaseStop();
+ }
+
+ /**
+ * Unit test the listener's does not stop on high battery.
+ */
+ @Test
+ public void testBatteryTerminator_high() throws Exception {
+ when(mIntent.getIntExtra("level", -1)).thenReturn(75);
+ mListener.testFinished(Description.EMPTY);
+ verify(mNotifier, never()).pleaseStop();
+ }
+}
diff --git a/libraries/longevity/tests/src/android/platform/longevity/listeners/ErrorTerminatorTest.java b/libraries/longevity/tests/src/android/platform/longevity/listeners/ErrorTerminatorTest.java
new file mode 100644
index 000000000..9cbd1a0b5
--- /dev/null
+++ b/libraries/longevity/tests/src/android/platform/longevity/listeners/ErrorTerminatorTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.platform.longevity.listeners;
+
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.Description;
+import org.junit.runner.RunWith;
+import org.junit.runner.notification.Failure;
+import org.junit.runner.notification.RunNotifier;
+import org.junit.runners.JUnit4;
+
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit tests for {@link ErrorTerminator}.
+ */
+@RunWith(JUnit4.class)
+public class ErrorTerminatorTest {
+ private ErrorTerminator mListener;
+ @Mock private RunNotifier mNotifier;
+
+ @Before
+ public void setupListener() {
+ MockitoAnnotations.initMocks(this);
+ mListener = new ErrorTerminator(mNotifier);
+ }
+
+ /**
+ * Unit test the listener's kill logic.
+ */
+ @Test
+ public void testErrorTerminator_errors() throws Exception {
+ mListener.testFailure(new Failure(Description.EMPTY, new Throwable()));
+ verify(mNotifier).pleaseStop();
+ }
+
+ /**
+ * Unit test the listener's kill logic.
+ */
+ @Test
+ public void testErrorTerminator_none() throws Exception {
+ mListener.testStarted(Description.EMPTY);
+ mListener.testFinished(Description.EMPTY);
+ mListener.testFinished(Description.EMPTY);
+ mListener.testIgnored(Description.EMPTY);
+ verify(mNotifier, never()).pleaseStop();
+ }
+}
diff --git a/libraries/longevity/tests/src/android/platform/longevity/listeners/TimeoutTerminatorTest.java b/libraries/longevity/tests/src/android/platform/longevity/listeners/TimeoutTerminatorTest.java
new file mode 100644
index 000000000..acfd13547
--- /dev/null
+++ b/libraries/longevity/tests/src/android/platform/longevity/listeners/TimeoutTerminatorTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.platform.longevity.listeners;
+
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+
+import android.os.Bundle;
+import android.os.SystemClock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.Description;
+import org.junit.runner.RunWith;
+import org.junit.runner.notification.RunNotifier;
+import org.junit.runners.JUnit4;
+
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit tests for {@link TimeoutTerminator}.
+ */
+@RunWith(JUnit4.class)
+public class TimeoutTerminatorTest {
+ private TimeoutTerminator mListener;
+ @Mock private RunNotifier mNotifier;
+
+ @Before
+ public void setupListener() {
+ MockitoAnnotations.initMocks(this);
+ Bundle args = new Bundle();
+ args.putString(TimeoutTerminator.OPTION, String.valueOf(50L));
+ mListener = new TimeoutTerminator(mNotifier, args);
+ }
+ /**
+ * Unit test the listener's kill logic.
+ */
+ @Test
+ public void testTimeoutTerminator_pass() throws Exception {
+ mListener.testStarted(Description.EMPTY);
+ SystemClock.sleep(10L);
+ verify(mNotifier, never()).pleaseStop();
+ }
+
+ /**
+ * Unit test the listener's kill logic.
+ */
+ @Test
+ public void testTimeoutTerminator_timeout() throws Exception {
+ mListener.testStarted(Description.EMPTY);
+ SystemClock.sleep(60L);
+ mListener.testFinished(Description.EMPTY);
+ verify(mNotifier).pleaseStop();
+ }
+}
diff --git a/libraries/longevity/tests/src/android/platform/longevity/scheduler/IterateTest.java b/libraries/longevity/tests/src/android/platform/longevity/scheduler/IterateTest.java
new file mode 100644
index 000000000..6aa17c10b
--- /dev/null
+++ b/libraries/longevity/tests/src/android/platform/longevity/scheduler/IterateTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.platform.longevity.scheduler;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.when;
+
+import android.os.Bundle;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import org.junit.Test;
+import org.junit.runner.Description;
+import org.junit.runner.Runner;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import org.mockito.Mockito;
+
+/**
+ * Unit test the logic for {@link Iterate}
+ */
+@RunWith(JUnit4.class)
+public class IterateTest {
+ private static final int NUM_TESTS = 10;
+ private static final int TEST_ITERATIONS = 25;
+
+ private Iterate mIterate = new Iterate();
+
+ /**
+ * Unit test the iteration count is respected.
+ */
+ @Test
+ public void testIterationsRespected() {
+ // Construct argument bundle.
+ Bundle args = new Bundle();
+ args.putString(Iterate.OPTION_NAME, String.valueOf(TEST_ITERATIONS));
+ // Construct input runners.
+ List<Runner> input = new ArrayList<>();
+ IntStream.range(1, NUM_TESTS).forEach(i -> input.add(getMockRunner(i)));
+ // Apply iterator on arguments and runners.
+ List<Runner> output = mIterate.apply(args, input);
+ // Count occurrences of test descriptions into a map.
+ Map<String, Long> countMap = output.stream()
+ .map(Runner::getDescription)
+ .map(Description::getDisplayName)
+ .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
+ // Ensure all test descriptions have N entries.
+ boolean respected = countMap.entrySet().stream()
+ .noneMatch(entry -> (entry.getValue() != TEST_ITERATIONS));
+ assertThat(respected).isTrue();
+ }
+
+ private Runner getMockRunner (int id) {
+ Runner result = Mockito.mock(Runner.class);
+ Description desc = Mockito.mock(Description.class);
+ when(result.getDescription()).thenReturn(desc);
+ when(desc.getDisplayName()).thenReturn(String.valueOf(id));
+ return result;
+ }
+}
diff --git a/libraries/longevity/tests/src/android/platform/longevity/scheduler/ShuffleTest.java b/libraries/longevity/tests/src/android/platform/longevity/scheduler/ShuffleTest.java
new file mode 100644
index 000000000..97486d290
--- /dev/null
+++ b/libraries/longevity/tests/src/android/platform/longevity/scheduler/ShuffleTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.platform.longevity.scheduler;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.when;
+
+import android.os.Bundle;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+import java.util.stream.IntStream;
+
+import org.junit.Test;
+import org.junit.runner.Description;
+import org.junit.runner.Runner;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import org.mockito.Mockito;
+
+/**
+ * Unit test the logic for {@link Shuffle}
+ */
+@RunWith(JUnit4.class)
+public class ShuffleTest {
+ private static final int NUM_TESTS = 10;
+ private static final long SEED_VALUE = new Random().nextLong();
+
+ private Shuffle mShuffle = new Shuffle();
+
+ /**
+ * Unit test that shuffling with a specific seed is respected.
+ */
+ @Test
+ public void testShuffleSeedRespected() {
+ // Construct argument bundle.
+ Bundle args = new Bundle();
+ args.putString(Shuffle.SHUFFLE_OPTION_NAME, "true");
+ args.putString(Shuffle.SEED_OPTION_NAME, String.valueOf(SEED_VALUE));
+ // Construct input runners.
+ List<Runner> input = new ArrayList<>();
+ IntStream.range(1, NUM_TESTS).forEach(i -> input.add(getMockRunner(i)));
+ // Apply shuffler on arguments and runners.
+ List<Runner> output = mShuffle.apply(args, new ArrayList(input));
+ // Shuffle locally against the same seed and compare results.
+ Collections.shuffle(input, new Random(SEED_VALUE));
+ assertThat(input).isEqualTo(output);
+ }
+
+ private Runner getMockRunner (int id) {
+ Runner result = Mockito.mock(Runner.class);
+ Description desc = Mockito.mock(Description.class);
+ when(result.getDescription()).thenReturn(desc);
+ when(desc.getDisplayName()).thenReturn(String.valueOf(id));
+ return result;
+ }
+}