BASIS  version 1.2.3 (revision 2104)
JTap.java
Go to the documentation of this file.
00001 /**
00002  * @file  JTap.java
00003  * @brief Unit testing framework for Java based on the Test Anything Protocol.
00004  *
00005  * @author Patrick LeBoutillier <patl at cpan.org>
00006  *
00007  * @note This file is a copy of the JTap.java file which is part of the
00008  *       JTap project (http://svn.solucorp.qc.ca/repos/solucorp/JTap/trunk/).
00009  *       The original implementation has been modified by Andreas Schuh as
00010  *       part of the BASIS project at SBIA.
00011  *
00012  * Copyright (c) Patrick LeBoutillier.<br />
00013  * Copyright (c) 2011, University of Pennsylvania.<br />
00014  * All rights reserved.
00015  *
00016  * This program is free software; you can redistribute it and/or modify
00017  * it under the terms of the GNU General Public License as published by
00018  * the Free Software Foundation; either version 2 of the License, or
00019  * any later version.
00020  *
00021  * This program is distributed in the hope that it will be useful,
00022  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00023  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00024  * GNU General Public License for more details.
00025  *
00026  * @sa http://testanything.org/wiki/index.php/Tap-functions
00027  *
00028  * Contact: SBIA Group <sbia-software at uphs.upenn.edu>
00029  *
00030  * @ingroup BasisJavaUtilities
00031  */
00032 
00033 import java.io.* ;
00034 
00035 
00036 public class JTap {
00037     final private String version = "1.0" ;
00038     
00039     private boolean plan_set = false ;
00040     private boolean no_plan = false ;
00041     private boolean skip_all = false ;
00042     private boolean test_died = false ;
00043     private int expected_tests = 0 ;
00044     private int executed_tests = 0 ;
00045     private int failed_tests = 0 ;
00046     private boolean exit = true ;
00047     private String todo = null ;
00048     
00049     private PrintStream out = null ;
00050     private PrintStream err = null ;
00051 
00052 
00053     public JTap(){
00054         this(true) ;
00055     }
00056 
00057 
00058     public JTap(boolean really_exit){
00059         out = System.out ;
00060         err = System.err ;
00061         exit = really_exit ;
00062     }
00063 
00064 
00065 
00066 
00067     synchronized public int plan_no_plan(){
00068         if (plan_set){
00069             die("You tried to plan twice!") ;
00070         }
00071 
00072         plan_set = true ;
00073         no_plan = true ;
00074 
00075         return 1 ;
00076     }
00077 
00078 
00079     synchronized public int plan_skip_all(String reason){
00080         if (plan_set){
00081             die("You tried to plan twice!") ;
00082         }
00083 
00084         print_plan(0, "Skip " + reason) ;
00085 
00086         skip_all = true ;
00087         plan_set = true ;
00088 
00089         exit(0) ;
00090 
00091         return 0 ;
00092     }
00093 
00094 
00095     synchronized public int plan_tests(int tests){
00096         if (plan_set){
00097             die("You tried to plan twice!") ;
00098         }
00099 
00100         if (tests == 0){
00101             die("You said to run 0 tests!  You've got to run something.") ;
00102         }
00103 
00104         print_plan(tests) ;
00105         expected_tests = tests ;
00106 
00107         plan_set = true ;
00108 
00109         return tests ;
00110     }
00111 
00112 
00113     private void print_plan(int expected){
00114         print_plan(expected, null) ;
00115     }
00116 
00117 
00118     synchronized private void print_plan(int expected_tests, String directive){
00119         out.print("1.." + expected_tests) ;
00120         if (directive != null){
00121             out.print(" # " + directive) ;
00122         }
00123         out.print("\n") ;
00124         out.flush() ;
00125     }
00126 
00127 
00128 
00129 
00130     public boolean pass(String name){
00131         return ok(true, name) ;
00132     }
00133 
00134 
00135     public boolean fail(String name){
00136         return ok(false, name) ;
00137     }
00138 
00139 
00140     public boolean ok(boolean result){
00141         return ok(result, null) ;
00142     }
00143 
00144 
00145     /*
00146         This is the workhorse method that actually
00147         prints the tests result.
00148     */
00149     synchronized public boolean ok(boolean result, String name){
00150         if (! plan_set){
00151             die("You tried to run a test without a plan!  Gotta have a plan.") ;
00152         }
00153 
00154         executed_tests++ ;
00155 
00156         if (name != null) {
00157             if (name.matches("[\\d\\s]+")){
00158                 diag("    You named your test '" + name 
00159                     + "'.  You shouldn't use numbers for your test names.") ;
00160                 diag("    Very confusing.") ;
00161             }
00162         }
00163 
00164         if (! result){
00165             out.print("not ") ;
00166             failed_tests++ ;
00167         }
00168         out.print("ok " + executed_tests) ;
00169 
00170         if (name != null) {
00171             out.print(" - ") ;
00172             out.print(name.replaceAll("#", "\\\\#")) ;
00173         }
00174 
00175         if (todo != null){
00176             out.print(" # TODO " + todo) ;
00177             if (! result){
00178                 failed_tests-- ;
00179             }
00180         }
00181 
00182         out.print("\n") ;
00183         out.flush() ;
00184         if (! result){
00185             Throwable t = new Throwable() ; 
00186             StackTraceElement stack[] = t.getStackTrace() ;
00187             String file = null ;
00188             String clas = null ;
00189             String func = null ;
00190             int line = 0 ;
00191 
00192             try {
00193                 for (int i = 0 ; i < stack.length ; i++){
00194                     Class c = Class.forName(stack[i].getClassName()) ;
00195                     if (! JTap.class.isAssignableFrom(c)){
00196                         // We are outside a JTap object, so this is probably the callpoint
00197                         file = stack[i].getFileName() ;
00198                         clas = c.getName() ;
00199                         func = stack[i].getMethodName() ;
00200                         line = stack[i].getLineNumber() ;
00201                         break ;
00202                     }
00203                 }
00204             }
00205             catch (Exception e){
00206                 e.printStackTrace() ;
00207             }
00208     
00209             if (name != null){      
00210                 diag("  Failed " + (todo == null ? "" : "(TODO) ") + "test '" + name + "'") ;
00211                 diag("  in " + file + ":" + func + "() at line " + line + ".") ;
00212             }
00213             else {
00214                 diag("  Failed " + (todo == null ? "" : "(TODO) ") + "test in " + file + ":" + func + "() at line " + line + ".") ;
00215             }
00216         }
00217 
00218         return result ;
00219     }
00220 
00221 
00222     private boolean equals(Object result, Object expected){
00223         boolean r ;
00224 
00225         if ((result == null)&&(expected == null)){
00226             r = true ;
00227         }
00228         else if ((result == null)||(expected == null)){
00229             r = false ;
00230         }
00231         else {
00232             r = result.equals(expected) ;
00233         }
00234 
00235         return r ;
00236     }
00237 
00238 
00239     private boolean matches(Object result, String pattern){
00240         boolean r ;
00241 
00242         if ((result == null)||(pattern == null)){
00243             r = false ;
00244         }
00245         else {
00246             r = result.toString().matches(pattern) ;
00247         }
00248 
00249         return r ;
00250     }
00251 
00252 
00253     private void is_diag(Object result, Object expected){
00254         diag("         got: '" + result + "'") ;
00255         diag("    expected: '" + expected + "'") ;
00256     }
00257 
00258 
00259     public boolean is(Object result, Object expected){
00260         return is(result, expected, null) ;
00261     }
00262 
00263 
00264     public boolean is(Object result, Object expected, String name){
00265         boolean r = ok(equals(result, expected), name) ;
00266         if (! r){
00267             is_diag(result, expected) ;
00268         }
00269         return r ;
00270     }
00271 
00272 
00273     public boolean is(long result, long expected){
00274         return is(new Long(result), new Long(expected)) ;
00275     }
00276 
00277 
00278     public boolean is(long result, long expected, String name){
00279         return is(new Long(result), new Long(expected), name) ;
00280     }
00281 
00282 
00283     public boolean is(double result, double expected){
00284         return is(new Double(result), new Double(expected)) ;
00285     }
00286 
00287 
00288     public boolean is(double result, double expected, String name){
00289         return is(new Double(result), new Double(expected), name) ;
00290     }
00291 
00292 
00293     public boolean isnt(Object result, Object expected){
00294         return isnt(result, expected, null) ;
00295     }
00296 
00297 
00298     public boolean isnt(Object result, Object expected, String name){
00299         boolean r = ok(! equals(result, expected), name) ;
00300         if (! r){
00301             is_diag(result, expected) ;
00302         }
00303         return r ;
00304     }
00305 
00306 
00307     public boolean isnt(long result, long expected){
00308         return isnt(new Long(result), new Long(expected)) ;
00309     }
00310 
00311 
00312     public boolean isnt(long result, long expected, String name){
00313         return isnt(new Long(result), new Long(expected), name) ;
00314     }
00315 
00316 
00317     public boolean isnt(double result, double expected){
00318         return isnt(new Double(result), new Double(expected)) ;
00319     }
00320 
00321 
00322     public boolean isnt(double result, double expected, String name){
00323         return isnt(new Double(result), new Double(expected), name) ;
00324     }
00325 
00326 
00327     public boolean like(Object result, String pattern){
00328         return like(result, pattern, null) ;
00329     }
00330 
00331 
00332     public boolean like(Object result, String pattern, String name){
00333         boolean r = ok(matches(result, pattern), name) ;
00334         if (! r){
00335             diag("    " + result + " doesn't match '" + pattern + "'") ;
00336         }
00337         return r ;
00338     }
00339 
00340 
00341     public boolean unlike(Object result, String pattern){
00342         return unlike(result, pattern, null) ;
00343     }
00344 
00345 
00346     public boolean unlike(Object result, String pattern, String name){
00347         boolean r = ok(! matches(result, pattern), name) ;
00348         if (! r){
00349             diag("    " + result + " matches '" + pattern + "'") ;
00350         }
00351         return r ;
00352     }
00353 
00354 
00355     public boolean isa_ok(Object o, Class c){
00356         return isa_ok(o, c, null) ;
00357     }
00358 
00359 
00360     public boolean isa_ok(Object o, Class c, String name){
00361         boolean r = false ;
00362         if ((o == null)||(c == null)){
00363             r = false ; 
00364         }
00365         else {
00366             r = ok(c.isInstance(o), name) ;
00367         }
00368         if (! r){
00369             diag("    Object isn't a '" + c.getName() + "' it's a '" + o.getClass().getName() + "'") ;
00370         }
00371 
00372         return r ;
00373     }
00374 
00375 
00376 
00377 
00378     synchronized public void skip(String reason){
00379         skip(reason, 1) ;
00380     }
00381 
00382 
00383     synchronized public void skip(String reason, int n){
00384         for (int i = 0 ; i < n ; i++){
00385             executed_tests++ ;
00386             out.print("ok " + executed_tests + " # skip " + reason + "\n") ;
00387             out.flush() ;
00388         }
00389         throw new JTapSkipException(reason) ;
00390     }
00391 
00392 
00393     synchronized public void todo_start(String reason){
00394         if (reason.equals("")){
00395             reason = null ;
00396         }
00397         todo = reason ;
00398     }
00399 
00400 
00401     synchronized public void todo_end(){
00402         todo = null ;
00403     }
00404 
00405 
00406     synchronized public boolean diag(String msg){
00407         if (msg != null){
00408             String lines[] = msg.split("\n") ;
00409             StringBuffer buf = new StringBuffer() ; 
00410             for (int i = 0 ; i < lines.length ; i++){
00411                 buf.append("# " + lines[i] + "\n") ;
00412             }
00413             out.print(buf) ;
00414             out.flush() ;
00415         }
00416         return false ;
00417     }
00418 
00419 
00420 
00421     
00422     synchronized private void die(String reason){
00423         err.println(reason) ;
00424         test_died = true ;
00425         exit(255) ;
00426     }
00427 
00428 
00429     synchronized public void BAIL_OUT(String reason){
00430         out.println("Bail out! " + reason) ;
00431         out.flush() ;
00432         exit(255) ;
00433     }
00434 
00435 
00436     private int cleanup(){
00437         int rc = 0 ;
00438 
00439         if (! plan_set){
00440             diag("Looks like your test died before it could output anything.") ;
00441             return rc ;
00442         }
00443 
00444         if (test_died){
00445             diag("Looks like your test died just after " + executed_tests + ".") ;
00446             return rc ;
00447         }
00448 
00449         if ((! skip_all)&&(no_plan)){
00450             print_plan(executed_tests) ;
00451         }
00452 
00453         if ((! no_plan)&&(expected_tests < executed_tests)) {
00454             diag("Looks like you planned " + expected_tests + " test" + (expected_tests > 1 ? "s" : "") + " but ran "
00455                 + (executed_tests - expected_tests) + " extra.") ;
00456             rc = -1 ;
00457         }
00458 
00459         if ((! no_plan)&&(expected_tests > executed_tests)) {
00460             diag("Looks like you planned " + expected_tests + " test" + (expected_tests > 1 ? "s" : "") + " but only ran "
00461                 + executed_tests + ".") ;
00462         }
00463 
00464         if (failed_tests > 0){
00465             diag("Looks like you failed " + failed_tests + " test" + (failed_tests > 1 ? "s" : "") + " of " + executed_tests + ".") ;
00466         }
00467 
00468         return rc ;
00469     }
00470 
00471 
00472     synchronized public int exit_status(){
00473         if ((no_plan)||(! plan_set)){
00474             return failed_tests ;
00475         }
00476 
00477         if (expected_tests < executed_tests){
00478             return executed_tests - expected_tests ;
00479         }
00480 
00481         return failed_tests + (expected_tests - executed_tests) ;
00482     }
00483 
00484 
00485     synchronized public void exit(){
00486         exit(exit_status()) ;
00487     }
00488 
00489 
00490     synchronized private void exit(int rc){
00491         int alt_rc = cleanup() ;
00492         if (alt_rc != 0){
00493             rc = alt_rc ;
00494         }
00495         if (exit){
00496             System.exit(rc) ;
00497         }
00498         else {
00499             throw new JTapExitException(rc) ;
00500         }
00501     }
00502 }
00503 
00504 
00505 
00506 
00507 class JTapException extends RuntimeException {
00508     JTapException(String msg){
00509         super(msg) ;
00510     }
00511 }
00512 
00513 
00514 
00515 
00516 class JTapExitException extends JTapException {
00517     JTapExitException(int rc){
00518         super("exit " + rc) ;
00519     }
00520 }
00521 
00522 
00523 
00524 
00525 class JTapSkipException extends JTapException {
00526     JTapSkipException(String reason){
00527         super("skip " + reason) ;
00528     }
00529 }
00530 
00531 
00532