Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 1 | // Copyright 2006-2015 Google Inc. All rights reserved. |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | package com.google.devtools.build.lib.syntax; |
| 15 | |
| 16 | import static com.google.common.truth.Truth.assertThat; |
| 17 | import static org.junit.Assert.fail; |
| 18 | |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 19 | import com.google.common.truth.Ordered; |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 20 | import com.google.devtools.build.lib.events.Event; |
| 21 | import com.google.devtools.build.lib.events.EventCollector; |
| 22 | import com.google.devtools.build.lib.events.EventHandler; |
| 23 | import com.google.devtools.build.lib.events.EventKind; |
| 24 | import com.google.devtools.build.lib.events.util.EventCollectionApparatus; |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 25 | import com.google.devtools.build.lib.packages.PackageFactory; |
| 26 | import com.google.devtools.build.lib.testutil.TestMode; |
| 27 | import com.google.devtools.build.lib.testutil.TestRuleClassProvider; |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 28 | |
| 29 | import org.junit.Before; |
| 30 | |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 31 | import java.util.LinkedList; |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 32 | import java.util.List; |
| 33 | |
| 34 | /** |
| 35 | * Base class for test cases that use parsing and evaluation services. |
| 36 | */ |
| 37 | public class EvaluationTestCase { |
Francois-Rene Rideau | 89312fb | 2015-09-10 18:53:03 +0000 | [diff] [blame] | 38 | |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 39 | private EventCollectionApparatus eventCollectionApparatus; |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 40 | private PackageFactory factory; |
| 41 | private TestMode testMode = TestMode.SKYLARK; |
Francois-Rene Rideau | 89312fb | 2015-09-10 18:53:03 +0000 | [diff] [blame] | 42 | protected Environment env; |
| 43 | protected Mutability mutability = Mutability.create("test"); |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 44 | |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 45 | public EvaluationTestCase() { |
| 46 | createNewInfrastructure(); |
| 47 | } |
Francois-Rene Rideau | 89312fb | 2015-09-10 18:53:03 +0000 | [diff] [blame] | 48 | |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 49 | @Before |
| 50 | public void setUp() throws Exception { |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 51 | createNewInfrastructure(); |
Francois-Rene Rideau | 89312fb | 2015-09-10 18:53:03 +0000 | [diff] [blame] | 52 | env = newEnvironment(); |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 53 | } |
| 54 | |
Francois-Rene Rideau | 89312fb | 2015-09-10 18:53:03 +0000 | [diff] [blame] | 55 | /** |
| 56 | * Creates a standard Environment for tests in the BUILD language. |
| 57 | * No PythonPreprocessing, mostly empty mutable Environment. |
| 58 | */ |
| 59 | public Environment newBuildEnvironment() { |
| 60 | return Environment.builder(mutability) |
| 61 | .setGlobals(Environment.BUILD) |
| 62 | .setEventHandler(getEventHandler()) |
| 63 | .setLoadingPhase() |
| 64 | .build(); |
| 65 | } |
| 66 | |
| 67 | /** |
| 68 | * Creates an Environment for Skylark with a mostly empty initial environment. |
| 69 | * For internal initialization or tests. |
| 70 | */ |
| 71 | public Environment newSkylarkEnvironment() { |
| 72 | return Environment.builder(mutability) |
| 73 | .setSkylark() |
| 74 | .setGlobals(Environment.SKYLARK) |
| 75 | .setEventHandler(getEventHandler()) |
| 76 | .build(); |
| 77 | } |
| 78 | |
| 79 | /** |
| 80 | * Creates a new Environment suitable for the test case. Subclasses may override it |
| 81 | * to fit their purpose and e.g. call newBuildEnvironment or newSkylarkEnvironment; |
| 82 | * or they may play with the testMode to run tests in either or both kinds of Environment. |
| 83 | * Note that all Environment-s may share the same Mutability, so don't close it. |
| 84 | * @return a fresh Environment. |
| 85 | */ |
| 86 | public Environment newEnvironment() throws Exception { |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 87 | if (testMode == null) { |
| 88 | throw new IllegalArgumentException( |
| 89 | "TestMode is null. Please set a Testmode via setMode() or set the " |
Francois-Rene Rideau | 89312fb | 2015-09-10 18:53:03 +0000 | [diff] [blame] | 90 | + "Environment manually by overriding newEnvironment()"); |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 91 | } |
Francois-Rene Rideau | 89312fb | 2015-09-10 18:53:03 +0000 | [diff] [blame] | 92 | return testMode.createEnvironment(getEventHandler(), null); |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 93 | } |
| 94 | |
| 95 | protected void createNewInfrastructure() { |
| 96 | eventCollectionApparatus = new EventCollectionApparatus(EventKind.ALL_EVENTS); |
| 97 | factory = new PackageFactory(TestRuleClassProvider.getRuleClassProvider()); |
| 98 | } |
Francois-Rene Rideau | 89312fb | 2015-09-10 18:53:03 +0000 | [diff] [blame] | 99 | |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 100 | /** |
Francois-Rene Rideau | 89312fb | 2015-09-10 18:53:03 +0000 | [diff] [blame] | 101 | * Sets the specified {@code TestMode} and tries to create the appropriate {@code Environment} |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 102 | * @param testMode |
| 103 | * @throws Exception |
| 104 | */ |
| 105 | protected void setMode(TestMode testMode) throws Exception { |
| 106 | this.testMode = testMode; |
Francois-Rene Rideau | 89312fb | 2015-09-10 18:53:03 +0000 | [diff] [blame] | 107 | env = newEnvironment(); |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 108 | } |
| 109 | |
| 110 | protected void enableSkylarkMode() throws Exception { |
| 111 | setMode(TestMode.SKYLARK); |
| 112 | } |
| 113 | |
| 114 | protected void enableBuildMode() throws Exception { |
| 115 | setMode(TestMode.BUILD); |
| 116 | } |
| 117 | |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 118 | protected EventHandler getEventHandler() { |
| 119 | return eventCollectionApparatus.reporter(); |
| 120 | } |
| 121 | |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 122 | protected PackageFactory getFactory() { |
| 123 | return factory; |
| 124 | } |
| 125 | |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 126 | public Environment getEnvironment() { |
Francois-Rene Rideau | 89312fb | 2015-09-10 18:53:03 +0000 | [diff] [blame] | 127 | return env; |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 128 | } |
Francois-Rene Rideau | 89312fb | 2015-09-10 18:53:03 +0000 | [diff] [blame] | 129 | |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 130 | public boolean isSkylark() { |
Francois-Rene Rideau | 89312fb | 2015-09-10 18:53:03 +0000 | [diff] [blame] | 131 | return env.isSkylark(); |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 132 | } |
| 133 | |
| 134 | protected List<Statement> parseFile(String... input) { |
Francois-Rene Rideau | 89312fb | 2015-09-10 18:53:03 +0000 | [diff] [blame] | 135 | return env.parseFile(input); |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 136 | } |
| 137 | |
Francois-Rene Rideau | 89312fb | 2015-09-10 18:53:03 +0000 | [diff] [blame] | 138 | /** Parses an Expression from string without a supporting file */ |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 139 | Expression parseExpression(String... input) { |
Lukacs Berki | afc93a6 | 2015-09-17 13:09:03 +0000 | [diff] [blame^] | 140 | return Parser.parseExpression(env.createLexer(input), getEventHandler()); |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 141 | } |
| 142 | |
| 143 | public EvaluationTestCase update(String varname, Object value) throws Exception { |
Francois-Rene Rideau | 89312fb | 2015-09-10 18:53:03 +0000 | [diff] [blame] | 144 | env.update(varname, value); |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 145 | return this; |
| 146 | } |
| 147 | |
| 148 | public Object lookup(String varname) throws Exception { |
Francois-Rene Rideau | 89312fb | 2015-09-10 18:53:03 +0000 | [diff] [blame] | 149 | return env.lookup(varname); |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 150 | } |
| 151 | |
| 152 | public Object eval(String... input) throws Exception { |
Francois-Rene Rideau | 89312fb | 2015-09-10 18:53:03 +0000 | [diff] [blame] | 153 | return env.eval(input); |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 154 | } |
| 155 | |
| 156 | public void checkEvalError(String msg, String... input) throws Exception { |
| 157 | setFailFast(true); |
| 158 | try { |
| 159 | eval(input); |
Laurent Le Brun | 092f13b | 2015-08-24 14:50:00 +0000 | [diff] [blame] | 160 | fail("Expected error '" + msg + "' but got no error"); |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 161 | } catch (IllegalArgumentException | EvalException e) { |
| 162 | assertThat(e).hasMessage(msg); |
| 163 | } |
| 164 | } |
| 165 | |
| 166 | public void checkEvalErrorContains(String msg, String... input) throws Exception { |
| 167 | try { |
| 168 | eval(input); |
Laurent Le Brun | 092f13b | 2015-08-24 14:50:00 +0000 | [diff] [blame] | 169 | fail("Expected error containing '" + msg + "' but got no error"); |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 170 | } catch (IllegalArgumentException | EvalException e) { |
| 171 | assertThat(e.getMessage()).contains(msg); |
| 172 | } |
| 173 | } |
| 174 | |
| 175 | public void checkEvalErrorStartsWith(String msg, String... input) throws Exception { |
| 176 | try { |
| 177 | eval(input); |
Laurent Le Brun | 092f13b | 2015-08-24 14:50:00 +0000 | [diff] [blame] | 178 | fail("Expected error starting with '" + msg + "' but got no error"); |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 179 | } catch (IllegalArgumentException | EvalException e) { |
| 180 | assertThat(e.getMessage()).startsWith(msg); |
| 181 | } |
| 182 | } |
| 183 | |
| 184 | // Forward relevant methods to the EventCollectionApparatus |
| 185 | public EvaluationTestCase setFailFast(boolean failFast) { |
| 186 | eventCollectionApparatus.setFailFast(failFast); |
| 187 | return this; |
| 188 | } |
| 189 | public EvaluationTestCase assertNoEvents() { |
| 190 | eventCollectionApparatus.assertNoEvents(); |
| 191 | return this; |
| 192 | } |
| 193 | public EventCollector getEventCollector() { |
| 194 | return eventCollectionApparatus.collector(); |
| 195 | } |
| 196 | public Event assertContainsEvent(String expectedMessage) { |
| 197 | return eventCollectionApparatus.assertContainsEvent(expectedMessage); |
| 198 | } |
| 199 | public List<Event> assertContainsEventWithFrequency(String expectedMessage, |
| 200 | int expectedFrequency) { |
| 201 | return eventCollectionApparatus.assertContainsEventWithFrequency( |
| 202 | expectedMessage, expectedFrequency); |
| 203 | } |
| 204 | public Event assertContainsEventWithWordsInQuotes(String... words) { |
| 205 | return eventCollectionApparatus.assertContainsEventWithWordsInQuotes(words); |
| 206 | } |
| 207 | public EvaluationTestCase clearEvents() { |
| 208 | eventCollectionApparatus.collector().clear(); |
| 209 | return this; |
| 210 | } |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 211 | |
| 212 | /** |
| 213 | * Encapsulates a separate test which can be executed by a {@code TestMode} |
| 214 | */ |
| 215 | protected interface Testable { |
| 216 | public void run() throws Exception; |
| 217 | } |
| 218 | |
| 219 | /** |
| 220 | * Base class for test cases that run in specific modes (e.g. Build and/or Skylark) |
Francois-Rene Rideau | 89312fb | 2015-09-10 18:53:03 +0000 | [diff] [blame] | 221 | */ |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 222 | protected abstract class ModalTestCase { |
| 223 | private final SetupActions setup; |
Francois-Rene Rideau | 89312fb | 2015-09-10 18:53:03 +0000 | [diff] [blame] | 224 | |
| 225 | protected ModalTestCase() { |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 226 | setup = new SetupActions(); |
| 227 | } |
Francois-Rene Rideau | 89312fb | 2015-09-10 18:53:03 +0000 | [diff] [blame] | 228 | |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 229 | /** |
| 230 | * Allows the execution of several statements before each following test |
| 231 | * @param statements The statement(s) to be executed |
| 232 | * @return This {@code ModalTestCase} |
| 233 | */ |
| 234 | public ModalTestCase setUp(String... statements) { |
| 235 | setup.registerEval(statements); |
| 236 | return this; |
| 237 | } |
| 238 | |
| 239 | /** |
| 240 | * Allows the update of the specified variable before each following test |
| 241 | * @param name The name of the variable that should be updated |
| 242 | * @param value The new value of the variable |
| 243 | * @return This {@code ModalTestCase} |
| 244 | */ |
| 245 | public ModalTestCase update(String name, Object value) { |
| 246 | setup.registerUpdate(name, value); |
| 247 | return this; |
| 248 | } |
Francois-Rene Rideau | 89312fb | 2015-09-10 18:53:03 +0000 | [diff] [blame] | 249 | |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 250 | /** |
| 251 | * Evaluates two parameters and compares their results. |
| 252 | * @param statement The statement to be evaluated |
| 253 | * @param expectedEvalString The expression of the expected result |
| 254 | * @return This {@code ModalTestCase} |
| 255 | * @throws Exception |
| 256 | */ |
| 257 | public ModalTestCase testEval(String statement, String expectedEvalString) throws Exception { |
| 258 | runTest(createComparisonTestable(statement, expectedEvalString, true)); |
| 259 | return this; |
| 260 | } |
| 261 | |
| 262 | /** |
| 263 | * Evaluates the given statement and compares its result to the expected object |
| 264 | * @param statement |
| 265 | * @param expected |
| 266 | * @return This {@code ModalTestCase} |
| 267 | * @throws Exception |
| 268 | */ |
| 269 | public ModalTestCase testStatement(String statement, Object expected) throws Exception { |
| 270 | runTest(createComparisonTestable(statement, expected, false)); |
| 271 | return this; |
| 272 | } |
| 273 | |
| 274 | /** |
| 275 | * Evaluates the given statement and compares its result to the collection of expected objects |
| 276 | * without considering their order |
| 277 | * @param statement The statement to be evaluated |
| 278 | * @param items The expected items |
| 279 | * @return This {@code ModalTestCase} |
| 280 | * @throws Exception |
| 281 | */ |
| 282 | public ModalTestCase testCollection(String statement, Object... items) throws Exception { |
| 283 | runTest(collectionTestable(statement, false, items)); |
| 284 | return this; |
| 285 | } |
Francois-Rene Rideau | 89312fb | 2015-09-10 18:53:03 +0000 | [diff] [blame] | 286 | |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 287 | /** |
| 288 | * Evaluates the given statement and compares its result to the collection of expected objects |
| 289 | * while considering their order |
| 290 | * @param statement The statement to be evaluated |
| 291 | * @param items The expected items, in order |
| 292 | * @return This {@code ModalTestCase} |
| 293 | * @throws Exception |
| 294 | */ |
| 295 | public ModalTestCase testExactOrder(String statement, Object... items) throws Exception { |
| 296 | runTest(collectionTestable(statement, true, items)); |
| 297 | return this; |
| 298 | } |
| 299 | |
| 300 | /** |
| 301 | * Evaluates the given statement and checks whether the given error message appears |
| 302 | * @param expectedError The expected error message |
| 303 | * @param statements The statement(s) to be evaluated |
| 304 | * @return This ModalTestCase |
| 305 | * @throws Exception |
| 306 | */ |
| 307 | public ModalTestCase testIfExactError(String expectedError, String... statements) |
| 308 | throws Exception { |
| 309 | runTest(errorTestable(true, expectedError, statements)); |
| 310 | return this; |
| 311 | } |
| 312 | |
| 313 | /** |
| 314 | * Evaluates the given statement and checks whether an error that contains the expected message |
| 315 | * occurs |
| 316 | * @param expectedError |
| 317 | * @param statements |
| 318 | * @return This ModalTestCase |
| 319 | * @throws Exception |
| 320 | */ |
| 321 | public ModalTestCase testIfErrorContains(String expectedError, String... statements) |
| 322 | throws Exception { |
| 323 | runTest(errorTestable(false, expectedError, statements)); |
| 324 | return this; |
| 325 | } |
| 326 | |
| 327 | /** |
| 328 | * Looks up the value of the specified variable and compares it to the expected value |
| 329 | * @param name |
| 330 | * @param expected |
| 331 | * @return This ModalTestCase |
| 332 | * @throws Exception |
| 333 | */ |
| 334 | public ModalTestCase testLookup(String name, Object expected) throws Exception { |
| 335 | runTest(createLookUpTestable(name, expected)); |
| 336 | return this; |
| 337 | } |
| 338 | |
| 339 | /** |
| 340 | * Creates a Testable that checks whether the evaluation of the given statement leads to the |
| 341 | * expected error |
| 342 | * @param statements |
| 343 | * @param error |
| 344 | * @param exactMatch If true, the error message has to be identical to the expected error |
| 345 | * @return An instance of Testable that runs the error check |
| 346 | */ |
| 347 | protected Testable errorTestable(final boolean exactMatch, final String error, |
| 348 | final String... statements) { |
| 349 | return new Testable() { |
| 350 | @Override |
| 351 | public void run() throws Exception { |
| 352 | if (exactMatch) { |
| 353 | checkEvalError(error, statements); |
| 354 | } else { |
| 355 | checkEvalErrorContains(error, statements); |
| 356 | } |
| 357 | } |
| 358 | }; |
| 359 | } |
| 360 | |
| 361 | /** |
| 362 | * Creates a testable that checks whether the evaluation of the given statement leads to a list |
| 363 | * that contains exactly the expected objects |
| 364 | * @param statement The statement to be evaluated |
| 365 | * @param ordered Determines whether the order of the elements is checked as well |
| 366 | * @param expected Expected objects |
| 367 | * @return An instance of Testable that runs the check |
| 368 | */ |
| 369 | protected Testable collectionTestable( |
| 370 | final String statement, final boolean ordered, final Object... expected) { |
| 371 | return new Testable() { |
| 372 | @Override |
| 373 | public void run() throws Exception { |
| 374 | Ordered tmp = assertThat((Iterable<?>) eval(statement)).containsExactly(expected); |
| 375 | |
| 376 | if (ordered) { |
| 377 | tmp.inOrder(); |
| 378 | } |
| 379 | } |
| 380 | }; |
| 381 | } |
| 382 | |
| 383 | /** |
| 384 | * Creates a testable that compares the evaluation of the given statement to a specified result |
| 385 | * |
| 386 | * @param statement The statement to be evaluated |
| 387 | * @param expected Either the expected object or an expression whose evaluation leads to the |
| 388 | * expected object |
| 389 | * @param expectedIsExpression Signals whether {@code expected} is an object or an expression |
| 390 | * @return An instance of Testable that runs the comparison |
| 391 | */ |
| 392 | protected Testable createComparisonTestable( |
| 393 | final String statement, final Object expected, final boolean expectedIsExpression) { |
| 394 | return new Testable() { |
| 395 | @Override |
| 396 | public void run() throws Exception { |
| 397 | Object actual = eval(statement); |
Florian Weikert | f31b947 | 2015-08-04 16:36:58 +0000 | [diff] [blame] | 398 | Object realExpected = expected; |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 399 | |
Florian Weikert | f31b947 | 2015-08-04 16:36:58 +0000 | [diff] [blame] | 400 | // We could also print the actual object and compare the string to the expected |
| 401 | // expression, but then the order of elements would matter. |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 402 | if (expectedIsExpression) { |
Florian Weikert | f31b947 | 2015-08-04 16:36:58 +0000 | [diff] [blame] | 403 | realExpected = eval((String) expected); |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 404 | } |
| 405 | |
Florian Weikert | f31b947 | 2015-08-04 16:36:58 +0000 | [diff] [blame] | 406 | assertThat(actual).isEqualTo(realExpected); |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 407 | } |
| 408 | }; |
| 409 | } |
| 410 | |
| 411 | /** |
| 412 | * Creates a Testable that looks up the given variable and compares its value to the expected |
| 413 | * value |
| 414 | * @param name |
| 415 | * @param expected |
| 416 | * @return An instance of Testable that does both lookup and comparison |
| 417 | */ |
| 418 | protected Testable createLookUpTestable(final String name, final Object expected) { |
| 419 | return new Testable() { |
| 420 | @Override |
| 421 | public void run() throws Exception { |
| 422 | assertThat(lookup(name)).isEqualTo(expected); |
| 423 | } |
| 424 | }; |
| 425 | } |
| 426 | |
| 427 | /** |
| 428 | * Executes the given Testable |
| 429 | * @param testable |
| 430 | * @throws Exception |
| 431 | */ |
| 432 | protected void runTest(Testable testable) throws Exception { |
| 433 | run(new TestableDecorator(setup, testable)); |
| 434 | } |
| 435 | |
| 436 | protected abstract void run(Testable testable) throws Exception; |
| 437 | } |
| 438 | |
| 439 | /** |
Francois-Rene Rideau | 89312fb | 2015-09-10 18:53:03 +0000 | [diff] [blame] | 440 | * A simple decorator that allows the execution of setup actions before running |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 441 | * a {@code Testable} |
| 442 | */ |
| 443 | class TestableDecorator implements Testable { |
| 444 | private final SetupActions setup; |
| 445 | private final Testable decorated; |
| 446 | |
| 447 | public TestableDecorator(SetupActions setup, Testable decorated) { |
| 448 | this.setup = setup; |
| 449 | this.decorated = decorated; |
| 450 | } |
| 451 | |
| 452 | /** |
| 453 | * Executes all stored actions and updates plus the actual {@code Testable} |
| 454 | */ |
| 455 | @Override |
| 456 | public void run() throws Exception { |
| 457 | setup.executeAll(); |
| 458 | decorated.run(); |
| 459 | } |
| 460 | } |
| 461 | |
| 462 | /** |
| 463 | * A container for collection actions that should be executed before a test |
| 464 | */ |
| 465 | class SetupActions { |
| 466 | private List<Testable> setup; |
| 467 | |
| 468 | public SetupActions() { |
| 469 | setup = new LinkedList<>(); |
| 470 | } |
| 471 | |
| 472 | /** |
| 473 | * Registers a variable that has to be updated before a test |
| 474 | * |
| 475 | * @param name |
| 476 | * @param value |
| 477 | */ |
| 478 | public void registerUpdate(final String name, final Object value) { |
| 479 | setup.add(new Testable() { |
| 480 | @Override |
| 481 | public void run() throws Exception { |
| 482 | EvaluationTestCase.this.update(name, value); |
| 483 | } |
| 484 | }); |
| 485 | } |
| 486 | |
| 487 | /** |
| 488 | * Registers a statement for evaluation prior to a test |
| 489 | * |
| 490 | * @param statements |
| 491 | */ |
| 492 | public void registerEval(final String... statements) { |
| 493 | setup.add(new Testable() { |
| 494 | @Override |
| 495 | public void run() throws Exception { |
| 496 | EvaluationTestCase.this.eval(statements); |
| 497 | } |
| 498 | }); |
| 499 | } |
| 500 | |
| 501 | /** |
| 502 | * Executes all stored actions and updates |
| 503 | * @throws Exception |
| 504 | */ |
| 505 | public void executeAll() throws Exception { |
| 506 | for (Testable testable : setup) { |
| 507 | testable.run(); |
| 508 | } |
| 509 | } |
| 510 | } |
Francois-Rene Rideau | 89312fb | 2015-09-10 18:53:03 +0000 | [diff] [blame] | 511 | |
Florian Weikert | 28da365 | 2015-07-01 14:52:30 +0000 | [diff] [blame] | 512 | /** |
| 513 | * A class that executes each separate test in both modes (Build and Skylark) |
| 514 | */ |
| 515 | protected class BothModesTest extends ModalTestCase { |
| 516 | public BothModesTest() {} |
| 517 | |
| 518 | /** |
| 519 | * Executes the given Testable in both Build and Skylark mode |
| 520 | */ |
| 521 | @Override |
| 522 | protected void run(Testable testable) throws Exception { |
| 523 | enableSkylarkMode(); |
| 524 | try { |
| 525 | testable.run(); |
| 526 | } catch (Exception e) { |
| 527 | throw new Exception("While in Skylark mode", e); |
| 528 | } |
| 529 | |
| 530 | enableBuildMode(); |
| 531 | try { |
| 532 | testable.run(); |
| 533 | } catch (Exception e) { |
| 534 | throw new Exception("While in Build mode", e); |
| 535 | } |
| 536 | } |
| 537 | } |
| 538 | |
| 539 | /** |
| 540 | * A class that runs all tests in Build mode |
| 541 | */ |
| 542 | protected class BuildTest extends ModalTestCase { |
| 543 | public BuildTest() {} |
| 544 | |
| 545 | @Override |
| 546 | protected void run(Testable testable) throws Exception { |
| 547 | enableBuildMode(); |
| 548 | testable.run(); |
| 549 | } |
| 550 | } |
| 551 | |
| 552 | /** |
| 553 | * A class that runs all tests in Skylark mode |
| 554 | */ |
| 555 | protected class SkylarkTest extends ModalTestCase { |
| 556 | public SkylarkTest() {} |
| 557 | |
| 558 | @Override |
| 559 | protected void run(Testable testable) throws Exception { |
| 560 | enableSkylarkMode(); |
| 561 | testable.run(); |
| 562 | } |
| 563 | } |
Francois-Rene Rideau | 5f3e30c | 2015-04-10 19:08:39 +0000 | [diff] [blame] | 564 | } |