|
58 | 58 | import java.util.regex.Matcher; |
59 | 59 | import java.util.regex.Pattern; |
60 | 60 | import java.util.stream.Collectors; |
| 61 | +import java.util.stream.Stream; |
61 | 62 | import javax.tools.StandardJavaFileManager; |
62 | 63 |
|
63 | 64 |
|
@@ -266,39 +267,129 @@ void check(Path dir) { |
266 | 267 |
|
267 | 268 | /** |
268 | 269 | * Run all methods annotated with @Test, followed by printSummary. |
269 | | - * Typically called on a tester object in main() |
| 270 | + * The methods are invoked in the order found using getDeclaredMethods. |
| 271 | + * The arguments for the invocation are provided {@link #getTestArgs(Method)}. |
| 272 | + * |
| 273 | + * Typically called on a tester object in main(). |
270 | 274 | * |
| 275 | + * @throws IllegalArgumentException if any test method does not have a recognized signature |
271 | 276 | * @throws Exception if any errors occurred |
272 | 277 | */ |
273 | 278 | public void runTests() throws Exception { |
274 | | - runTests(m -> new Object[0]); |
| 279 | + runTests(this::getTestArgs); |
275 | 280 | } |
276 | 281 |
|
277 | 282 | /** |
278 | 283 | * Runs all methods annotated with @Test, followed by printSummary. |
| 284 | + * The methods are invoked in the order found using getDeclaredMethods. |
| 285 | + * The arguments for the invocation are provided by a given function. |
| 286 | + * |
279 | 287 | * Typically called on a tester object in main() |
280 | 288 | * |
281 | 289 | * @param f a function which will be used to provide arguments to each |
282 | 290 | * invoked method |
283 | | - * @throws Exception if any errors occurred |
| 291 | + * @throws Exception if any errors occurred while executing a test method |
284 | 292 | */ |
285 | 293 | public void runTests(Function<Method, Object[]> f) throws Exception { |
286 | | - for (Method m: getClass().getDeclaredMethods()) { |
| 294 | + for (Method m : getClass().getDeclaredMethods()) { |
287 | 295 | Annotation a = m.getAnnotation(Test.class); |
288 | 296 | if (a != null) { |
289 | | - try { |
290 | | - out.println("Running test " + m.getName()); |
291 | | - m.invoke(this, f.apply(m)); |
292 | | - } catch (InvocationTargetException e) { |
293 | | - Throwable cause = e.getCause(); |
294 | | - throw (cause instanceof Exception) ? ((Exception) cause) : e; |
295 | | - } |
| 297 | + runTest(m, f); |
296 | 298 | out.println(); |
297 | 299 | } |
298 | 300 | } |
299 | 301 | printSummary(); |
300 | 302 | } |
301 | 303 |
|
| 304 | + /** |
| 305 | + * Run the specified methods annotated with @Test, or all methods annotated |
| 306 | + * with @Test if none are specified, followed by printSummary. |
| 307 | + * The methods are invoked in the order given in the methodNames argument, |
| 308 | + * or the order returned by getDeclaredMethods if no names are provided. |
| 309 | + * The arguments for the invocation are provided {@link #getTestArgs(Method)}. |
| 310 | + * |
| 311 | + * Typically called on a tester object in main(String[] args), perhaps using |
| 312 | + * args as the list of method names. |
| 313 | + * |
| 314 | + * @throws IllegalStateException if any methods annotated with @Test are overloaded |
| 315 | + * @throws IllegalArgumentException if any of the method names does not name a suitable method |
| 316 | + * @throws NullPointerException if {@code methodNames} is {@code null}, or if any of the names are {@code null} |
| 317 | + * @throws Exception if any errors occurred while executing a test method |
| 318 | + */ |
| 319 | + public void runTests(String... methodNames) throws Exception { |
| 320 | + runTests(this::getTestArgs, methodNames); |
| 321 | + } |
| 322 | + |
| 323 | + /** |
| 324 | + * Run the specified methods annotated with @Test, or all methods annotated |
| 325 | + * with @Test if non are specified, followed by printSummary. |
| 326 | + * The methods are invoked in the order given in the methodNames argument, |
| 327 | + * or the order returned by getDeclaredMethods if no names are provided. |
| 328 | + * The arguments for the invocation are provided {@link #getTestArgs(Method)}. |
| 329 | + * |
| 330 | + * Typically called on a tester object in main(String[] args), perhaps using |
| 331 | + * args as the list of method names. |
| 332 | + * |
| 333 | + * @throws IllegalStateException if any methods annotated with @Test are overloaded |
| 334 | + * @throws IllegalArgumentException if any of the method names does not name a suitable method |
| 335 | + * @throws NullPointerException if {@code methodNames} is {@code null}, or if any of the names are {@code null} |
| 336 | + * @throws Exception if any errors occurred while executing a test method |
| 337 | + */ |
| 338 | + public void runTests(Function<Method, Object[]> f, String... methodNames) throws Exception { |
| 339 | + if (methodNames.length == 0) { |
| 340 | + runTests(f); |
| 341 | + } else { |
| 342 | + Map<String, Method> testMethods = Stream.of(getClass().getDeclaredMethods()) |
| 343 | + .filter(this::isTestMethod) |
| 344 | + .collect(Collectors.toMap(Method::getName, Function.identity(), |
| 345 | + (o, n) -> { |
| 346 | + throw new IllegalStateException("test method " + o.getName() + " is overloaded"); |
| 347 | + })); |
| 348 | + |
| 349 | + List<Method> list = new ArrayList<>(); |
| 350 | + for (String mn : methodNames) { |
| 351 | + Method m = testMethods.get(mn); |
| 352 | + if (m == null) { |
| 353 | + throw new IllegalArgumentException("test method " + mn + " not found"); |
| 354 | + } |
| 355 | + list.add(m); |
| 356 | + } |
| 357 | + |
| 358 | + for (Method m : list) { |
| 359 | + runTest(m, f); |
| 360 | + } |
| 361 | + } |
| 362 | + } |
| 363 | + |
| 364 | + protected boolean isTestMethod(Method m) { |
| 365 | + return m.getAnnotation(Test.class) != null; |
| 366 | + } |
| 367 | + |
| 368 | + protected Object[] getTestArgs(Method m) throws IllegalArgumentException { |
| 369 | + Class<?>[] paramTypes = m.getParameterTypes(); |
| 370 | + if (paramTypes.length == 0) { |
| 371 | + return new Object[] {}; |
| 372 | + } else if (paramTypes.length == 1 && paramTypes[0] == Path.class) { |
| 373 | + return new Object[] { Path.of(m.getName())}; |
| 374 | + } else { |
| 375 | + throw new IllegalArgumentException("unknown signature for method " |
| 376 | + + m + Stream.of(paramTypes) |
| 377 | + .map(Class::toString) |
| 378 | + .collect(Collectors.joining(", ", "(", ")"))) ; |
| 379 | + } |
| 380 | + } |
| 381 | + |
| 382 | + protected void runTest(Method m, Function<Method, Object[]> f) throws Exception { |
| 383 | + try { |
| 384 | + out.println("Running test " + m.getName()); |
| 385 | + m.invoke(this, f.apply(m)); |
| 386 | + } catch (InvocationTargetException e) { |
| 387 | + Throwable cause = e.getCause(); |
| 388 | + throw (cause instanceof Exception) ? ((Exception) cause) : e; |
| 389 | + } |
| 390 | + |
| 391 | + } |
| 392 | + |
302 | 393 | /** |
303 | 394 | * Runs javadoc. |
304 | 395 | * The output directory used by this call and the final exit code |
|
0 commit comments