diff options
author | android-build-team Robot <android-build-team-robot@google.com> | 2017-09-06 07:34:42 +0000 |
---|---|---|
committer | android-build-team Robot <android-build-team-robot@google.com> | 2017-09-06 07:34:42 +0000 |
commit | b7aa7cbf4b7e61ff6a12bc9f727e6b257253026a (patch) | |
tree | 800b763ebead4f650c4d7dc31e9038f00cf8dc2a | |
parent | f989f1456af552202feeefff3689e061f9c16d12 (diff) | |
parent | 6d4aa95e1f962057c0af52be2cdf3afe296f5f44 (diff) | |
download | platform_testing-b7aa7cbf4b7e61ff6a12bc9f727e6b257253026a.tar.gz |
release-request-ee600ee1-fadc-4a92-9feb-e22548c84a75-for-git_oc-mr1-release-4318546 snap-temp-L23800000099760172
Change-Id: I5533c2b2928e72dc93081ee762794637031792e3
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; + } +} |