xref: /aosp_15_r20/external/vogar/test/vogar/target/AbstractTestRunnerTest.java (revision e17b455832b152077f3f109e08c3a59761ae9ee5)
1*e17b4558SAndroid Build Coastguard Worker /*
2*e17b4558SAndroid Build Coastguard Worker  * Copyright (C) 2016 The Android Open Source Project
3*e17b4558SAndroid Build Coastguard Worker  *
4*e17b4558SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*e17b4558SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*e17b4558SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*e17b4558SAndroid Build Coastguard Worker  *
8*e17b4558SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*e17b4558SAndroid Build Coastguard Worker  *
10*e17b4558SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*e17b4558SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*e17b4558SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*e17b4558SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*e17b4558SAndroid Build Coastguard Worker  * limitations under the License.
15*e17b4558SAndroid Build Coastguard Worker  */
16*e17b4558SAndroid Build Coastguard Worker 
17*e17b4558SAndroid Build Coastguard Worker package vogar.target;
18*e17b4558SAndroid Build Coastguard Worker 
19*e17b4558SAndroid Build Coastguard Worker import com.google.common.base.Function;
20*e17b4558SAndroid Build Coastguard Worker import com.google.common.base.Functions;
21*e17b4558SAndroid Build Coastguard Worker import java.util.List;
22*e17b4558SAndroid Build Coastguard Worker import java.util.Properties;
23*e17b4558SAndroid Build Coastguard Worker import java.util.concurrent.atomic.AtomicInteger;
24*e17b4558SAndroid Build Coastguard Worker import org.junit.After;
25*e17b4558SAndroid Build Coastguard Worker import org.junit.Before;
26*e17b4558SAndroid Build Coastguard Worker import org.junit.Rule;
27*e17b4558SAndroid Build Coastguard Worker import vogar.Result;
28*e17b4558SAndroid Build Coastguard Worker import vogar.target.junit.JUnitUtils;
29*e17b4558SAndroid Build Coastguard Worker import vogar.testing.InterceptOutputStreams;
30*e17b4558SAndroid Build Coastguard Worker import vogar.testing.InterceptOutputStreams.Stream;
31*e17b4558SAndroid Build Coastguard Worker 
32*e17b4558SAndroid Build Coastguard Worker import static org.junit.Assert.assertEquals;
33*e17b4558SAndroid Build Coastguard Worker import static org.junit.Assert.assertTrue;
34*e17b4558SAndroid Build Coastguard Worker 
35*e17b4558SAndroid Build Coastguard Worker /**
36*e17b4558SAndroid Build Coastguard Worker  * Provides support for testing {@link TestRunner} class.
37*e17b4558SAndroid Build Coastguard Worker  *
38*e17b4558SAndroid Build Coastguard Worker  * <p>Subclasses provide the individual test methods, each test method has the following structure.
39*e17b4558SAndroid Build Coastguard Worker  *
40*e17b4558SAndroid Build Coastguard Worker  * <p>It is annotated with {@link TestRunnerProperties @TestRunnerProperties} that specifies the
41*e17b4558SAndroid Build Coastguard Worker  * properties that the main Vogar process supplies to the client process that actually runs the
42*e17b4558SAndroid Build Coastguard Worker  * tests. It must specify either a {@link TestRunnerProperties#testClass()} or
43*e17b4558SAndroid Build Coastguard Worker  * {@link TestRunnerProperties#testClassOrPackage()}, all the remaining properties are optional.
44*e17b4558SAndroid Build Coastguard Worker  *
45*e17b4558SAndroid Build Coastguard Worker  * <p>It calls {@code testRunnerRule.createTestRunner(...)} to create a {@link TestRunner}; passing
46*e17b4558SAndroid Build Coastguard Worker  * in any additional command line arguments that {@link TestRunner#TestRunner(Properties, List)}
47*e17b4558SAndroid Build Coastguard Worker  * accepts.
48*e17b4558SAndroid Build Coastguard Worker  *
49*e17b4558SAndroid Build Coastguard Worker  * <p>It calls {@link TestRunner#run()} to actually run the tests.
50*e17b4558SAndroid Build Coastguard Worker  *
51*e17b4558SAndroid Build Coastguard Worker  * <p>It calls {@link #expectedResults()} to obtain a {@link ExpectedResults} instance that it uses
52*e17b4558SAndroid Build Coastguard Worker  * to specify the expected results for the test. Once it has specified the expected results then it
53*e17b4558SAndroid Build Coastguard Worker  * must call either {@link ExpectedResults#completedNormally()} or
54*e17b4558SAndroid Build Coastguard Worker  * {@link ExpectedResults#aborted()}. They indicate whether the test process completed normally or
55*e17b4558SAndroid Build Coastguard Worker  * would abort (due to a test timing out) and cause the actual results to be checked against the
56*e17b4558SAndroid Build Coastguard Worker  * expected results.
57*e17b4558SAndroid Build Coastguard Worker  */
58*e17b4558SAndroid Build Coastguard Worker public abstract class AbstractTestRunnerTest {
59*e17b4558SAndroid Build Coastguard Worker 
60*e17b4558SAndroid Build Coastguard Worker     @Rule
61*e17b4558SAndroid Build Coastguard Worker     public InterceptOutputStreams ios = new InterceptOutputStreams(Stream.OUT);
62*e17b4558SAndroid Build Coastguard Worker 
63*e17b4558SAndroid Build Coastguard Worker     @Rule
64*e17b4558SAndroid Build Coastguard Worker     public TestRunnerRule testRunnerRule = new TestRunnerRule();
65*e17b4558SAndroid Build Coastguard Worker 
66*e17b4558SAndroid Build Coastguard Worker     /**
67*e17b4558SAndroid Build Coastguard Worker      * Keeps track of number of times {@link #expectedResults()} has been called without
68*e17b4558SAndroid Build Coastguard Worker      * {@link ExpectedResults#checkFilteredOutput(String)}
69*e17b4558SAndroid Build Coastguard Worker      * also being called. If it is {@code > 0} then the test is in error.
70*e17b4558SAndroid Build Coastguard Worker      */
71*e17b4558SAndroid Build Coastguard Worker     private AtomicInteger checkCount;
72*e17b4558SAndroid Build Coastguard Worker 
73*e17b4558SAndroid Build Coastguard Worker     @Before
beforeTest()74*e17b4558SAndroid Build Coastguard Worker     public void beforeTest() {
75*e17b4558SAndroid Build Coastguard Worker         checkCount = new AtomicInteger();
76*e17b4558SAndroid Build Coastguard Worker     }
77*e17b4558SAndroid Build Coastguard Worker 
78*e17b4558SAndroid Build Coastguard Worker     @After
afterTest()79*e17b4558SAndroid Build Coastguard Worker     public void afterTest() {
80*e17b4558SAndroid Build Coastguard Worker         if (checkCount.get() != 0) {
81*e17b4558SAndroid Build Coastguard Worker             throw new IllegalStateException("Test called expectedResults() but failed to call"
82*e17b4558SAndroid Build Coastguard Worker                     + "either aborted() or completedNormally()");
83*e17b4558SAndroid Build Coastguard Worker         }
84*e17b4558SAndroid Build Coastguard Worker     }
85*e17b4558SAndroid Build Coastguard Worker 
expectedResults()86*e17b4558SAndroid Build Coastguard Worker     protected ExpectedResults expectedResults() {
87*e17b4558SAndroid Build Coastguard Worker         checkCount.incrementAndGet();
88*e17b4558SAndroid Build Coastguard Worker         return new ExpectedResults(testRunnerRule.testClass(), ios,
89*e17b4558SAndroid Build Coastguard Worker                 checkCount);
90*e17b4558SAndroid Build Coastguard Worker     }
91*e17b4558SAndroid Build Coastguard Worker 
92*e17b4558SAndroid Build Coastguard Worker     protected static class ExpectedResults {
93*e17b4558SAndroid Build Coastguard Worker 
94*e17b4558SAndroid Build Coastguard Worker         private final StringBuilder builder = new StringBuilder();
95*e17b4558SAndroid Build Coastguard Worker         private final InterceptOutputStreams ios;
96*e17b4558SAndroid Build Coastguard Worker         private final AtomicInteger checkCount;
97*e17b4558SAndroid Build Coastguard Worker         private String testClassName;
98*e17b4558SAndroid Build Coastguard Worker         private Function<String, String> filter;
99*e17b4558SAndroid Build Coastguard Worker 
ExpectedResults( Class<?> testClass, InterceptOutputStreams ios, AtomicInteger checkCount)100*e17b4558SAndroid Build Coastguard Worker         private ExpectedResults(
101*e17b4558SAndroid Build Coastguard Worker                 Class<?> testClass, InterceptOutputStreams ios, AtomicInteger checkCount) {
102*e17b4558SAndroid Build Coastguard Worker             this.testClassName = testClass.getName();
103*e17b4558SAndroid Build Coastguard Worker             this.checkCount = checkCount;
104*e17b4558SAndroid Build Coastguard Worker             // Automatically strip out methods from a stack trace to avoid making tests dependent
105*e17b4558SAndroid Build Coastguard Worker             // on either the call hierarchy or on source line numbers which would make the tests
106*e17b4558SAndroid Build Coastguard Worker             // incredibly fragile. If a test fails then the unfiltered output containing the full
107*e17b4558SAndroid Build Coastguard Worker             // stack trace will be output so this will not lose information needed to debug errors.
108*e17b4558SAndroid Build Coastguard Worker             filter = new Function<String, String>() {
109*e17b4558SAndroid Build Coastguard Worker                 @Override
110*e17b4558SAndroid Build Coastguard Worker                 public String apply(String input) {
111*e17b4558SAndroid Build Coastguard Worker                     // Remove stack trace from output.
112*e17b4558SAndroid Build Coastguard Worker                     return input.replaceAll("\\t(at[^\\n]+|\\.\\.\\. [0-9]+ more)\\n", "");
113*e17b4558SAndroid Build Coastguard Worker                 }
114*e17b4558SAndroid Build Coastguard Worker             };
115*e17b4558SAndroid Build Coastguard Worker             this.ios = ios;
116*e17b4558SAndroid Build Coastguard Worker         }
117*e17b4558SAndroid Build Coastguard Worker 
text(String message)118*e17b4558SAndroid Build Coastguard Worker         public ExpectedResults text(String message) {
119*e17b4558SAndroid Build Coastguard Worker             builder.append(message);
120*e17b4558SAndroid Build Coastguard Worker             return this;
121*e17b4558SAndroid Build Coastguard Worker         }
122*e17b4558SAndroid Build Coastguard Worker 
addFilter(Function<String, String> function)123*e17b4558SAndroid Build Coastguard Worker         private ExpectedResults addFilter(Function<String, String> function) {
124*e17b4558SAndroid Build Coastguard Worker             filter = Functions.compose(filter, function);
125*e17b4558SAndroid Build Coastguard Worker             return this;
126*e17b4558SAndroid Build Coastguard Worker         }
127*e17b4558SAndroid Build Coastguard Worker 
forTestClass(Class<?> testClass)128*e17b4558SAndroid Build Coastguard Worker         public ExpectedResults forTestClass(Class<?> testClass) {
129*e17b4558SAndroid Build Coastguard Worker             this.testClassName = testClass.getName();
130*e17b4558SAndroid Build Coastguard Worker             return this;
131*e17b4558SAndroid Build Coastguard Worker         }
132*e17b4558SAndroid Build Coastguard Worker 
forTestClass(String testClassName)133*e17b4558SAndroid Build Coastguard Worker         public ExpectedResults forTestClass(String testClassName) {
134*e17b4558SAndroid Build Coastguard Worker             this.testClassName = testClassName;
135*e17b4558SAndroid Build Coastguard Worker             return this;
136*e17b4558SAndroid Build Coastguard Worker         }
137*e17b4558SAndroid Build Coastguard Worker 
failure(String methodName, String message)138*e17b4558SAndroid Build Coastguard Worker         public ExpectedResults failure(String methodName, String message) {
139*e17b4558SAndroid Build Coastguard Worker             String output = outcome(testClassName, methodName, message, Result.EXEC_FAILED);
140*e17b4558SAndroid Build Coastguard Worker             return text(output);
141*e17b4558SAndroid Build Coastguard Worker         }
142*e17b4558SAndroid Build Coastguard Worker 
success(String methodName)143*e17b4558SAndroid Build Coastguard Worker         public ExpectedResults success(String methodName) {
144*e17b4558SAndroid Build Coastguard Worker             String output = outcome(testClassName, methodName, null, Result.SUCCESS);
145*e17b4558SAndroid Build Coastguard Worker             return text(output);
146*e17b4558SAndroid Build Coastguard Worker         }
147*e17b4558SAndroid Build Coastguard Worker 
success(String methodName, String message)148*e17b4558SAndroid Build Coastguard Worker         public ExpectedResults success(String methodName, String message) {
149*e17b4558SAndroid Build Coastguard Worker             String output = outcome(testClassName, methodName, message, Result.SUCCESS);
150*e17b4558SAndroid Build Coastguard Worker             return text(output);
151*e17b4558SAndroid Build Coastguard Worker         }
152*e17b4558SAndroid Build Coastguard Worker 
153*e17b4558SAndroid Build Coastguard Worker 
unsupported()154*e17b4558SAndroid Build Coastguard Worker         public ExpectedResults unsupported() {
155*e17b4558SAndroid Build Coastguard Worker             String output = outcome(
156*e17b4558SAndroid Build Coastguard Worker                     testClassName, null,
157*e17b4558SAndroid Build Coastguard Worker                     "Skipping " + testClassName + ": no associated runner class\n",
158*e17b4558SAndroid Build Coastguard Worker                     Result.UNSUPPORTED);
159*e17b4558SAndroid Build Coastguard Worker             return text(output);
160*e17b4558SAndroid Build Coastguard Worker         }
161*e17b4558SAndroid Build Coastguard Worker 
noRunner()162*e17b4558SAndroid Build Coastguard Worker         public ExpectedResults noRunner() {
163*e17b4558SAndroid Build Coastguard Worker             String message =
164*e17b4558SAndroid Build Coastguard Worker                     String.format("Skipping %s: no associated runner class\n", testClassName);
165*e17b4558SAndroid Build Coastguard Worker             String output = outcome(testClassName, null, message, Result.UNSUPPORTED);
166*e17b4558SAndroid Build Coastguard Worker             return text(output);
167*e17b4558SAndroid Build Coastguard Worker         }
168*e17b4558SAndroid Build Coastguard Worker 
completedNormally()169*e17b4558SAndroid Build Coastguard Worker         public void completedNormally() {
170*e17b4558SAndroid Build Coastguard Worker             text("//00xx{\"completedNormally\":true}\n");
171*e17b4558SAndroid Build Coastguard Worker             checkFilteredOutput(builder.toString());
172*e17b4558SAndroid Build Coastguard Worker         }
173*e17b4558SAndroid Build Coastguard Worker 
aborted()174*e17b4558SAndroid Build Coastguard Worker         public void aborted() {
175*e17b4558SAndroid Build Coastguard Worker             checkFilteredOutput(builder.toString());
176*e17b4558SAndroid Build Coastguard Worker         }
177*e17b4558SAndroid Build Coastguard Worker 
178*e17b4558SAndroid Build Coastguard Worker 
outcome( String testClassName, String methodName, String message, Result result)179*e17b4558SAndroid Build Coastguard Worker         private static String outcome(
180*e17b4558SAndroid Build Coastguard Worker                 String testClassName, String methodName, String message, Result result) {
181*e17b4558SAndroid Build Coastguard Worker             String testName = JUnitUtils.getTestName(testClassName, methodName);
182*e17b4558SAndroid Build Coastguard Worker 
183*e17b4558SAndroid Build Coastguard Worker             return String.format("//00xx{\"outcome\":\"%s\"}\n"
184*e17b4558SAndroid Build Coastguard Worker                             + "%s"
185*e17b4558SAndroid Build Coastguard Worker                             + "//00xx{\"result\":\"%s\"}\n",
186*e17b4558SAndroid Build Coastguard Worker                     testName, message == null ? "" : message, result);
187*e17b4558SAndroid Build Coastguard Worker         }
188*e17b4558SAndroid Build Coastguard Worker 
checkFilteredOutput(String expected)189*e17b4558SAndroid Build Coastguard Worker         private void checkFilteredOutput(String expected) {
190*e17b4558SAndroid Build Coastguard Worker             checkCount.decrementAndGet();
191*e17b4558SAndroid Build Coastguard Worker             String output = ios.contents(Stream.OUT);
192*e17b4558SAndroid Build Coastguard Worker             String filtered = filter.apply(output);
193*e17b4558SAndroid Build Coastguard Worker             if (!expected.equals(filtered)) {
194*e17b4558SAndroid Build Coastguard Worker                 assertEquals(expected, output);
195*e17b4558SAndroid Build Coastguard Worker             }
196*e17b4558SAndroid Build Coastguard Worker         }
197*e17b4558SAndroid Build Coastguard Worker     }
198*e17b4558SAndroid Build Coastguard Worker }
199