aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/com/google/testing/junit/testparameterinjector/BaseTestParameterValidator.java
blob: ab5003e3fccbfe41a93febdcb159359c60464130 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
/*
 * Copyright 2021 Google Inc.
 *
 * 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 com.google.testing.junit.testparameterinjector;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static java.lang.Math.min;

import java.lang.annotation.Annotation;
import java.util.Comparator;
import java.util.List;

/**
 * Default base class for {@link TestParameterValidator}, simplifying how validators can exclude
 * variable independent test parameters annotations.
 */
abstract class BaseTestParameterValidator implements TestParameterValidator {

  @Override
  public boolean shouldSkip(Context context) {
    for (List<Class<? extends Annotation>> parameters : getIndependentParameters(context)) {
      checkArgument(!parameters.isEmpty());
      // For independent test parameters, the only allowed tests will be those that use the same
      // Nth specified parameter, except for parameter values that have less specified values than
      // others.

      // For example, if parameter A has values a1 and a2, parameter B has values b1 and b2, and
      // parameter C has values c1, c2 and c3, given that A, B and C are independent, the only
      // tests that will not be skipped will be {(a1, b1, c1), (a2, b2, c2), (a2, b2, c3)},
      // instead of 12 tests that would constitute their cartesian product.

      // First, find the largest specified value count (parameter C in the example above),
      // so that we can easily determine which parameter value should be used for validating the
      // other parameters (e.g. should this test be for (a1, b1, c1), (a2, b2, c2), or
      // (a2, b2, c3). The test parameter 'C' will be the 'leadingParameter'.
      Class<? extends Annotation> leadingParameter =
          parameters.stream()
              .max(Comparator.comparing(parameter -> context.getSpecifiedValues(parameter).size()))
              .get();
      // Second, determine which index is the current value in the specified value list of
      // the leading parameter.  In the example above, the index of the current value 'c2' of the
      // leading parameter 'C' would be '1', given the specified values (c1, c2, c3).
      int leadingParameterValueIndex =
          getValueIndex(context, leadingParameter, context.getValue(leadingParameter).get());
      checkState(leadingParameterValueIndex >= 0);
      // Each independent test parameter should be the same index, or the last available index.
      // For example, if the parameter is A, and the leading parameter (C) index is 2, the A's index
      // should be 1, since a2 is the only available value.
      for (Class<? extends Annotation> parameter : parameters) {
        List<Object> specifiedValues = context.getSpecifiedValues(parameter);
        int valueIndex = specifiedValues.indexOf(context.getValue(parameter).get());
        int requiredValueIndex = min(leadingParameterValueIndex, specifiedValues.size() - 1);
        if (valueIndex != requiredValueIndex) {
          return true;
        }
      }
    }
    return false;
  }

  private int getValueIndex(Context context, Class<? extends Annotation> annotation, Object value) {
    return context.getSpecifiedValues(annotation).indexOf(value);
  }

  /**
   * Returns a list of TestParameterAnnotation annotated annotation types that are mutually
   * independent, and therefore the combinations of their values do not need to be tested.
   */
  protected abstract List<List<Class<? extends Annotation>>> getIndependentParameters(
      Context context);
}