1f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper/** 2f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * @license 3f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * Copyright 2013 Google Inc. All rights reserved. 4f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * Licensed under the Apache License, Version 2.0 (the "License"); 5f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * you may not use this file except in compliance with the License. 6f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * You may obtain a copy of the License at 7f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * 8f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * http://www.apache.org/licenses/LICENSE-2.0 9f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * 10f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * Unless required by applicable law or agreed to in writing, software 11f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * distributed under the License is distributed on an "AS IS" BASIS, 12f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * See the License for the specific language governing permissions and 14f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * limitations under the License. 15f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper */ 16f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper 17f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasperpackage com.google.security.wycheproof; 18f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper 19f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasperimport java.lang.annotation.ElementType; 20f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasperimport java.lang.annotation.Retention; 21f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasperimport java.lang.annotation.RetentionPolicy; 22f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasperimport java.lang.annotation.Target; 23f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasperimport java.util.Arrays; 24f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasperimport org.junit.runner.Description; 25f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasperimport org.junit.runner.manipulation.Filter; 26f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasperimport org.junit.runner.manipulation.NoTestsRemainException; 27f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasperimport org.junit.runners.Suite; 28f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasperimport org.junit.runners.model.InitializationError; 29f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasperimport org.junit.runners.model.RunnerBuilder; 30f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper 31f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper/** 32f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * <p>A custom JUnit4 runner that, with annotations, allows choosing tests to run on a specific 33f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * provider. To use it, annotate a runner class with {@code RunWith(WycheproofRunner.class)}, and 34f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * {@code SuiteClasses({AesGcmTest.class, ...})}. When you run this class, it will run all the tests 35f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * in all the suite classes. 36f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * 37f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * <p>To exclude certain tests, a runner class should be annotated with {@code @Provider} which 38f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * indicates the target provider. Test exclusion is defined as follows: 39f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * <ul>@Fast test runners skip @SlowTest test functions. 40f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * <ul>@Presubmit test runners skip @NoPresubmitTest test functions. 41f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * <ul>All test runners skip @ExcludedTest test functions. 42f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * 43f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * @author thaidn@google.com (Thai Duong) 44f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper */ 45f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasperpublic class WycheproofRunner extends Suite { 46f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper 47f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper /** List of supported providers. */ 48f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper public enum ProviderType { 49f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper BOUNCY_CASTLE, 50f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper CONSCRYPT, 51f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper OPENJDK, 52f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper SPONGY_CASTLE, 53f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper } 54f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper 55f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper // Annotations for test runners. 56f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper 57f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper /** 58f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * Annotation to specify the target provider of a test runner. 59f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * 60f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * <p>Usage: @Provider(ProviderType.BOUNCY_CASTLE) 61f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper */ 62f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper @Retention(RetentionPolicy.RUNTIME) 63f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper @Target({ElementType.TYPE}) 64f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper public @interface Provider { 65f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper ProviderType value(); 66f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper } 67f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper 68f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper /** 69f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * Annotation to specify presubmit test runners that exclude {@code @NoPresubmitTets} tests. 70f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * 71f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * <p>Usage: @Presubmit(ProviderType.BOUNCY_CASTLE) 72f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper */ 73f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper @Retention(RetentionPolicy.RUNTIME) 74f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper @Target({ElementType.TYPE}) 75f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper public @interface Presubmit {} 76f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper 77f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper /** 78f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * Annotation to specify fast test runners that exclude {@code @SlowTest} tests. 79f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * 80f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * <p>Usage: @Fast 81f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper */ 82f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper @Retention(RetentionPolicy.RUNTIME) 83f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper @Target({ElementType.TYPE}) 84f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper public @interface Fast {} 85f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper 86f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper // Annotations for test functions 87f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper 88f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper /** 89f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * Tests that take too much time to run, should be excluded from TAP and wildcard target patterns 90f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * like:..., :*, or :all. 91f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * 92f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * <p>Usage: @SlowTest(providers = {ProviderType.BOUNCY_CASTLE, ...}) 93f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper */ 94f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper @Retention(RetentionPolicy.RUNTIME) 95f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper @Target({ElementType.METHOD}) 96f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper public @interface SlowTest { 97f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper ProviderType[] providers(); 98f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper } 99f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper 100f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper /** 101f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * Tests that should be excluded from presubmit checks on specific providers. 102f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * 103f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * <p>Usage: @NoPresubmitTest( 104f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * providers = {ProviderType.BOUNCY_CASTLE, ...}, 105f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * bugs = {"b/123456789"} 106f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * ) 107f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper */ 108f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper @Retention(RetentionPolicy.RUNTIME) 109f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper @Target({ElementType.METHOD, ElementType.FIELD}) 110f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper public @interface NoPresubmitTest { 111f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper /** List of providers that this test method should not run as presubmit check. */ 112f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper ProviderType[] providers(); 113f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper 114f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper /** List of blocking bugs (and comments). */ 115f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper String[] bugs(); 116f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper } 117f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper 118f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper /** 119f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * Annotation to specify test functions that should be excluded on specific providers. 120f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * 121f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * <p>Usage: @ExcludedTest(providers = {ProviderType.BOUNCY_CASTLE, ProviderType.OPENJDK}) 122f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper */ 123f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper @Retention(RetentionPolicy.RUNTIME) 124f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper @Target({ElementType.METHOD}) 125f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper public @interface ExcludedTest { 126f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper ProviderType[] providers(); 127f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper String comment(); 128f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper } 129f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper 130f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper /** 131f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * Custom filter to exclude certain test functions. 132f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper * 133f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper */ 134f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper public static class ExcludeTestFilter extends Filter { 135f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper 136f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper Class<?> runnerClass; 137f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper Provider targetProvider; 138f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper Fast fast; 139f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper Presubmit presubmit; 140f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper 141f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper public ExcludeTestFilter(Class<?> runnerClass) { 142f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper this.runnerClass = runnerClass; 143f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper this.targetProvider = runnerClass.getAnnotation(Provider.class); 144f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper this.fast = runnerClass.getAnnotation(Fast.class); 145f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper this.presubmit = runnerClass.getAnnotation(Presubmit.class); 146f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper } 147f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper 148f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper @Override 149f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper public String describe() { 150f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper return "exclude certain tests on specific providers"; 151f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper } 152f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper 153f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper @Override 154f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper public boolean shouldRun(Description description) { 155f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper return isOkayToRunTest(description); 156f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper } 157f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper 158f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper private boolean isOkayToRunTest(Description description) { 159f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper if (targetProvider == null) { 160f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper // Run all test functions if the test runner is not annotated with {@code @Provider}. 161f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper return true; 162f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper } 163f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper // Skip @ExcludedTest tests 164f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper ExcludedTest excludedTest = description.getAnnotation(ExcludedTest.class); 165f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper if (excludedTest != null 166f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper && Arrays.asList(excludedTest.providers()).contains(targetProvider.value())) { 167f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper return false; 168f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper } 169f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper 170f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper // If the runner class is annotated with @Presubmit, skip non-presubmit tests 171f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper if (presubmit != null) { 172f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper NoPresubmitTest ignoreOn = description.getAnnotation(NoPresubmitTest.class); 173f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper if (ignoreOn != null 174f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper && Arrays.asList(ignoreOn.providers()).contains(targetProvider.value())) { 175f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper return false; 176f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper } 177f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper } 178f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper 179f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper // If the runner class is annotated with @Fast, skip slow tests 180f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper if (fast != null) { 181f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper SlowTest ignoreOn = description.getAnnotation(SlowTest.class); 182f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper if (ignoreOn != null 183f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper && Arrays.asList(ignoreOn.providers()).contains(targetProvider.value())) { 184f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper return false; 185f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper } 186f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper } 187f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper 188f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper // run everything else 189f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper return true; 190f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper } 191f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper } 192f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper 193f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper /** Required constructor: called by JUnit reflectively. */ 194f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper public WycheproofRunner(Class<?> runnerClass, RunnerBuilder builder) throws InitializationError { 195f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper super(runnerClass, builder); 196f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper addFilter(new ExcludeTestFilter(runnerClass)); 197f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper } 198f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper 199f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper private void addFilter(Filter filter) { 200f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper try { 201f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper filter(filter); 202f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper } catch (NoTestsRemainException ex) { 203f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper System.out.println("No tests remain exception: " + ex); 204f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper } 205f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper } 206f7ecb0dfad2cc15d42eacefb13e8675dd664dd38Emilia Kasper} 207