Mercurial > hg > Members > kono > jpf-core
comparison src/main/gov/nasa/jpf/util/test/TestJPF.java @ 0:61d41facf527
initial v8 import (history reset)
author | Peter Mehlitz <Peter.C.Mehlitz@nasa.gov> |
---|---|
date | Fri, 23 Jan 2015 10:14:01 -0800 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:61d41facf527 |
---|---|
1 /* | |
2 * Copyright (C) 2014, United States Government, as represented by the | |
3 * Administrator of the National Aeronautics and Space Administration. | |
4 * All rights reserved. | |
5 * | |
6 * The Java Pathfinder core (jpf-core) platform is licensed under the | |
7 * Apache License, Version 2.0 (the "License"); you may not use this file except | |
8 * in compliance with the License. You may obtain a copy of the License at | |
9 * | |
10 * http://www.apache.org/licenses/LICENSE-2.0. | |
11 * | |
12 * Unless required by applicable law or agreed to in writing, software | |
13 * distributed under the License is distributed on an "AS IS" BASIS, | |
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
15 * See the License for the specific language governing permissions and | |
16 * limitations under the License. | |
17 */ | |
18 package gov.nasa.jpf.util.test; | |
19 | |
20 import gov.nasa.jpf.Config; | |
21 import gov.nasa.jpf.Error; | |
22 import gov.nasa.jpf.JPF; | |
23 import gov.nasa.jpf.JPFShell; | |
24 import gov.nasa.jpf.Property; | |
25 import gov.nasa.jpf.annotation.FilterField; | |
26 import gov.nasa.jpf.tool.RunTest; | |
27 import gov.nasa.jpf.util.DevNullPrintStream; | |
28 import gov.nasa.jpf.util.TypeRef; | |
29 import gov.nasa.jpf.util.JPFSiteUtils; | |
30 import gov.nasa.jpf.util.Reflection; | |
31 import gov.nasa.jpf.vm.ExceptionInfo; | |
32 import gov.nasa.jpf.vm.NoUncaughtExceptionsProperty; | |
33 import gov.nasa.jpf.vm.NotDeadlockedProperty; | |
34 | |
35 import java.io.PrintStream; | |
36 import java.io.PrintWriter; | |
37 import java.lang.annotation.Annotation; | |
38 import java.lang.reflect.InvocationTargetException; | |
39 import java.lang.reflect.Method; | |
40 import java.lang.reflect.Modifier; | |
41 import java.util.ArrayList; | |
42 import java.util.List; | |
43 | |
44 /** | |
45 * base class for JPF unit tests. TestJPF mostly includes JPF invocations | |
46 * that check for occurrence or absence of certain execution results | |
47 * | |
48 * This class can be used in two modes: | |
49 * | |
50 * <ol> | |
51 * <li> wrapping a number of related tests for different SuTs into one class | |
52 * (suite) that calls the various JPF runners with complete argument lists | |
53 * (as in JPF.main(String[]args)) </li> | |
54 * | |
55 * <li> derive a class from TestJPF that uses the "..This" methods, which in | |
56 * turn use reflection to automatically append the test class and method to the | |
57 * JPF.main argument list (based on the calling class / method names). Note that | |
58 * you have to obey naming conventions for this to work: | |
59 * | |
60 * <li> the SuT class has to be the same as the test class without "Test", e.g. | |
61 * "CastTest" -> "Cast" </li> | |
62 * | |
63 * <li> the SuT method has to have the same name as the @Test method that | |
64 * invokes JPF, e.g. "CastTest {.. @Test void testArrayCast() ..}" -> | |
65 * "Cast {.. void testArrayCast()..} </li> | |
66 * </ol> | |
67 */ | |
68 public abstract class TestJPF implements JPFShell { | |
69 static PrintStream out = System.out; | |
70 | |
71 public static final String UNNAMED_PACKAGE = ""; | |
72 public static final String SAME_PACKAGE = null; | |
73 | |
74 //--- those are only used outside of JPF execution | |
75 @FilterField protected static boolean globalRunDirectly, globalShowConfig; | |
76 | |
77 @FilterField protected static boolean runDirectly; // don't run test methods through JPF, invoke it directly | |
78 @FilterField protected static boolean stopOnFailure; // stop as soon as we encounter a failed test or error | |
79 @FilterField protected static boolean showConfig; // for debugging purposes | |
80 @FilterField protected static boolean showConfigSources; // for debugging purposes | |
81 @FilterField protected static boolean hideSummary; | |
82 | |
83 @FilterField protected static boolean quiet; // don't show test output | |
84 | |
85 @FilterField protected String sutClassName; | |
86 | |
87 static class GlobalArg { | |
88 String key; | |
89 String val; | |
90 | |
91 GlobalArg (String k, String v){ | |
92 key = k; | |
93 val = v; | |
94 } | |
95 } | |
96 | |
97 // it seems wrong to pull globalArgs here instead of setting it from | |
98 // RunTest, but RunTest has to make sure TestJPF is loaded through the | |
99 // JPFClassLoader, i.e. cannot directly reference this class. | |
100 | |
101 @FilterField static ArrayList<GlobalArg> globalArgs; | |
102 | |
103 protected static ArrayList<GlobalArg> getGlobalArgs() { | |
104 // NOTE - this is only set if we execute tests from build.xml | |
105 Config globalConf = RunTest.getConfig(); | |
106 if (globalConf != null){ | |
107 ArrayList<GlobalArg> list = new ArrayList<GlobalArg>(); | |
108 | |
109 //--- the "test.<key>" specs | |
110 String[] testKeys = globalConf.getKeysStartingWith("test."); | |
111 if (testKeys.length > 0){ | |
112 for (String key : testKeys){ | |
113 String val = globalConf.getString(key); | |
114 // <2do> this is a hack to avoid the problem of not being able to store | |
115 // empty/nil/null values in the global config (they are removed during global config init) | |
116 if (val.equals("REMOVE")){ | |
117 val = null; | |
118 } | |
119 | |
120 key = key.substring(5); | |
121 | |
122 list.add(new GlobalArg(key,val)); | |
123 } | |
124 } | |
125 | |
126 return list; | |
127 } | |
128 | |
129 return null; | |
130 } | |
131 | |
132 static { | |
133 if (!isJPFRun()){ | |
134 globalArgs = getGlobalArgs(); | |
135 } | |
136 } | |
137 | |
138 //--- internal methods | |
139 | |
140 public static void fail (String msg, String[] args, String cause){ | |
141 StringBuilder sb = new StringBuilder(); | |
142 | |
143 sb.append(msg); | |
144 if (args != null){ | |
145 for (String s : args){ | |
146 sb.append(s); | |
147 sb.append(' '); | |
148 } | |
149 } | |
150 | |
151 if (cause != null){ | |
152 sb.append(':'); | |
153 sb.append(cause); | |
154 } | |
155 | |
156 fail(sb.toString()); | |
157 } | |
158 | |
159 public static void fail (){ | |
160 throw new AssertionError(); | |
161 } | |
162 | |
163 public static void fail (String msg){ | |
164 throw new AssertionError(msg); | |
165 } | |
166 | |
167 public void report (String[] args) { | |
168 if (!quiet){ | |
169 out.print(" running jpf with args:"); | |
170 | |
171 for (int i = 0; i < args.length; i++) { | |
172 out.print(' '); | |
173 out.print(args[i]); | |
174 } | |
175 | |
176 out.println(); | |
177 } | |
178 } | |
179 | |
180 /** | |
181 * compute the SuT class name for a given JUnit test class: remove | |
182 * optionally ending "..Test", and replace package (if specified) | |
183 * | |
184 * @param testClassName the JUnit test class | |
185 * @param sutPackage optional SuT package name (without ending '.', null | |
186 * os SAME_PACKAGE means same package, "" or UNNAMED_PACKAGE means unnamed package) | |
187 * @return main class name of system under test | |
188 */ | |
189 protected static String getSutClassName (String testClassName, String sutPackage){ | |
190 | |
191 String sutClassName = testClassName; | |
192 | |
193 int i = sutClassName.lastIndexOf('.'); | |
194 if (i >= 0){ // testclass has a package | |
195 | |
196 if (sutPackage == null){ // use same package | |
197 // nothing to do | |
198 } else if (sutPackage.length() > 0) { // explicit sut package | |
199 sutClassName = sutPackage + sutClassName.substring(i); | |
200 | |
201 } else { // unnamed sut package | |
202 sutClassName = sutClassName.substring(i+1); | |
203 } | |
204 | |
205 } else { // test class has no package | |
206 if (sutPackage == null || sutPackage.length() == 0){ // use same package | |
207 // nothing to do | |
208 } else { // explicit sut package | |
209 sutClassName = sutPackage + '.' + sutClassName; | |
210 } | |
211 } | |
212 | |
213 if (sutClassName.endsWith("JPF")) { | |
214 sutClassName = sutClassName.substring(0, sutClassName.length() - 3); | |
215 } | |
216 | |
217 return sutClassName; | |
218 } | |
219 | |
220 // we can't set the sutClassName only from main() called methods (like | |
221 // runTestsOfThisClass()) since main() doesn't get called if this is executed | |
222 // by Ant (via <junit> task) | |
223 // the default ctor is always executed | |
224 public TestJPF () { | |
225 sutClassName = getSutClassName(getClass().getName(), SAME_PACKAGE); | |
226 } | |
227 | |
228 | |
229 | |
230 //------ the API to be used by subclasses | |
231 | |
232 /** | |
233 * to be used from default ctor of derived class if the SuT is in a different | |
234 * package | |
235 * @param sutClassName the qualified SuT class name to be checked by JPF | |
236 */ | |
237 protected TestJPF (String sutClassName){ | |
238 this.sutClassName = sutClassName; | |
239 } | |
240 | |
241 public static boolean isJPFRun () { | |
242 return false; | |
243 } | |
244 | |
245 public static boolean isJUnitRun() { | |
246 // intercepted by native peer if this runs under JPF | |
247 Throwable t = new Throwable(); | |
248 t.fillInStackTrace(); | |
249 | |
250 for (StackTraceElement se : t.getStackTrace()){ | |
251 if (se.getClassName().startsWith("org.junit.")){ | |
252 return true; | |
253 } | |
254 } | |
255 | |
256 return false; | |
257 } | |
258 | |
259 public static boolean isRunTestRun() { | |
260 // intercepted by native peer if this runs under JPF | |
261 Throwable t = new Throwable(); | |
262 t.fillInStackTrace(); | |
263 | |
264 for (StackTraceElement se : t.getStackTrace()){ | |
265 if (se.getClassName().equals("gov.nasa.jpf.tool.RunTest")){ | |
266 return true; | |
267 } | |
268 } | |
269 | |
270 return false; | |
271 } | |
272 | |
273 | |
274 protected static void getOptions (String[] args){ | |
275 runDirectly = globalRunDirectly; | |
276 showConfig = globalShowConfig; | |
277 | |
278 // hideSummary and stopOnFailure only make sense as global options anyways | |
279 | |
280 if (args != null){ | |
281 for (int i=0; i<args.length; i++){ | |
282 String a = args[i]; | |
283 if (a != null){ | |
284 if (a.length() > 0){ | |
285 if (a.charAt(0) == '-'){ | |
286 a = a.substring(1); | |
287 | |
288 if (a.equals("d")){ | |
289 runDirectly = true; | |
290 } else if (a.equals("s") || a.equals("show")){ | |
291 showConfig = true; | |
292 } else if (a.equals("l") || a.equals("log")){ | |
293 showConfigSources = true; | |
294 } else if (a.equals("q") || a.equals("quiet")){ | |
295 quiet = true; | |
296 } else if (a.equals("x")){ | |
297 stopOnFailure = true; | |
298 } else if (a.equals("h")){ | |
299 hideSummary = true; | |
300 } | |
301 args[i] = null; // set it consumed | |
302 | |
303 } else { | |
304 break; // done, this is a test method | |
305 } | |
306 } | |
307 } | |
308 } | |
309 } | |
310 } | |
311 | |
312 protected static boolean hasExplicitTestMethods(String[] args){ | |
313 for (String a : args){ | |
314 if (a != null){ | |
315 return true; | |
316 } | |
317 } | |
318 | |
319 return false; | |
320 } | |
321 | |
322 protected static List<Method> getMatchingMethods(Class<? extends TestJPF> testCls, | |
323 int setModifiers, int unsetModifiers, String[] annotationNames){ | |
324 List<Method> list = new ArrayList<Method>(); | |
325 | |
326 for (Method m : testCls.getMethods()){ | |
327 if (isMatchingMethod(m, setModifiers, unsetModifiers, annotationNames)){ | |
328 list.add(m); | |
329 } | |
330 } | |
331 | |
332 return list; | |
333 } | |
334 | |
335 protected static boolean isMatchingMethod(Method m, int setModifiers, int unsetModifiers, String[] annotationNames) { | |
336 int mod = m.getModifiers(); | |
337 if (((mod & setModifiers) != 0) && ((mod & unsetModifiers) == 0)) { | |
338 if (m.getParameterTypes().length == 0) { | |
339 if (annotationNames != null){ | |
340 Annotation[] annotations = m.getAnnotations(); | |
341 for (int i = 0; i < annotations.length; i++) { | |
342 String annotType = annotations[i].annotationType().getName(); | |
343 for (int j = 0; j < annotationNames.length; j++) { | |
344 if (annotType.equals(annotationNames[j])) { | |
345 return true; | |
346 } | |
347 } | |
348 } | |
349 } else { | |
350 return true; | |
351 } | |
352 } | |
353 } | |
354 | |
355 return false; | |
356 } | |
357 | |
358 protected static List<Method> getContextMethods(Class<? extends TestJPF> testCls, | |
359 int setModifiers, int unsetModifiers, String annotation){ | |
360 String[] annotations = {annotation}; | |
361 | |
362 List<Method> list = new ArrayList<Method>(); | |
363 for (Method m : testCls.getMethods()){ | |
364 if (isMatchingMethod(m, setModifiers, unsetModifiers, annotations)){ | |
365 list.add(m); | |
366 } | |
367 } | |
368 return list; | |
369 } | |
370 | |
371 protected static List<Method> getBeforeMethods(Class<? extends TestJPF> testCls){ | |
372 return getContextMethods(testCls, Modifier.PUBLIC, Modifier.STATIC, "org.junit.Before"); | |
373 } | |
374 | |
375 protected static List<Method> getAfterMethods(Class<? extends TestJPF> testCls){ | |
376 return getContextMethods(testCls, Modifier.PUBLIC, Modifier.STATIC, "org.junit.After"); | |
377 } | |
378 | |
379 protected static List<Method> getBeforeClassMethods(Class<? extends TestJPF> testCls){ | |
380 return getContextMethods(testCls, Modifier.PUBLIC | Modifier.STATIC, 0, "org.junit.BeforeClass"); | |
381 } | |
382 | |
383 protected static List<Method> getAfterClassMethods(Class<? extends TestJPF> testCls){ | |
384 return getContextMethods(testCls, Modifier.PUBLIC | Modifier.STATIC, 0, "org.junit.AfterClass"); | |
385 } | |
386 | |
387 protected static boolean haveTestMethodSpecs( String[] args){ | |
388 if (args != null && args.length > 0){ | |
389 for (int i=0; i<args.length; i++){ | |
390 if (args[i] != null){ | |
391 return true; | |
392 } | |
393 } | |
394 } | |
395 | |
396 return false; | |
397 } | |
398 | |
399 protected static List<Method> getTestMethods(Class<? extends TestJPF> testCls, String[] args){ | |
400 String[] testAnnotations = {"org.junit.Test", "org.testng.annotations.Test"}; | |
401 | |
402 if (haveTestMethodSpecs( args)){ // test methods specified as arguments | |
403 List<Method> list = new ArrayList<Method>(); | |
404 | |
405 for (String test : args){ | |
406 if (test != null){ | |
407 | |
408 try { | |
409 Method m = testCls.getMethod(test); | |
410 | |
411 if (isMatchingMethod(m, Modifier.PUBLIC, Modifier.STATIC, null /*testAnnotations*/ )){ | |
412 list.add(m); | |
413 } else { | |
414 throw new RuntimeException("test method must be @Test annotated public instance method without arguments: " + test); | |
415 } | |
416 | |
417 } catch (NoSuchMethodException x) { | |
418 throw new RuntimeException("method: " + test | |
419 + "() not in test class: " + testCls.getName(), x); | |
420 } | |
421 } | |
422 } | |
423 | |
424 return list; | |
425 | |
426 } else { // no explicit test method specification, get all matches | |
427 return getMatchingMethods(testCls, Modifier.PUBLIC, Modifier.STATIC, testAnnotations); | |
428 } | |
429 } | |
430 | |
431 | |
432 protected static void reportTestStart(String mthName){ | |
433 if (!quiet){ | |
434 System.out.println(); | |
435 System.out.print("......................................... testing "); | |
436 System.out.print(mthName); | |
437 System.out.println("()"); | |
438 } | |
439 } | |
440 | |
441 protected static void reportTestInitialization(String mthName){ | |
442 if (!quiet){ | |
443 System.out.print(".... running test initialization: "); | |
444 System.out.print(mthName); | |
445 System.out.println("()"); | |
446 } | |
447 } | |
448 | |
449 protected static void reportTestCleanup(String mthName){ | |
450 if (!quiet){ | |
451 System.out.print(".... running test cleanup: "); | |
452 System.out.print(mthName); | |
453 System.out.println("()"); | |
454 } | |
455 } | |
456 | |
457 protected static void reportTestFinished(String msg){ | |
458 if (!quiet){ | |
459 System.out.print("......................................... "); | |
460 System.out.println(msg); | |
461 } | |
462 } | |
463 | |
464 protected static void reportResults(String clsName, int nTests, int nFailures, int nErrors, List<String> results){ | |
465 System.out.println(); | |
466 System.out.print("......................................... execution of testsuite: " + clsName); | |
467 if (nFailures > 0 || nErrors > 0){ | |
468 System.out.println(" FAILED"); | |
469 } else if (nTests > 0) { | |
470 System.out.println(" SUCCEEDED"); | |
471 } else { | |
472 System.out.println(" OBSOLETE"); | |
473 } | |
474 | |
475 if (!quiet){ | |
476 if (results != null) { | |
477 int i = 0; | |
478 for (String result : results) { | |
479 System.out.print(".... [" + ++i + "] "); | |
480 System.out.println(result); | |
481 } | |
482 } | |
483 } | |
484 | |
485 System.out.print("........................................."); | |
486 System.out.println(" tests: " + nTests + ", failures: " + nFailures + ", errors: " + nErrors); | |
487 } | |
488 | |
489 | |
490 static void invoke (Method m, Object testObject) throws IllegalAccessException, InvocationTargetException { | |
491 PrintStream sysOut = null; | |
492 | |
493 try { | |
494 if (quiet){ | |
495 sysOut = System.out; | |
496 System.setOut( new DevNullPrintStream()); | |
497 } | |
498 | |
499 m.invoke( testObject); | |
500 | |
501 } finally { | |
502 if (quiet){ | |
503 System.setOut( sysOut); | |
504 } | |
505 } | |
506 } | |
507 | |
508 /** | |
509 * this is the main test loop if this TestJPF instance is executed directly | |
510 * or called from RunTest. It is *not* called if this is executed from JUnit | |
511 */ | |
512 public static void runTests (Class<? extends TestJPF> testCls, String... args){ | |
513 int nTests = 0; | |
514 int nFailures = 0; | |
515 int nErrors = 0; | |
516 String testMethodName = null; | |
517 List<String> results = null; | |
518 | |
519 getOptions(args); | |
520 globalRunDirectly = runDirectly; | |
521 globalShowConfig = showConfig; | |
522 boolean globalStopOnFailure = stopOnFailure; | |
523 | |
524 try { | |
525 List<Method> testMethods = getTestMethods(testCls, args); | |
526 results = new ArrayList<String>(testMethods.size()); | |
527 | |
528 // check if we have JUnit style housekeeping methods (initialization and | |
529 // cleanup should use the same mechanisms as JUnit) | |
530 | |
531 List<Method> beforeClassMethods = getBeforeClassMethods(testCls); | |
532 List<Method> afterClassMethods = getAfterClassMethods(testCls); | |
533 | |
534 List<Method> beforeMethods = getBeforeMethods(testCls); | |
535 List<Method> afterMethods = getAfterMethods(testCls); | |
536 | |
537 for (Method initMethod : beforeClassMethods) { | |
538 reportTestInitialization(initMethod.getName()); | |
539 initMethod.invoke(null); | |
540 } | |
541 | |
542 for (Method testMethod : testMethods) { | |
543 testMethodName = testMethod.getName(); | |
544 String result = testMethodName; | |
545 try { | |
546 Object testObject = testCls.newInstance(); | |
547 | |
548 nTests++; | |
549 reportTestStart( testMethodName); | |
550 | |
551 // run per test initialization methods | |
552 for (Method initMethod : beforeMethods){ | |
553 reportTestInitialization( initMethod.getName()); | |
554 invoke( initMethod, testObject); | |
555 } | |
556 | |
557 // now run the test method itself | |
558 invoke( testMethod, testObject); | |
559 result += ": Ok"; | |
560 | |
561 // run per test initialization methods | |
562 for (Method cleanupMethod : afterMethods){ | |
563 reportTestCleanup( cleanupMethod.getName()); | |
564 invoke( cleanupMethod, testObject); | |
565 } | |
566 | |
567 } catch (InvocationTargetException x) { | |
568 Throwable cause = x.getCause(); | |
569 cause.printStackTrace(); | |
570 if (cause instanceof AssertionError) { | |
571 nFailures++; | |
572 reportTestFinished("test method failed with: " + cause.getMessage()); | |
573 result += ": Failed"; | |
574 } else { | |
575 nErrors++; | |
576 reportTestFinished("unexpected error while executing test method: " + cause.getMessage()); | |
577 result += ": Error"; | |
578 } | |
579 | |
580 if (globalStopOnFailure){ | |
581 break; | |
582 } | |
583 } | |
584 | |
585 results.add(result); | |
586 reportTestFinished(result); | |
587 } | |
588 | |
589 for (Method cleanupMethod : afterClassMethods) { | |
590 reportTestCleanup( cleanupMethod.getName()); | |
591 cleanupMethod.invoke(null); | |
592 } | |
593 | |
594 | |
595 //--- those exceptions are unexpected and represent unrecoverable test harness errors | |
596 } catch (InvocationTargetException x) { | |
597 Throwable cause = x.getCause(); | |
598 cause.printStackTrace(); | |
599 nErrors++; | |
600 reportTestFinished("TEST ERROR: @BeforeClass,@AfterClass method failed: " + x.getMessage()); | |
601 | |
602 } catch (InstantiationException x) { | |
603 nErrors++; | |
604 reportTestFinished("TEST ERROR: cannot instantiate test class: " + x.getMessage()); | |
605 } catch (IllegalAccessException x) { // can't happen if getTestMethods() worked | |
606 nErrors++; | |
607 reportTestFinished("TEST ERROR: default constructor or test method not public: " + testMethodName); | |
608 } catch (IllegalArgumentException x) { // can't happen if getTestMethods() worked | |
609 nErrors++; | |
610 reportTestFinished("TEST ERROR: illegal argument for test method: " + testMethodName); | |
611 } catch (RuntimeException rx) { | |
612 nErrors++; | |
613 reportTestFinished("TEST ERROR: " + rx.toString()); | |
614 } | |
615 | |
616 if (!hideSummary){ | |
617 reportResults(testCls.getName(), nTests, nFailures, nErrors, results); | |
618 } | |
619 | |
620 if (nErrors > 0 || nFailures > 0){ | |
621 if (isRunTestRun()){ | |
622 // we need to reportTestFinished this test has failed | |
623 throw new RunTest.Failed(); | |
624 } | |
625 } | |
626 } | |
627 | |
628 static String getProperty(String key){ | |
629 // intercepted by peer | |
630 return null; | |
631 } | |
632 | |
633 /** | |
634 * this is the JPF entry method in case there is no main() in the test class | |
635 * | |
636 * <2do> we should support test method arguments here | |
637 */ | |
638 static void runTestMethod(String args[]) throws Throwable { | |
639 String testClsName = getProperty("target"); | |
640 String testMthName = getProperty("target.test_method"); | |
641 | |
642 Class<?> testCls = Class.forName(testClsName); | |
643 Object target = testCls.newInstance(); | |
644 | |
645 Method method = testCls.getMethod(testMthName); | |
646 | |
647 try { | |
648 method.invoke(target); | |
649 } catch (InvocationTargetException e) { | |
650 throw e.getCause(); | |
651 } | |
652 } | |
653 | |
654 /** | |
655 * NOTE: this needs to be called from the concrete test class, typically from | |
656 * its main() method, otherwise we don't know the name of the class we have | |
657 * to pass to JPF | |
658 */ | |
659 protected static void runTestsOfThisClass (String[] testMethods){ | |
660 // needs to be at the same stack level, so we can't delegate | |
661 Class<? extends TestJPF> testClass = Reflection.getCallerClass(TestJPF.class); | |
662 runTests(testClass, testMethods); | |
663 } | |
664 | |
665 /** | |
666 * needs to be broken up into two methods for cases that do additional | |
667 * JPF initialization (jpf-inspector) | |
668 * | |
669 * this is called from the various verifyX() methods (i.e. host VM) to | |
670 * start JPF, it is never executed under JPF | |
671 */ | |
672 protected JPF createAndRunJPF (StackTraceElement testMethod, String[] args) { | |
673 JPF jpf = createJPF( testMethod, args); | |
674 if (jpf != null){ | |
675 jpf.run(); | |
676 } | |
677 return jpf; | |
678 } | |
679 | |
680 /** | |
681 * this is never executed under JPF | |
682 */ | |
683 protected JPF createJPF (StackTraceElement testMethod, String[] args) { | |
684 JPF jpf = null; | |
685 | |
686 Config conf = new Config(args); | |
687 | |
688 // --- add global args (if we run under RunTest) | |
689 if (globalArgs != null) { | |
690 for (GlobalArg ga : globalArgs) { | |
691 String key = ga.key; | |
692 String val = ga.val; | |
693 if (val != null){ | |
694 conf.put(key, val); | |
695 } else { | |
696 conf.remove(key); | |
697 } | |
698 } | |
699 } | |
700 | |
701 setTestTargetKeys(conf, testMethod); | |
702 | |
703 // --- initialize the classpath from <projectId>.test_classpath | |
704 String projectId = JPFSiteUtils.getCurrentProjectId(); | |
705 if (projectId != null) { | |
706 String testCp = conf.getString(projectId + ".test_classpath"); | |
707 if (testCp != null) { | |
708 conf.append("classpath", testCp, ","); | |
709 } | |
710 } | |
711 | |
712 // --- if we have any specific test property overrides, do so | |
713 conf.promotePropertyCategory("test."); | |
714 | |
715 getOptions(args); | |
716 | |
717 if (showConfig || showConfigSources) { | |
718 PrintWriter pw = new PrintWriter(System.out, true); | |
719 if (showConfigSources) { | |
720 conf.printSources(pw); | |
721 } | |
722 | |
723 if (showConfig) { | |
724 conf.print(pw); | |
725 } | |
726 pw.flush(); | |
727 } | |
728 | |
729 jpf = new JPF(conf); | |
730 | |
731 return jpf; | |
732 } | |
733 | |
734 protected void setTestTargetKeys(Config conf, StackTraceElement testMethod) { | |
735 conf.put("target.entry", "runTestMethod([Ljava/lang/String;)V"); | |
736 conf.put("target", testMethod.getClassName()); | |
737 conf.put("target.test_method", testMethod.getMethodName()); | |
738 } | |
739 | |
740 //--- the JPFShell interface | |
741 @Override | |
742 public void start(String[] testMethods){ | |
743 Class<? extends TestJPF> testClass = getClass(); // this is an instance method | |
744 runTests(testClass, testMethods); | |
745 } | |
746 | |
747 protected StackTraceElement getCaller(){ | |
748 StackTraceElement[] st = (new Throwable()).getStackTrace(); | |
749 return st[2]; | |
750 } | |
751 | |
752 protected StackTraceElement setTestMethod (String clsName, String mthName){ | |
753 return new StackTraceElement( clsName, mthName, null, -1); | |
754 } | |
755 | |
756 protected StackTraceElement setTestMethod (String mthName){ | |
757 return new StackTraceElement( getClass().getName(), mthName, null, -1); | |
758 } | |
759 | |
760 | |
761 //--- the JPF run test methods | |
762 | |
763 /** | |
764 * run JPF expecting a AssertionError in the SuT | |
765 * @param args JPF main() arguments | |
766 */ | |
767 protected JPF assertionError (StackTraceElement testMethod, String... args){ | |
768 return unhandledException( testMethod, "java.lang.AssertionError", null, args ); | |
769 } | |
770 protected JPF assertionError (String... args) { | |
771 return unhandledException( getCaller(), "java.lang.AssertionError", null, args ); | |
772 } | |
773 | |
774 protected JPF assertionErrorDetails (StackTraceElement testMethod, String details, String... args) { | |
775 return unhandledException( testMethod, "java.lang.AssertionError", details, args ); | |
776 } | |
777 protected JPF assertionErrorDetails (String details, String... args) { | |
778 return unhandledException( getCaller(), "java.lang.AssertionError", details, args ); | |
779 } | |
780 protected boolean verifyAssertionErrorDetails (String details, String... args){ | |
781 if (runDirectly) { | |
782 return true; | |
783 } else { | |
784 unhandledException( getCaller(), "java.lang.AssertionError", details, args); | |
785 return false; | |
786 } | |
787 } | |
788 protected boolean verifyAssertionError (String... args){ | |
789 if (runDirectly) { | |
790 return true; | |
791 } else { | |
792 unhandledException( getCaller(), "java.lang.AssertionError", null, args); | |
793 return false; | |
794 } | |
795 } | |
796 | |
797 /** | |
798 * run JPF expecting no SuT property violations | |
799 */ | |
800 protected JPF noPropertyViolation (StackTraceElement testMethod, String... args) { | |
801 JPF jpf = null; | |
802 | |
803 report(args); | |
804 | |
805 try { | |
806 jpf = createAndRunJPF( testMethod, args); | |
807 } catch (Throwable t) { | |
808 // we get as much as one little hickup and we declare it failed | |
809 t.printStackTrace(); | |
810 fail("JPF internal exception executing: ", args, t.toString()); | |
811 return jpf; | |
812 } | |
813 | |
814 List<Error> errors = jpf.getSearchErrors(); | |
815 if ((errors != null) && (errors.size() > 0)) { | |
816 fail("JPF found unexpected errors: " + (errors.get(0)).getDescription()); | |
817 } | |
818 | |
819 return jpf; | |
820 } | |
821 | |
822 protected JPF noPropertyViolation (String... args) { | |
823 return noPropertyViolation( getCaller(), args); | |
824 } | |
825 | |
826 protected boolean verifyNoPropertyViolation (String...args){ | |
827 if (runDirectly) { | |
828 return true; | |
829 } else { | |
830 noPropertyViolation( getCaller(), args); | |
831 return false; | |
832 } | |
833 } | |
834 | |
835 /** | |
836 * NOTE: this uses the exception class name because it might be an | |
837 * exception type that is only known to JPF (i.e. not in the native classpath) | |
838 * | |
839 * @param xClassName name of the exception base type that is expected | |
840 * @param details detail message of the expected exception | |
841 * @param args JPF arguments | |
842 */ | |
843 protected JPF unhandledException (StackTraceElement testMethod, String xClassName, String details, String... args) { | |
844 JPF jpf = null; | |
845 | |
846 report(args); | |
847 | |
848 try { | |
849 jpf = createAndRunJPF(testMethod, args); | |
850 } catch (Throwable t) { | |
851 t.printStackTrace(); | |
852 fail("JPF internal exception executing: ", args, t.toString()); | |
853 return jpf; | |
854 } | |
855 | |
856 Error error = jpf.getLastError(); | |
857 if (error != null){ | |
858 Property errorProperty = error.getProperty(); | |
859 if (errorProperty instanceof NoUncaughtExceptionsProperty){ | |
860 ExceptionInfo xi = ((NoUncaughtExceptionsProperty)errorProperty).getUncaughtExceptionInfo(); | |
861 String xn = xi.getExceptionClassname(); | |
862 if (!xn.equals(xClassName)) { | |
863 fail("JPF caught wrong exception: " + xn + ", expected: " + xClassName); | |
864 } | |
865 | |
866 if (details != null) { | |
867 String gotDetails = xi.getDetails(); | |
868 if (gotDetails == null) { | |
869 fail("JPF caught the right exception but no details, expected: " + details); | |
870 } else { | |
871 if (!gotDetails.endsWith(details)) { | |
872 fail("JPF caught the right exception but the details were wrong: " + gotDetails + ", expected: " + details); | |
873 } | |
874 } | |
875 } | |
876 } else { // error not a NoUncaughtExceptionsProperty | |
877 fail("JPF failed to catch exception executing: ", args, ("expected " + xClassName)); | |
878 } | |
879 } else { // no error | |
880 fail("JPF failed to catch exception executing: ", args, ("expected " + xClassName)); | |
881 } | |
882 | |
883 return jpf; | |
884 } | |
885 | |
886 protected JPF unhandledException (String xClassName, String details, String... args) { | |
887 return unhandledException( getCaller(), xClassName, details, args); | |
888 } | |
889 | |
890 | |
891 protected boolean verifyUnhandledExceptionDetails (String xClassName, String details, String... args){ | |
892 if (runDirectly) { | |
893 return true; | |
894 } else { | |
895 unhandledException( getCaller(), xClassName, details, args); | |
896 return false; | |
897 } | |
898 } | |
899 protected boolean verifyUnhandledException (String xClassName, String... args){ | |
900 if (runDirectly) { | |
901 return true; | |
902 } else { | |
903 unhandledException( getCaller(), xClassName, null, args); | |
904 return false; | |
905 } | |
906 } | |
907 | |
908 | |
909 /** | |
910 * run JPF expecting it to throw an exception | |
911 * NOTE - xClassName needs to be the concrete exception, not a super class | |
912 * @param args JPF main() arguments | |
913 */ | |
914 protected JPF jpfException (StackTraceElement testMethod, Class<? extends Throwable> xCls, String... args) { | |
915 JPF jpf = null; | |
916 Throwable exception = null; | |
917 | |
918 report(args); | |
919 | |
920 try { | |
921 jpf = createAndRunJPF( testMethod, args); | |
922 } catch (JPF.ExitException xx) { | |
923 exception = xx.getCause(); | |
924 } catch (Throwable x) { | |
925 exception = x; | |
926 } | |
927 | |
928 if (exception != null){ | |
929 if (!xCls.isAssignableFrom(exception.getClass())){ | |
930 fail("JPF produced wrong exception: " + exception + ", expected: " + xCls.getName()); | |
931 } | |
932 } else { | |
933 fail("JPF failed to produce exception, expected: " + xCls.getName()); | |
934 } | |
935 | |
936 return jpf; | |
937 } | |
938 | |
939 protected JPF jpfException (Class<? extends Throwable> xCls, String... args) { | |
940 return jpfException( getCaller(), xCls, args); | |
941 } | |
942 | |
943 protected boolean verifyJPFException (TypeRef xClsSpec, String... args){ | |
944 if (runDirectly) { | |
945 return true; | |
946 | |
947 } else { | |
948 try { | |
949 Class<? extends Throwable> xCls = xClsSpec.asNativeSubclass(Throwable.class); | |
950 | |
951 jpfException( getCaller(), xCls, args); | |
952 | |
953 } catch (ClassCastException ccx){ | |
954 fail("not a property type: " + xClsSpec); | |
955 } catch (ClassNotFoundException cnfx){ | |
956 fail("property class not found: " + xClsSpec); | |
957 } | |
958 return false; | |
959 } | |
960 } | |
961 | |
962 | |
963 | |
964 /** | |
965 * run JPF expecting a property violation of the SuT | |
966 * @param args JPF main() arguments | |
967 */ | |
968 protected JPF propertyViolation (StackTraceElement testMethod, Class<? extends Property> propertyCls, String... args ){ | |
969 JPF jpf = null; | |
970 | |
971 report(args); | |
972 | |
973 try { | |
974 jpf = createAndRunJPF( testMethod, args); | |
975 } catch (Throwable t) { | |
976 t.printStackTrace(); | |
977 fail("JPF internal exception executing: ", args, t.toString()); | |
978 } | |
979 | |
980 List<Error> errors = jpf.getSearchErrors(); | |
981 if (errors != null) { | |
982 for (Error e : errors) { | |
983 if (propertyCls == e.getProperty().getClass()) { | |
984 return jpf; // success, we got the sucker | |
985 } | |
986 } | |
987 } | |
988 | |
989 fail("JPF failed to detect error: " + propertyCls.getName()); | |
990 return jpf; | |
991 } | |
992 | |
993 protected JPF propertyViolation (Class<? extends Property> propertyCls, String... args ){ | |
994 return propertyViolation( getCaller(), propertyCls, args); | |
995 } | |
996 | |
997 protected boolean verifyPropertyViolation (TypeRef propertyClsSpec, String... args){ | |
998 if (runDirectly) { | |
999 return true; | |
1000 | |
1001 } else { | |
1002 try { | |
1003 Class<? extends Property> propertyCls = propertyClsSpec.asNativeSubclass(Property.class); | |
1004 propertyViolation( getCaller(), propertyCls, args); | |
1005 | |
1006 } catch (ClassCastException ccx){ | |
1007 fail("not a property type: " + propertyClsSpec); | |
1008 } catch (ClassNotFoundException cnfx){ | |
1009 fail("property class not found: " + propertyClsSpec); | |
1010 } | |
1011 return false; | |
1012 } | |
1013 } | |
1014 | |
1015 | |
1016 /** | |
1017 * run JPF expecting a deadlock in the SuT | |
1018 * @param args JPF main() arguments | |
1019 */ | |
1020 protected JPF deadlock (String... args) { | |
1021 return propertyViolation( getCaller(), NotDeadlockedProperty.class, args ); | |
1022 } | |
1023 | |
1024 protected boolean verifyDeadlock (String... args){ | |
1025 if (runDirectly) { | |
1026 return true; | |
1027 } else { | |
1028 propertyViolation( getCaller(), NotDeadlockedProperty.class, args); | |
1029 return false; | |
1030 } | |
1031 } | |
1032 | |
1033 // these are the org.junit.Assert APIs, but we don't want org.junit to be | |
1034 // required to run tests | |
1035 | |
1036 public static void assertEquals(String msg, Object expected, Object actual){ | |
1037 if (expected == null && actual == null) { | |
1038 return; | |
1039 } | |
1040 | |
1041 if (expected != null && expected.equals(actual)) { | |
1042 return; | |
1043 } | |
1044 | |
1045 fail(msg); | |
1046 } | |
1047 | |
1048 public static void assertEquals(Object expected, Object actual){ | |
1049 assertEquals("", expected, actual); | |
1050 } | |
1051 | |
1052 public static void assertEquals(String msg, int expected, int actual){ | |
1053 if (expected != actual) { | |
1054 fail(msg); | |
1055 } | |
1056 } | |
1057 | |
1058 public static void assertEquals(int expected, int actual){ | |
1059 assertEquals("expected != actual : " + expected + " != " + actual, expected, actual); | |
1060 } | |
1061 | |
1062 public static void assertEquals(String msg, long expected, long actual){ | |
1063 if (expected != actual) { | |
1064 fail(msg); | |
1065 } | |
1066 } | |
1067 | |
1068 public static void assertEquals(long expected, long actual){ | |
1069 assertEquals("expected != actual : " + expected + " != " + actual, | |
1070 expected, actual); | |
1071 } | |
1072 | |
1073 public static void assertEquals(double expected, double actual){ | |
1074 if (expected != actual){ | |
1075 fail("expected != actual : " + expected + " != " + actual); | |
1076 } | |
1077 } | |
1078 | |
1079 public static void assertEquals(String msg, double expected, double actual){ | |
1080 if (expected != actual){ | |
1081 fail(msg); | |
1082 } | |
1083 } | |
1084 | |
1085 public static void assertEquals(float expected, float actual){ | |
1086 if (expected != actual){ | |
1087 fail("expected != actual : " + expected + " != " + actual); | |
1088 } | |
1089 } | |
1090 | |
1091 public static void assertEquals(String msg, float expected, float actual){ | |
1092 if (expected != actual){ | |
1093 fail(msg); | |
1094 } | |
1095 } | |
1096 | |
1097 public static void assertEquals(String msg, double expected, double actual, double delta){ | |
1098 if (Math.abs(expected - actual) > delta) { | |
1099 fail(msg); | |
1100 } | |
1101 } | |
1102 | |
1103 public static void assertEquals(double expected, double actual, double delta){ | |
1104 assertEquals("Math.abs(expected - actual) > delta : " + "Math.abs(" + expected + " - " + actual + ") > " + delta, | |
1105 expected, actual, delta); | |
1106 } | |
1107 | |
1108 public static void assertEquals(String msg, float expected, float actual, float delta){ | |
1109 if (Math.abs(expected - actual) > delta) { | |
1110 fail(msg); | |
1111 } | |
1112 } | |
1113 | |
1114 public static void assertEquals(float expected, float actual, float delta){ | |
1115 assertEquals("Math.abs(expected - actual) > delta : " + "Math.abs(" + expected + " - " + actual + ") > " + delta, | |
1116 expected, actual, delta); | |
1117 } | |
1118 | |
1119 public static void assertArrayEquals(byte[] expected, byte[] actual){ | |
1120 if (((expected == null) != (actual == null)) || | |
1121 (expected.length != actual.length)){ | |
1122 fail("array sizes different"); | |
1123 } | |
1124 | |
1125 for (int i=0; i<expected.length; i++){ | |
1126 if (expected[i] != actual[i]){ | |
1127 fail("array element" + i + " different, expected " + expected[i] + ", actual " + actual[i]); | |
1128 } | |
1129 } | |
1130 } | |
1131 | |
1132 public static void assertNotNull(String msg, Object o) { | |
1133 if (o == null) { | |
1134 fail(msg); | |
1135 } | |
1136 } | |
1137 | |
1138 public static void assertNotNull(Object o){ | |
1139 assertNotNull("o == null", o); | |
1140 } | |
1141 | |
1142 public static void assertNull(String msg, Object o){ | |
1143 if (o != null) { | |
1144 fail(msg); | |
1145 } | |
1146 } | |
1147 | |
1148 public static void assertNull(Object o){ | |
1149 assertNull("o != null", o); | |
1150 } | |
1151 | |
1152 public static void assertSame(String msg, Object expected, Object actual){ | |
1153 if (expected != actual) { | |
1154 fail(msg); | |
1155 } | |
1156 } | |
1157 | |
1158 public static void assertSame(Object expected, Object actual){ | |
1159 assertSame("expected != actual : " + expected + " != " + actual, expected, actual); | |
1160 } | |
1161 | |
1162 public static void assertFalse (String msg, boolean cond){ | |
1163 if (cond) { | |
1164 fail(msg); | |
1165 } | |
1166 } | |
1167 | |
1168 public static void assertFalse (boolean cond){ | |
1169 assertFalse("", cond); | |
1170 } | |
1171 | |
1172 public static void assertTrue (String msg, boolean cond){ | |
1173 if (!cond) { | |
1174 fail(msg); | |
1175 } | |
1176 } | |
1177 | |
1178 public static void assertTrue (boolean cond){ | |
1179 assertTrue("", cond); | |
1180 } | |
1181 } |