Java

Форк
0
/
FeatureSpecificTestSuiteBuilder.java 
318 строк · 10.2 Кб
1
/*
2
 * Copyright (C) 2008 The Guava Authors
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 com.google.common.collect.testing;
18

19
import static java.util.Collections.disjoint;
20
import static java.util.logging.Level.FINER;
21

22
import com.google.common.annotations.GwtIncompatible;
23
import com.google.common.collect.testing.features.ConflictingRequirementsException;
24
import com.google.common.collect.testing.features.Feature;
25
import com.google.common.collect.testing.features.FeatureUtil;
26
import com.google.common.collect.testing.features.TesterRequirements;
27
import com.google.errorprone.annotations.CanIgnoreReturnValue;
28
import java.lang.reflect.Method;
29
import java.util.ArrayList;
30
import java.util.Arrays;
31
import java.util.Collection;
32
import java.util.Collections;
33
import java.util.Enumeration;
34
import java.util.HashSet;
35
import java.util.LinkedHashSet;
36
import java.util.List;
37
import java.util.Set;
38
import java.util.logging.Logger;
39
import junit.framework.Test;
40
import junit.framework.TestCase;
41
import junit.framework.TestSuite;
42
import org.checkerframework.checker.nullness.qual.Nullable;
43

44
/**
45
 * Creates, based on your criteria, a JUnit test suite that exhaustively tests the object generated
46
 * by a G, selecting appropriate tests by matching them against specified features.
47
 *
48
 * @param <B> The concrete type of this builder (the 'self-type'). All the Builder methods of this
49
 *     class (such as {@link #named}) return this type, so that Builder methods of more derived
50
 *     classes can be chained onto them without casting.
51
 * @param <G> The type of the generator to be passed to testers in the generated test suite. An
52
 *     instance of G should somehow provide an instance of the class under test, plus any other
53
 *     information required to parameterize the test.
54
 * @author George van den Driessche
55
 */
56
@GwtIncompatible
57
public abstract class FeatureSpecificTestSuiteBuilder<
58
    B extends FeatureSpecificTestSuiteBuilder<B, G>, G> {
59
  @SuppressWarnings("unchecked")
60
  protected B self() {
61
    return (B) this;
62
  }
63

64
  // Test Data
65

66
  private @Nullable G subjectGenerator;
67
  // Gets run before every test.
68
  private Runnable setUp;
69
  // Gets run at the conclusion of every test.
70
  private Runnable tearDown;
71

72
  @CanIgnoreReturnValue
73
  protected B usingGenerator(G subjectGenerator) {
74
    this.subjectGenerator = subjectGenerator;
75
    return self();
76
  }
77

78
  public G getSubjectGenerator() {
79
    return subjectGenerator;
80
  }
81

82
  @CanIgnoreReturnValue
83
  public B withSetUp(Runnable setUp) {
84
    this.setUp = setUp;
85
    return self();
86
  }
87

88
  public Runnable getSetUp() {
89
    return setUp;
90
  }
91

92
  @CanIgnoreReturnValue
93
  public B withTearDown(Runnable tearDown) {
94
    this.tearDown = tearDown;
95
    return self();
96
  }
97

98
  public Runnable getTearDown() {
99
    return tearDown;
100
  }
101

102
  // Features
103

104
  private final Set<Feature<?>> features = new LinkedHashSet<>();
105

106
  /**
107
   * Configures this builder to produce tests appropriate for the given features. This method may be
108
   * called more than once to add features in multiple groups.
109
   */
110
  @CanIgnoreReturnValue
111
  public B withFeatures(Feature<?>... features) {
112
    return withFeatures(Arrays.asList(features));
113
  }
114

115
  @CanIgnoreReturnValue
116
  public B withFeatures(Iterable<? extends Feature<?>> features) {
117
    for (Feature<?> feature : features) {
118
      this.features.add(feature);
119
    }
120
    return self();
121
  }
122

123
  public Set<Feature<?>> getFeatures() {
124
    return Collections.unmodifiableSet(features);
125
  }
126

127
  // Name
128

129
  private @Nullable String name;
130

131
  /** Configures this builder produce a TestSuite with the given name. */
132
  @CanIgnoreReturnValue
133
  public B named(String name) {
134
    if (name.contains("(")) {
135
      throw new IllegalArgumentException(
136
          "Eclipse hides all characters after "
137
              + "'('; please use '[]' or other characters instead of parentheses");
138
    }
139
    this.name = name;
140
    return self();
141
  }
142

143
  public String getName() {
144
    return name;
145
  }
146

147
  // Test suppression
148

149
  private final Set<Method> suppressedTests = new HashSet<>();
150

151
  /**
152
   * Prevents the given methods from being run as part of the test suite.
153
   *
154
   * <p><em>Note:</em> in principle this should never need to be used, but it might be useful if the
155
   * semantics of an implementation disagree in unforeseen ways with the semantics expected by a
156
   * test, or to keep dependent builds clean in spite of an erroneous test.
157
   */
158
  @CanIgnoreReturnValue
159
  public B suppressing(Method... methods) {
160
    return suppressing(Arrays.asList(methods));
161
  }
162

163
  @CanIgnoreReturnValue
164
  public B suppressing(Collection<Method> methods) {
165
    suppressedTests.addAll(methods);
166
    return self();
167
  }
168

169
  public Set<Method> getSuppressedTests() {
170
    return suppressedTests;
171
  }
172

173
  private static final Logger logger =
174
      Logger.getLogger(FeatureSpecificTestSuiteBuilder.class.getName());
175

176
  /** Creates a runnable JUnit test suite based on the criteria already given. */
177
  public TestSuite createTestSuite() {
178
    checkCanCreate();
179

180
    logger.fine(" Testing: " + name);
181
    logger.fine("Features: " + formatFeatureSet(features));
182

183
    FeatureUtil.addImpliedFeatures(features);
184

185
    logger.fine("Expanded: " + formatFeatureSet(features));
186

187
    @SuppressWarnings("rawtypes") // class literals
188
    List<Class<? extends AbstractTester>> testers = getTesters();
189

190
    TestSuite suite = new TestSuite(name);
191
    for (@SuppressWarnings("rawtypes") // class literals
192
    Class<? extends AbstractTester> testerClass : testers) {
193
      @SuppressWarnings("unchecked") // getting rid of the raw type, for better or for worse
194
      TestSuite testerSuite =
195
          makeSuiteForTesterClass((Class<? extends AbstractTester<?>>) testerClass);
196
      if (testerSuite.countTestCases() > 0) {
197
        suite.addTest(testerSuite);
198
      }
199
    }
200
    return suite;
201
  }
202

203
  /** Throw {@link IllegalStateException} if {@link #createTestSuite()} can't be called yet. */
204
  protected void checkCanCreate() {
205
    if (subjectGenerator == null) {
206
      throw new IllegalStateException("Call using() before createTestSuite().");
207
    }
208
    if (name == null) {
209
      throw new IllegalStateException("Call named() before createTestSuite().");
210
    }
211
    if (features == null) {
212
      throw new IllegalStateException("Call withFeatures() before createTestSuite().");
213
    }
214
  }
215

216
  @SuppressWarnings("rawtypes") // class literals
217
  protected abstract List<Class<? extends AbstractTester>> getTesters();
218

219
  private boolean matches(Test test) {
220
    Method method;
221
    try {
222
      method = extractMethod(test);
223
    } catch (IllegalArgumentException e) {
224
      logger.finer(Platform.format("%s: including by default: %s", test, e.getMessage()));
225
      return true;
226
    }
227
    if (suppressedTests.contains(method)) {
228
      logger.finer(Platform.format("%s: excluding because it was explicitly suppressed.", test));
229
      return false;
230
    }
231
    TesterRequirements requirements;
232
    try {
233
      requirements = FeatureUtil.getTesterRequirements(method);
234
    } catch (ConflictingRequirementsException e) {
235
      throw new RuntimeException(e);
236
    }
237
    if (!features.containsAll(requirements.getPresentFeatures())) {
238
      if (logger.isLoggable(FINER)) {
239
        Set<Feature<?>> missingFeatures = Helpers.copyToSet(requirements.getPresentFeatures());
240
        missingFeatures.removeAll(features);
241
        logger.finer(
242
            Platform.format(
243
                "%s: skipping because these features are absent: %s", method, missingFeatures));
244
      }
245
      return false;
246
    }
247
    if (intersect(features, requirements.getAbsentFeatures())) {
248
      if (logger.isLoggable(FINER)) {
249
        Set<Feature<?>> unwantedFeatures = Helpers.copyToSet(requirements.getAbsentFeatures());
250
        unwantedFeatures.retainAll(features);
251
        logger.finer(
252
            Platform.format(
253
                "%s: skipping because these features are present: %s", method, unwantedFeatures));
254
      }
255
      return false;
256
    }
257
    return true;
258
  }
259

260
  private static boolean intersect(Set<?> a, Set<?> b) {
261
    return !disjoint(a, b);
262
  }
263

264
  private static Method extractMethod(Test test) {
265
    if (test instanceof AbstractTester) {
266
      AbstractTester<?> tester = (AbstractTester<?>) test;
267
      return Helpers.getMethod(tester.getClass(), tester.getTestMethodName());
268
    } else if (test instanceof TestCase) {
269
      TestCase testCase = (TestCase) test;
270
      return Helpers.getMethod(testCase.getClass(), testCase.getName());
271
    } else {
272
      throw new IllegalArgumentException("unable to extract method from test: not a TestCase.");
273
    }
274
  }
275

276
  protected TestSuite makeSuiteForTesterClass(Class<? extends AbstractTester<?>> testerClass) {
277
    TestSuite candidateTests = new TestSuite(testerClass);
278
    TestSuite suite = filterSuite(candidateTests);
279

280
    Enumeration<?> allTests = suite.tests();
281
    while (allTests.hasMoreElements()) {
282
      Object test = allTests.nextElement();
283
      if (test instanceof AbstractTester) {
284
        @SuppressWarnings("unchecked")
285
        AbstractTester<? super G> tester = (AbstractTester<? super G>) test;
286
        tester.init(subjectGenerator, name, setUp, tearDown);
287
      }
288
    }
289

290
    return suite;
291
  }
292

293
  private TestSuite filterSuite(TestSuite suite) {
294
    TestSuite filtered = new TestSuite(suite.getName());
295
    Enumeration<?> tests = suite.tests();
296
    while (tests.hasMoreElements()) {
297
      Test test = (Test) tests.nextElement();
298
      if (matches(test)) {
299
        filtered.addTest(test);
300
      }
301
    }
302
    return filtered;
303
  }
304

305
  protected static String formatFeatureSet(Set<? extends Feature<?>> features) {
306
    List<String> temp = new ArrayList<>();
307
    for (Feature<?> feature : features) {
308
      Object featureAsObject = feature; // to work around bogus JDK warning
309
      if (featureAsObject instanceof Enum) {
310
        Enum<?> f = (Enum<?>) featureAsObject;
311
        temp.add(f.getDeclaringClass().getSimpleName() + "." + feature);
312
      } else {
313
        temp.add(feature.toString());
314
      }
315
    }
316
    return temp.toString();
317
  }
318
}
319

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.