Home | History | Annotate | Download | only in aupt
      1 /*
      2  * Copyright (C) 2016 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 
     17 package android.support.test.aupt;
     18 
     19 import junit.framework.TestCase;
     20 
     21 import java.util.Iterator;
     22 import java.util.List;
     23 import java.util.Random;
     24 
     25 /**
     26  * A Scheduler produces an execution ordering for our test cases.
     27  *
     28  * For example, the Sequential scheduler will just repeat our test cases a fixed number of times.
     29  */
     30 public abstract class Scheduler {
     31 
     32     /** Shuffle and/or iterate through a list of test cases. */
     33     public final Iterable<TestCase> apply(final List<TestCase> cases) {
     34         return new Iterable<TestCase>() {
     35             public Iterator<TestCase> iterator() {
     36                 return applyInternal(cases);
     37             }
     38         };
     39     }
     40 
     41     /**
     42      * A Scheduler that just loops through test cases a fixed number of times.
     43      *
     44      * @param iterations the number of times to loop through every test case.
     45      */
     46     public static Scheduler sequential(Long iterations) {
     47         return new Sequential(iterations);
     48     }
     49 
     50     /**
     51      * A Scheduler that permutes the test case list, then just iterates.
     52      *
     53      * @param iterations the number of times to loop through every test case.
     54      * @param random the Random to use: with the same random, we'll return the same ordering.
     55      */
     56     public static Scheduler shuffled(Random random, Long iterations) {
     57         return new Shuffled(random, iterations);
     58     }
     59 
     60     /** Private interface to Scheduler::apply */
     61     protected abstract Iterator<TestCase> applyInternal(List<TestCase> cases);
     62 
     63     private static class Sequential extends Scheduler {
     64         private final Long mIterations;
     65 
     66         Sequential(Long iterations) {
     67             mIterations = iterations;
     68         }
     69 
     70         protected Iterator<TestCase> applyInternal (final List<TestCase> cases) {
     71             return new Iterator<TestCase>() {
     72                 private int count = 0;
     73 
     74                 public boolean hasNext() {
     75                     return count < (cases.size() * mIterations);
     76                 }
     77 
     78                 public TestCase next() {
     79                     return cases.get(count++ % cases.size());
     80                 }
     81 
     82                 public void remove() { }
     83             };
     84         }
     85     }
     86 
     87     private static class Shuffled extends Scheduler {
     88         private final Random mRandom;
     89         private final Long mIterations;
     90 
     91         Shuffled(Random random, Long iterations) {
     92             mRandom = random;
     93             mIterations = iterations;
     94         }
     95 
     96         /**
     97          * Find a GCD by the nieve Euclidean Algorithm
     98          * TODO: get this from guava or some other library
     99          */
    100         private int gcd(final int _a, final int _b) {
    101             int a = _a;
    102             int b = _b;
    103 
    104             while (b > 0) {
    105                 int tmp = b;
    106                 b = a % b;
    107                 a = tmp;
    108             }
    109 
    110             return a;
    111         }
    112 
    113         /**
    114          * Find a random number relatively prime to our modulus
    115          * TODO: get this from guava or some other library
    116          */
    117         private int randomRelPrime(Integer modulus) {
    118             if (modulus <= 1) {
    119                 return 1;
    120             } else {
    121                 int x = 0;
    122 
    123                 // Sample random numbers until we get something coprime to our modulus
    124                 while (gcd(x, modulus) != 1) {
    125                     x = mRandom.nextInt() % modulus;
    126                 }
    127 
    128                 return x % modulus;
    129             }
    130         }
    131 
    132         /**
    133          * Return the tests in a shuffled order using a simple linear congruential generator: i.e.
    134          * the elements are permuted by (a x + b % n), will produce a permutation of the elements of n
    135          * iff a is coprime to n.
    136          * <p>
    137          * The reason to do this is that it also produces a permutation each cases.size() rounds, which
    138          * is *not* the case for Collections.shuffle(); and implementing a comparable iterator with
    139          * those primitives is somewhat less elegant.
    140          */
    141         protected Iterator<TestCase> applyInternal(final List<TestCase> cases) {
    142             final int a = randomRelPrime(cases.size());
    143             final int b = Math.abs(mRandom.nextInt());
    144 
    145             return new Iterator<TestCase>() {
    146                 private int count = 0;
    147 
    148                 public boolean hasNext() {
    149                     return count < (cases.size() * mIterations);
    150                 }
    151 
    152                 public TestCase next() {
    153                     return cases.get((a * (count++) + b) % cases.size());
    154                 }
    155 
    156                 public void remove() {
    157                 }
    158             };
    159         }
    160     }
    161 }
    162