1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package android.longevity.core; 17 18 import android.longevity.core.listener.ErrorTerminator; 19 import android.longevity.core.listener.TimeoutTerminator; 20 import android.longevity.core.scheduler.Iterate; 21 import android.longevity.core.scheduler.Shuffle; 22 23 import org.junit.runner.Runner; 24 import org.junit.runner.notification.RunNotifier; 25 import org.junit.runners.Suite; 26 import org.junit.runners.model.InitializationError; 27 import org.junit.runners.model.RunnerBuilder; 28 29 import java.util.List; 30 import java.util.Map; 31 import java.util.function.BiFunction; 32 import java.util.stream.Collectors; 33 34 /** 35 * Using the {@code LongevitySuite} as a runner allows you to run test sequences repeatedly and with 36 * shuffling in order to simulate longevity conditions and repeated stress or exercise. For examples 37 * look at the bundled samples package. 38 */ 39 public class LongevitySuite extends Suite { 40 private static final String QUITTER_OPTION = "quitter"; 41 private static final boolean QUITTER_DEFAULT = false; // don't quit 42 43 protected Map<String, String> mArguments; 44 45 /** 46 * Called reflectively on classes annotated with {@code @RunWith(LongevitySuite.class)} 47 */ 48 public LongevitySuite(Class<?> klass, RunnerBuilder builder) 49 throws InitializationError { 50 this(klass, builder, 51 System.getProperties().entrySet() 52 .stream() 53 .collect(Collectors.toMap( 54 i -> String.valueOf(i.getKey()), 55 i -> String.valueOf(i.getValue())))); 56 } 57 58 /** 59 * Called explicitly to pass in configurable arguments without affecting expected formats. 60 */ 61 public LongevitySuite(Class<?> klass, RunnerBuilder builder, Map<String, String> args) 62 throws InitializationError { 63 this(klass, constructClassRunners(klass, builder, args), args); 64 } 65 66 /** 67 * Called by this class once the suite class and runners have been determined. 68 */ 69 protected LongevitySuite(Class<?> klass, List<Runner> runners, Map<String, String> args) 70 throws InitializationError { 71 super(klass, runners); 72 mArguments = args; 73 } 74 75 /** 76 * Constructs the sequence of {@link Runner}s that produce the full longevity test. 77 */ 78 private static List<Runner> constructClassRunners( 79 Class<?> suite, RunnerBuilder builder, Map<String, String> args) 80 throws InitializationError { 81 // Retrieve annotated suite classes. 82 SuiteClasses annotation = suite.getAnnotation(SuiteClasses.class); 83 if (annotation == null) { 84 throw new InitializationError(String.format( 85 "Longevity suite, '%s', must have a SuiteClasses annotation", suite.getName())); 86 } 87 // Construct and store custom runners for the full suite. 88 BiFunction<Map<String, String>, List<Runner>, List<Runner>> modifier = 89 new Iterate().andThen(new Shuffle()); 90 return modifier.apply(args, builder.runners(suite, annotation.value())); 91 } 92 93 @Override 94 public void run(final RunNotifier notifier) { 95 // Add action terminators for custom runner logic. 96 if (mArguments.containsKey(QUITTER_OPTION) ? 97 Boolean.parseBoolean(mArguments.get(QUITTER_OPTION)) : QUITTER_DEFAULT) { 98 notifier.addListener(getErrorTerminator(notifier)); 99 } 100 notifier.addListener(getTimeoutTerminator(notifier)); 101 // Invoke tests to run through super call. 102 super.run(notifier); 103 } 104 105 /** 106 * Returns the {@link ErrorTerminator} to register with the {@link RunNotifier}. 107 * <p> 108 * Note: exposed for overriding with a platform-specific {@link ErrorTerminator}. 109 */ 110 public ErrorTerminator getErrorTerminator(final RunNotifier notifier) { 111 return new ErrorTerminator(notifier); 112 } 113 114 /** 115 * Returns the {@link TimeoutTerminator} to register with the {@link RunNotifier}. 116 * <p> 117 * Note: exposed for overriding with a platform-specific {@link TimeoutTerminator}. 118 */ 119 public TimeoutTerminator getTimeoutTerminator(final RunNotifier notifier) { 120 return new TimeoutTerminator(notifier, mArguments); 121 } 122 123 /** 124 * Returns the {@link List} of {@link Runner}s children for explicit modification by another 125 * class. 126 * <p> 127 * Note: using this method is highly discouraged unless explicitly needed. 128 */ 129 public List<Runner> getRunners() { 130 return getChildren(); 131 } 132 } 133