1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * 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 /* 19 * $Id$ 20 */ 21 22 /* 23 * 24 * Reporter.java 25 * 26 */ 27 package org.apache.qetest; 28 29 import org.apache.test.android.TestLogger; 30 31 import java.io.File; 32 import java.io.FileWriter; 33 import java.io.IOException; 34 import java.io.PrintWriter; 35 import java.lang.reflect.Constructor; 36 import java.lang.reflect.InvocationTargetException; 37 import java.lang.reflect.Method; 38 import java.util.Enumeration; 39 import java.util.Hashtable; 40 import java.util.Properties; 41 import java.util.StringTokenizer; 42 43 /** 44 * Class defining how a test can report results including convenience methods. 45 * <p>Tests generally interact with a Reporter, which turns around to call 46 * a Logger to actually store the results. The Reporter serves as a 47 * single funnel for all results, hiding both the details and number of 48 * actual loggers that might currently be turned on (file, screen, network, 49 * etc.) from the test that created us.</p> 50 * <p>Note that Reporter adds numerous convenience methods that, while they 51 * are not strictly necessary to express a test's results, make coding 52 * tests much easier. Reporter is designed to be subclassed for your 53 * particular application; in general you only need to provide setup mechanisims 54 * specific to your testing/product environment.</p> 55 * @todo all methods should check that available loggers are OK 56 * @todo explain better how results are rolled up and calculated 57 * @author Shane_Curcuru@lotus.com 58 * @author Jo_Grant@lotus.com 59 * @version $Id$ 60 */ 61 public class Reporter implements Logger 62 { 63 64 /** 65 * Parameter: (optional) Name of results summary file. 66 * <p>This is a custom parameter optionally used in writeResultsStatus.</p> 67 */ 68 public static final String OPT_SUMMARYFILE = "summaryFile"; 69 70 71 /** 72 * Constructor calls initialize(p). 73 * @param p Properties block to initialize us with. 74 */ Reporter(Properties p)75 public Reporter(Properties p) 76 { 77 ready = initialize(p); 78 79 // Android-changed: Hard-coded AndroidLogger here. Otherwise, all test failures are silent. 80 addLogger(TestLogger.class.getName(), null); 81 } 82 83 /** If we're ready to start outputting yet. */ 84 protected boolean ready = false; 85 86 //----------------------------------------------------- 87 //-------- Implement Logger Control and utility routines -------- 88 //----------------------------------------------------- 89 90 /** 91 * Return a description of what this Logger/Reporter does. 92 * @author Shane_Curcuru@lotus.com 93 * @return description of how this Logger outputs results, OR 94 * how this Reporter uses Loggers, etc.. 95 */ getDescription()96 public String getDescription() 97 { 98 return "Reporter: default reporter implementation"; 99 } 100 101 /** 102 * Returns information about the Property name=value pairs that 103 * are understood by this Logger/Reporter. 104 * @author Shane_Curcuru@lotus.com 105 * @return same as {@link java.applet.Applet.getParameterInfo}. 106 */ getParameterInfo()107 public String[][] getParameterInfo() 108 { 109 110 String pinfo[][] = 111 { 112 { OPT_LOGGERS, "String", "FQCN of Loggers to add" }, 113 { OPT_LOGFILE, "String", 114 "Name of file to use for file-based Logger output" }, 115 { OPT_LOGGINGLEVEL, "int", 116 "to setLoggingLevel() to control amount of output" }, 117 { OPT_PERFLOGGING, "boolean", 118 "if we should log performance data as well" }, 119 { OPT_INDENT, "int", 120 "number of spaces to indent for supporting Loggers" }, 121 { OPT_DEBUG, "boolean", "generic debugging flag" } 122 }; 123 124 return pinfo; 125 } 126 127 /** 128 * Accessor methods for a properties block. 129 * @return our Properties block. 130 * @todo should this clone first? 131 */ getProperties()132 public Properties getProperties() 133 { 134 return reporterProps; 135 } 136 137 /** 138 * Accessor methods for a properties block. 139 * Always having a Properties block allows users to pass common 140 * options to a Logger/Reporter without having to know the specific 141 * 'properties' on the object. 142 * <p>Much like in Applets, users can call getParameterInfo() to 143 * find out what kind of properties are available. Callers more 144 * commonly simply call initialize(p) instead of setProperties(p)</p> 145 * @author Shane_Curcuru@lotus.com 146 * @param p Properties to set (should be cloned). 147 */ setProperties(Properties p)148 public void setProperties(Properties p) 149 { 150 if (p != null) 151 reporterProps = (Properties) p.clone(); 152 } 153 154 /** 155 * Call once to initialize this Logger/Reporter from Properties. 156 * <p>Simple hook to allow Logger/Reporters with special output 157 * items to initialize themselves.</p> 158 * 159 * @author Shane_Curcuru@lotus.com 160 * @param p Properties block to initialize from. 161 * @param status, true if OK, false if an error occoured. 162 */ initialize(Properties p)163 public boolean initialize(Properties p) 164 { 165 166 setProperties(p); 167 168 String dbg = reporterProps.getProperty(OPT_DEBUG); 169 170 if ((dbg != null) && dbg.equalsIgnoreCase("true")) 171 { 172 setDebug(true); 173 } 174 175 String perf = reporterProps.getProperty(OPT_PERFLOGGING); 176 177 if ((perf != null) && perf.equalsIgnoreCase("true")) 178 { 179 setPerfLogging(true); 180 } 181 182 // int values need to be parsed 183 String logLvl = reporterProps.getProperty(OPT_LOGGINGLEVEL); 184 185 if (logLvl != null) 186 { 187 try 188 { 189 setLoggingLevel(Integer.parseInt(logLvl)); 190 } 191 catch (NumberFormatException numEx) 192 { /* no-op */ 193 } 194 } 195 196 // Add however many loggers are askedfor 197 boolean b = true; 198 StringTokenizer st = 199 new StringTokenizer(reporterProps.getProperty(OPT_LOGGERS), 200 LOGGER_SEPARATOR); 201 int i; 202 203 for (i = 0; st.hasMoreTokens(); i++) 204 { 205 String temp = st.nextToken(); 206 207 if ((temp != null) && (temp.length() > 1)) 208 { 209 b &= addLogger(temp, reporterProps); 210 } 211 } 212 213 return true; 214 } 215 216 /** 217 * Is this Logger/Reporter ready to log results? 218 * @author Shane_Curcuru@lotus.com 219 * @return status - true if it's ready to report, false otherwise 220 * @todo should we check our contained Loggers for their status? 221 */ isReady()222 public boolean isReady() 223 { 224 return ready; 225 } 226 227 /** 228 * Flush this Logger/Reporter - should ensure all output is flushed. 229 * Note that the flush operation is not necessarily pertinent to 230 * all types of Logger/Reporter - console-type Loggers no-op this. 231 * @author Shane_Curcuru@lotus.com 232 */ flush()233 public void flush() 234 { 235 236 for (int i = 0; i < numLoggers; i++) 237 { 238 loggers[i].flush(); 239 } 240 } 241 242 /** 243 * Close this Logger/Reporter - should include closing any OutputStreams, etc. 244 * Logger/Reporters should return isReady() = false after closing. 245 * @author Shane_Curcuru@lotus.com 246 */ close()247 public void close() 248 { 249 250 for (int i = 0; i < numLoggers; i++) 251 { 252 loggers[i].close(); 253 } 254 } 255 256 /** 257 * Generic properties for this Reporter. 258 * <p>Use a Properties block to make it easier to add new features 259 * and to be able to pass data to our loggers. Any properties that 260 * we recognize will be set here, and the entire block will be passed 261 * to any loggers that we control.</p> 262 */ 263 protected Properties reporterProps = new Properties(); 264 265 /** 266 * This determines the amount of data actually logged out to results. 267 * <p>Setting this higher will result in more data being logged out. 268 * Values range from Reporter.CRITICALMSG (0) to TRACEMSG (60). 269 * For non-performance-critical testing, you may wish to set this high, 270 * so all data gets logged, and then use reporting tools on the test output 271 * to filter for human use (since the appropriate level is stored with 272 * every logMsg() call)</p> 273 * @see #logMsg(int, java.lang.String) 274 */ 275 protected int loggingLevel = DEFAULT_LOGGINGLEVEL; 276 277 /** 278 * Marker that a testcase is currently running. 279 * <p>NEEDSWORK: should do a better job of reporting results in cases 280 * where users might not call testCaseInit/testCaseClose in non-nested pairs.</p> 281 */ 282 protected boolean duringTestCase = false; 283 284 /** 285 * Flag if we should force loggers closed upon testFileClose. 286 * <p>Default: true. Standalone tests can leave this alone. 287 * Test Harnesses may want to reset this so they can have multiple 288 * file results in one actual output 'file' for file-based loggers.</p> 289 */ 290 protected boolean closeOnFileClose = true; 291 292 /** 293 * Accessor method for closeOnFileClose. 294 * 295 * @return our value for closeOnFileClose 296 */ getCloseOnFileClose()297 public boolean getCloseOnFileClose() 298 { 299 return closeOnFileClose; 300 } 301 302 /** 303 * Accessor method for closeOnFileClose. 304 * 305 * @param b value to set for closeOnFileClose 306 */ setCloseOnFileClose(boolean b)307 public void setCloseOnFileClose(boolean b) 308 { 309 closeOnFileClose = b; 310 } 311 312 //----------------------------------------------------- 313 //-------- Test results computation members and methods -------- 314 //----------------------------------------------------- 315 316 /** Name of the current test. */ 317 protected String testName; 318 319 /** Description of the current test. */ 320 protected String testComment; 321 322 /** Number of current case within a test, usually automatically calculated. */ 323 protected int caseNum; 324 325 /** Description of current case within a test. */ 326 protected String caseComment; 327 328 /** Overall test result of current test, automatically calculated. */ 329 protected int testResult; 330 331 /** Overall test result of current testcase, automatically calculated. */ 332 protected int caseResult; 333 334 /** 335 * Counters for overall number of results - passes, fails, etc. 336 * @todo update this if we use TestResult objects 337 */ 338 protected static final int FILES = 0; 339 340 /** NEEDSDOC Field CASES */ 341 protected static final int CASES = 1; 342 343 /** NEEDSDOC Field CHECKS */ 344 protected static final int CHECKS = 2; 345 346 /** NEEDSDOC Field MAX_COUNTERS */ 347 protected static final int MAX_COUNTERS = CHECKS + 1; 348 349 /** 350 * Counters for overall number of results - passes, fails, etc. 351 * @todo update this if we use TestResult objects 352 */ 353 protected int[] incpCount = new int[MAX_COUNTERS]; 354 355 /** NEEDSDOC Field passCount */ 356 protected int[] passCount = new int[MAX_COUNTERS]; 357 358 /** NEEDSDOC Field ambgCount */ 359 protected int[] ambgCount = new int[MAX_COUNTERS]; 360 361 /** NEEDSDOC Field failCount */ 362 protected int[] failCount = new int[MAX_COUNTERS]; 363 364 /** NEEDSDOC Field errrCount */ 365 protected int[] errrCount = new int[MAX_COUNTERS]; 366 367 368 //----------------------------------------------------- 369 //-------- Composite Pattern Variables And Methods -------- 370 //----------------------------------------------------- 371 372 /** 373 * Optimization: max number of loggers, stored in an array. 374 * <p>This is a design decision: normally, you might use a ConsoleReporter, 375 * some sort of file-based one, and maybe a network-based one.</p> 376 */ 377 protected int MAX_LOGGERS = 3; 378 379 /** 380 * Array of loggers to whom we pass results. 381 * <p>Store our loggers in an array for optimization, since we want 382 * logging calls to take as little time as possible.</p> 383 */ 384 protected Logger[] loggers = new Logger[MAX_LOGGERS]; 385 386 /** NEEDSDOC Field numLoggers */ 387 protected int numLoggers = 0; 388 389 /** 390 * Add a new Logger to our array, optionally initializing it with Properties. 391 * <p>Store our Loggers in an array for optimization, since we want 392 * logging calls to take as little time as possible.</p> 393 * @todo enable users to add more than MAX_LOGGERS 394 * @author Gang Of Four 395 * @param rName fully qualified class name of Logger to add. 396 * @param p (optional) Properties block to initialize the Logger with. 397 * @return status - true if successful, false otherwise. 398 */ addLogger(String rName, Properties p)399 public boolean addLogger(String rName, Properties p) 400 { 401 402 if ((rName == null) || (rName.length() < 1)) 403 return false; 404 405 debugPrintln("addLogger(" + numLoggers + ", " + rName + " ...)"); 406 407 if ((numLoggers + 1) > loggers.length) 408 { 409 410 // @todo enable users to add more than MAX_LOGGERS 411 return false; 412 } 413 414 // Attempt to add Logger to our list 415 Class rClass; 416 Constructor rCtor; 417 418 try 419 { 420 rClass = Class.forName(rName); 421 422 debugPrintln("rClass is " + rClass.toString()); 423 424 if (p == null) 425 426 // @todo should somehow pass along our own props as well 427 // Need to ensure Reporter and callers of this method always 428 // coordinate the initialization of the Loggers we hold 429 { 430 loggers[numLoggers] = (Logger) rClass.newInstance(); 431 } 432 else 433 { 434 Class[] parameterTypes = new Class[1]; 435 436 parameterTypes[0] = java.util.Properties.class; 437 rCtor = rClass.getConstructor(parameterTypes); 438 439 Object[] initArgs = new Object[1]; 440 441 initArgs[0] = (Object) p; 442 loggers[numLoggers] = (Logger) rCtor.newInstance(initArgs); 443 } 444 } 445 catch (Exception e) 446 { 447 448 // @todo should we inform user why it failed? 449 // Note: the logMsg may fail since we might not have any reporters at this point! 450 debugPrintln("addLogger exception: " + e.toString()); 451 logCriticalMsg("addLogger exception: " + e.toString()); 452 logThrowable(CRITICALMSG, e, "addLogger exception:"); 453 454 return false; 455 } 456 457 // Increment counter for later use 458 numLoggers++; 459 460 return true; 461 } 462 463 /** 464 * Return an Hashtable of all active Loggers. 465 * @todo revisit; perhaps use a Vector 466 * @reurns Hash of all active Loggers; null if none 467 * 468 * NEEDSDOC ($objectName$) @return 469 */ getLoggers()470 public Hashtable getLoggers() 471 { 472 473 // Optimization 474 if (numLoggers == 0) 475 return (null); 476 477 Hashtable temp = new Hashtable(); 478 479 for (int i = 0; i < numLoggers; i++) 480 { 481 temp.put(loggers[i].getClass().getName(), loggers[i]); 482 } 483 484 return temp; 485 } 486 487 /** 488 * Add the default Logger to this Reporter, whatever it is. 489 * <p>Only adds the Logger if numLoggers <= 0; if the user has already 490 * setup another Logger, this is a no-op (for the testwriter who doesn't 491 * want the performance hit or annoyance of having Console output)</p> 492 * @author Gang Of Four 493 * @return status - true if successful, false otherwise. 494 */ addDefaultLogger()495 public boolean addDefaultLogger() 496 { 497 498 // Optimization - return true, since they already have a logger 499 if (numLoggers > 0) 500 return true; 501 502 return addLogger(DEFAULT_LOGGER, reporterProps); 503 } 504 505 //----------------------------------------------------- 506 //-------- Testfile / Testcase start and stop routines -------- 507 //----------------------------------------------------- 508 509 /** 510 * Call once to initialize your Loggers for your test file. 511 * Also resets test name, result, case results, etc. 512 * <p>Currently, you must init/close your test file before init/closing 513 * any test cases. No checking is currently done to ensure that 514 * mismatched test files are not nested. This is an area that needs 515 * design decisions and some work eventually to be a really clean design.</p> 516 * <p>Not only do nested testfiles/testcases have implications for good 517 * testing practices, they may also have implications for various Loggers, 518 * especially XML or other ones with an implicit hierarcy in the reports.</p> 519 * @author Shane_Curcuru@lotus.com 520 * @param name file name or tag specifying the test. 521 * @param comment comment about the test. 522 */ testFileInit(String name, String comment)523 public void testFileInit(String name, String comment) 524 { 525 526 testName = name; 527 testComment = comment; 528 testResult = DEFAULT_RESULT; 529 caseNum = 0; 530 caseComment = null; 531 caseResult = DEFAULT_RESULT; 532 duringTestCase = false; 533 534 for (int i = 0; i < numLoggers; i++) 535 { 536 loggers[i].testFileInit(testName, testComment); 537 } 538 539 // Log out time whole test script starts 540 // Note there is a slight delay while logPerfMsg calls all reporters 541 long t = System.currentTimeMillis(); 542 543 logPerfMsg(TEST_START, t, testName); 544 } 545 546 /** 547 * Call once to close out your test and summate results. 548 * <p>will close an open testCase before closing the file. May also 549 * force all Loggers closed if getCloseOnFileClose() (which may imply 550 * that no more output will be logged to file-based reporters)</p> 551 * @author Shane_Curcuru@lotus.com 552 * @todo make this settable as to how/where the resultsCounters get output 553 */ testFileClose()554 public void testFileClose() 555 { 556 557 // Cache the time whole test script ends 558 long t = System.currentTimeMillis(); 559 560 if (duringTestCase) 561 { 562 563 // Either user messed up (forgot to call testCaseClose) or something went wrong 564 logErrorMsg("WARNING! testFileClose when duringTestCase=true!"); 565 566 // Force call to testCaseClose() 567 testCaseClose(); 568 } 569 570 // Actually log the time the test script ends after closing any potentially open testcases 571 logPerfMsg(TEST_STOP, t, testName); 572 573 // Increment our results counters 574 incrementResultCounter(FILES, testResult); 575 576 // Print out an overall count of results by type 577 // @todo make this settable as to how/where the resultsCounters get output 578 logResultsCounters(); 579 580 // end this testfile - finish up any reporting we need to 581 for (int i = 0; i < numLoggers; i++) 582 { 583 584 // Log we're done and then flush 585 loggers[i].testFileClose(testComment, resultToString(testResult)); 586 loggers[i].flush(); 587 588 // Only close each reporter if asked to; this implies we're done 589 // and can't perform any more logging ourselves (or our reporters) 590 if (getCloseOnFileClose()) 591 { 592 loggers[i].close(); 593 } 594 } 595 596 // Note: explicitly leave testResult, caseResult, etc. set for debugging 597 // purposes or for use by external test harnesses 598 } 599 600 /** 601 * Implement Logger-only method. 602 * <p>Here, a Reporter is simply acting as a logger: so don't 603 * summate any results, do performance measuring, or anything 604 * else, just pass the call through to our Loggers. 605 * @param msg message to log out 606 * @param result result of testfile 607 */ testFileClose(String msg, String result)608 public void testFileClose(String msg, String result) 609 { 610 611 if (duringTestCase) 612 { 613 614 // Either user messed up (forgot to call testCaseClose) or something went wrong 615 logErrorMsg("WARNING! testFileClose when duringTestCase=true!"); 616 617 // Force call to testCaseClose() 618 testCaseClose(); 619 } 620 621 // end this testfile - finish up any reporting we need to 622 for (int i = 0; i < numLoggers; i++) 623 { 624 625 // Log we're done and then flush 626 loggers[i].testFileClose(testComment, resultToString(testResult)); 627 loggers[i].flush(); 628 629 // Only close each reporter if asked to; this implies we're done 630 // and can't perform any more logging ourselves (or our reporters) 631 if (getCloseOnFileClose()) 632 { 633 loggers[i].close(); 634 } 635 } 636 } 637 638 /** 639 * Call once to start each test case; logs out testcase number and your comment. 640 * <p>Testcase numbers are calculated as integers incrementing from 1. Will 641 * also close any previously init'd but not closed testcase.</p> 642 * @author Shane_Curcuru@lotus.com 643 * @todo investigate tieing this to the actual testCase methodnames, 644 * instead of blindly incrementing the counter 645 * @param comment short description of this test case's objective. 646 */ testCaseInit(String comment)647 public void testCaseInit(String comment) 648 { 649 650 if (duringTestCase) 651 { 652 653 // Either user messed up (forgot to call testCaseClose) or something went wrong 654 logErrorMsg("WARNING! testCaseInit when duringTestCase=true!"); 655 656 // Force call to testCaseClose() 657 testCaseClose(); 658 } 659 660 caseNum++; 661 662 caseComment = comment; 663 caseResult = DEFAULT_RESULT; 664 665 for (int i = 0; i < numLoggers; i++) 666 { 667 loggers[i].testCaseInit(String.valueOf(caseNum) + " " 668 + caseComment); 669 } 670 671 duringTestCase = true; 672 673 // Note there is a slight delay while logPerfMsg calls all reporters 674 long t = System.currentTimeMillis(); 675 676 logPerfMsg(CASE_START, t, caseComment); 677 } 678 679 /** 680 * Call once to end each test case and sub-summate results. 681 * @author Shane_Curcuru@lotus.com 682 */ testCaseClose()683 public void testCaseClose() 684 { 685 686 long t = System.currentTimeMillis(); 687 688 logPerfMsg(CASE_STOP, t, caseComment); 689 690 if (!duringTestCase) 691 { 692 logErrorMsg("WARNING! testCaseClose when duringTestCase=false!"); 693 694 // Force call to testCaseInit() 695 // NEEDSWORK: should we really do this? This ensures any results 696 // are well-formed, however a user might not expect this. 697 testCaseInit("WARNING! testCaseClose when duringTestCase=false!"); 698 } 699 700 duringTestCase = false; 701 testResult = java.lang.Math.max(testResult, caseResult); 702 703 // Increment our results counters 704 incrementResultCounter(CASES, caseResult); 705 706 for (int i = 0; i < numLoggers; i++) 707 { 708 loggers[i].testCaseClose( 709 String.valueOf(caseNum) + " " + caseComment, 710 resultToString(caseResult)); 711 } 712 } 713 714 /** 715 * Implement Logger-only method. 716 * <p>Here, a Reporter is simply acting as a logger: so don't 717 * summate any results, do performance measuring, or anything 718 * else, just pass the call through to our Loggers. 719 * @param msg message of name of test case to log out 720 * @param result result of testfile 721 */ testCaseClose(String msg, String result)722 public void testCaseClose(String msg, String result) 723 { 724 725 if (!duringTestCase) 726 { 727 logErrorMsg("WARNING! testCaseClose when duringTestCase=false!"); 728 729 // Force call to testCaseInit() 730 // NEEDSWORK: should we really do this? This ensures any results 731 // are well-formed, however a user might not expect this. 732 testCaseInit("WARNING! testCaseClose when duringTestCase=false!"); 733 } 734 735 duringTestCase = false; 736 737 for (int i = 0; i < numLoggers; i++) 738 { 739 loggers[i].testCaseClose( 740 String.valueOf(caseNum) + " " + caseComment, 741 resultToString(caseResult)); 742 } 743 } 744 745 /** 746 * Calls back into a Test to run test cases in order. 747 * <p>Use reflection to call back and execute each testCaseXX method 748 * in the calling test in order, catching exceptions along the way.</p> 749 * //@todo rename to 'executeTestCases' or something 750 * //@todo implement options: either an inclusion or exclusion list 751 * @author Shane Curcuru 752 * @param testObject the test object itself. 753 * @param numTestCases number of consecutively numbered test cases to execute. 754 * @param options (future use: options to pass to testcases) 755 * @return status, true if OK, false if big bad error occoured 756 */ executeTests(Test testObject, int numTestCases, Object options)757 public boolean executeTests(Test testObject, int numTestCases, 758 Object options) 759 { 760 761 // Flag denoting if we've had any errors 762 boolean gotException = false; 763 764 // Declare all needed java variables 765 String tmpErrString = "executeTests: no errors yet"; 766 Object noArgs[] = new Object[0]; // use options instead 767 Class noParams[] = new Class[0]; 768 Method currTestCase; 769 Class testClass; 770 771 // Get class reference for the test applet itself 772 testClass = testObject.getClass(); 773 774 logTraceMsg("executeTests: running " + numTestCases + " tests now."); 775 776 for (int tcNum = 1; tcNum <= numTestCases; tcNum++) 777 { 778 try 779 { // get a reference to the next test case that we'll be calling 780 tmpErrString = "executeTests: No such method: testCase" 781 + tcNum + "()"; 782 currTestCase = testClass.getMethod("testCase" + tcNum, 783 noParams); 784 785 // Now directly invoke that test case 786 tmpErrString = 787 "executeTests: Method threw an exception: testCase" 788 + tcNum + "(): "; 789 790 logTraceMsg("executeTests: invoking testCase" + tcNum 791 + " now."); 792 currTestCase.invoke(testObject, noArgs); 793 } 794 catch (InvocationTargetException ite) 795 { 796 // Catch any error, log it as an error, and allow next test case to run 797 gotException = true; 798 testResult = java.lang.Math.max(ERRR_RESULT, testResult); 799 tmpErrString += ite.toString(); 800 logErrorMsg(tmpErrString); 801 802 // Grab the contained error, log it if available 803 java.lang.Throwable containedThrowable = 804 ite.getTargetException(); 805 if (containedThrowable != null) 806 { 807 logThrowable(ERRORMSG, containedThrowable, tmpErrString + "(1)"); 808 } 809 logThrowable(ERRORMSG, ite, tmpErrString + "(2)"); 810 } // end of catch 811 catch (Throwable t) 812 { 813 // Catch any error, log it as an error, and allow next test case to run 814 gotException = true; 815 testResult = java.lang.Math.max(ERRR_RESULT, testResult); 816 tmpErrString += t.toString(); 817 logErrorMsg(tmpErrString); 818 logThrowable(ERRORMSG, t, tmpErrString); 819 } // end of catch 820 } // end of for 821 822 // Convenience functionality: remind user if they appear to 823 // have set numTestCases too low 824 try 825 { 826 // Get a reference to the *next* test case after numTestCases 827 int moreTestCase = numTestCases + 1; 828 currTestCase = testClass.getMethod("testCase" + moreTestCase, noParams); 829 830 // If we get here, we found another testCase - warn the user 831 logWarningMsg("executeTests: extra testCase"+ moreTestCase 832 + " found, perhaps numTestCases is too low?"); 833 } 834 catch (Throwable t) 835 { 836 // Ignore errors: we don't care, since they didn't 837 // ask us to look for this method anyway 838 } 839 840 // Return true only if everything passed 841 if (testResult == PASS_RESULT) 842 return true; 843 else 844 return false; 845 } // end of executeTests 846 847 //----------------------------------------------------- 848 //-------- Test results logging routines -------- 849 //----------------------------------------------------- 850 851 /** 852 * Accessor for loggingLevel, determines what level of log*() calls get output. 853 * @return loggingLevel, as an int. 854 */ getLoggingLevel()855 public int getLoggingLevel() 856 { 857 return loggingLevel; 858 } 859 860 /** 861 * Accessor for loggingLevel, determines what level of log*() calls get output. 862 * @param setLL loggingLevel; normalized to be between CRITICALMSG and TRACEMSG. 863 */ setLoggingLevel(int setLL)864 public void setLoggingLevel(int setLL) 865 { 866 867 if (setLL < CRITICALMSG) 868 { 869 loggingLevel = CRITICALMSG; 870 } 871 else if (setLL > TRACEMSG) 872 { 873 loggingLevel = TRACEMSG; 874 } 875 else 876 { 877 loggingLevel = setLL; 878 } 879 } 880 881 /** 882 * Report a comment to result file with specified severity. 883 * <p>Works in conjunction with {@link #loggingLevel }; 884 * only outputs messages that are more severe (i.e. lower) 885 * than the current logging level.</p> 886 * <p>Note that some Loggers may limit the comment string, 887 * either in overall length or by stripping any linefeeds, etc. 888 * This is to allow for optimization of file or database-type 889 * reporters with fixed fields. Users who need to log out 890 * special string data should use logArbitrary() instead.</p> 891 * <p>Remember, use {@link #check(String, String, String) 892 * various check*() methods} to report the actual results 893 * of your tests.</p> 894 * @author Shane_Curcuru@lotus.com 895 * @param level severity of message. 896 * @param msg comment to log out. 897 * @see #loggingLevel 898 */ logMsg(int level, String msg)899 public void logMsg(int level, String msg) 900 { 901 902 if (level > loggingLevel) 903 return; 904 905 for (int i = 0; i < numLoggers; i++) 906 { 907 loggers[i].logMsg(level, msg); 908 } 909 } 910 911 /** 912 * Report an arbitrary String to result file with specified severity. 913 * Log out the String provided exactly as-is. 914 * @author Shane_Curcuru@lotus.com 915 * @param level severity or class of message. 916 * @param msg arbitrary String to log out. 917 */ logArbitrary(int level, String msg)918 public void logArbitrary(int level, String msg) 919 { 920 921 if (level > loggingLevel) 922 return; 923 924 for (int i = 0; i < numLoggers; i++) 925 { 926 loggers[i].logArbitrary(level, msg); 927 } 928 } 929 930 /** 931 * Logs out statistics to result file with specified severity. 932 * <p>This is a general-purpose way to log out numeric statistics. We accept 933 * both a long and a double to allow users to save whatever kind of numbers 934 * they need to, with the simplest API. The actual meanings of the numbers 935 * are dependent on the implementer.</p> 936 * @author Shane_Curcuru@lotus.com 937 * @param level severity of message. 938 * @param lVal statistic in long format. 939 * @param dVal statistic in doubleformat. 940 * @param msg comment to log out. 941 */ logStatistic(int level, long lVal, double dVal, String msg)942 public void logStatistic(int level, long lVal, double dVal, String msg) 943 { 944 945 if (level > loggingLevel) 946 return; 947 948 for (int i = 0; i < numLoggers; i++) 949 { 950 loggers[i].logStatistic(level, lVal, dVal, msg); 951 } 952 } 953 954 /** 955 * Logs out a element to results with specified severity. 956 * This method is primarily for reporters that output to fixed 957 * structures, like files, XML data, or databases. 958 * @author Shane_Curcuru@lotus.com 959 * @param level severity of message. 960 * @param element name of enclosing element 961 * @param attrs hash of name=value attributes 962 * @param msg Object to log out; up to reporters to handle 963 * processing of this; usually logs just .toString(). 964 */ logElement(int level, String element, Hashtable attrs, Object msg)965 public void logElement(int level, String element, Hashtable attrs, 966 Object msg) 967 { 968 969 if (level > loggingLevel) 970 return; 971 972 for (int i = 0; i < numLoggers; i++) 973 { 974 loggers[i].logElement(level, element, attrs, msg); 975 } 976 } 977 978 /** 979 * Logs out Throwable.toString() and a stack trace of the 980 * Throwable with the specified severity. 981 * <p>Works in conjuntion with {@link #setLoggingLevel(int)}; 982 * only outputs messages that are more severe than the current 983 * logging level.</p> 984 * <p>This uses logArbitrary to log out your msg - message, 985 * a newline, throwable.toString(), a newline, 986 * and then throwable.printStackTrace().</p> 987 * <p>Note that this does not imply a failure or problem in 988 * a test in any way: many tests may want to verify that 989 * certain exceptions are thrown, etc.</p> 990 * @author Shane_Curcuru@lotus.com 991 * @param level severity of message. 992 * @param throwable throwable/exception to log out. 993 * @param msg description of the throwable. 994 */ logThrowable(int level, Throwable throwable, String msg)995 public void logThrowable(int level, Throwable throwable, String msg) 996 { 997 998 if (level > loggingLevel) 999 return; 1000 1001 for (int i = 0; i < numLoggers; i++) 1002 { 1003 loggers[i].logThrowable(level, throwable, msg); 1004 } 1005 } 1006 1007 /** 1008 * Logs out contents of a Hashtable with specified severity. 1009 * <p>Works in conjuntion with setLoggingLevel(int); only outputs messages that 1010 * are more severe than the current logging level.</p> 1011 * <p>Loggers should store or log the full contents of the hashtable.</p> 1012 * @author Shane_Curcuru@lotus.com 1013 * @param level severity of message. 1014 * @param hash Hashtable to log the contents of. 1015 * @param msg description of the Hashtable. 1016 */ logHashtable(int level, Hashtable hash, String msg)1017 public void logHashtable(int level, Hashtable hash, String msg) 1018 { 1019 if (level > loggingLevel) 1020 return; 1021 1022 // Don't log anyway if level is 10 or less. 1023 //@todo revisit this decision: I don't like having special 1024 // rules like this to exclude output. On the other hand, 1025 // if the user set loggingLevel this low, they really don't 1026 // want much output coming out, and hashtables are big 1027 if (loggingLevel <= 10) 1028 return; 1029 1030 for (int i = 0; i < numLoggers; i++) 1031 { 1032 loggers[i].logHashtable(level, hash, msg); 1033 } 1034 } 1035 1036 /** 1037 * Logs out an critical a comment to results; always printed out. 1038 * @author Shane_Curcuru@lotus.com 1039 * @param msg comment to log out. 1040 */ logCriticalMsg(String msg)1041 public void logCriticalMsg(String msg) 1042 { 1043 logMsg(CRITICALMSG, msg); 1044 } 1045 1046 // There is no logFailsOnlyMsg(String msg) method 1047 1048 /** 1049 * Logs out an error a comment to results. 1050 * <p>Note that subclassed libraries may choose to override to 1051 * cause a fail to happen along with printing out the message.</p> 1052 * @author Shane_Curcuru@lotus.com 1053 * @param msg comment to log out. 1054 */ logErrorMsg(String msg)1055 public void logErrorMsg(String msg) 1056 { 1057 logMsg(ERRORMSG, msg); 1058 } 1059 1060 /** 1061 * Logs out a warning a comment to results. 1062 * @author Shane_Curcuru@lotus.com 1063 * @param msg comment to log out. 1064 */ logWarningMsg(String msg)1065 public void logWarningMsg(String msg) 1066 { 1067 logMsg(WARNINGMSG, msg); 1068 } 1069 1070 /** 1071 * Logs out an status a comment to results. 1072 * @author Shane_Curcuru@lotus.com 1073 * @param msg comment to log out. 1074 */ logStatusMsg(String msg)1075 public void logStatusMsg(String msg) 1076 { 1077 logMsg(STATUSMSG, msg); 1078 } 1079 1080 /** 1081 * Logs out an informational a comment to results. 1082 * @author Shane_Curcuru@lotus.com 1083 * @param msg comment to log out. 1084 */ logInfoMsg(String msg)1085 public void logInfoMsg(String msg) 1086 { 1087 logMsg(INFOMSG, msg); 1088 } 1089 1090 /** 1091 * Logs out an trace a comment to results. 1092 * @author Shane_Curcuru@lotus.com 1093 * @param msg comment to log out. 1094 */ logTraceMsg(String msg)1095 public void logTraceMsg(String msg) 1096 { 1097 logMsg(TRACEMSG, msg); 1098 } 1099 1100 //----------------------------------------------------- 1101 //-------- Test results reporting check* routines -------- 1102 //----------------------------------------------------- 1103 // There is no public void checkIncp(String comment) method 1104 1105 /* EXPERIMENTAL: have duplicate set of check*() methods 1106 that all output some form of ID as well as comment. 1107 Leave the non-ID taking forms for both simplicity to the 1108 end user who doesn't care about IDs as well as for 1109 backwards compatibility. 1110 */ 1111 1112 /** 1113 * Writes out a Pass record with comment. 1114 * @author Shane_Curcuru@lotus.com 1115 * @param comment comment to log with the pass record. 1116 */ checkPass(String comment)1117 public void checkPass(String comment) 1118 { 1119 checkPass(comment, null); 1120 } 1121 1122 /** 1123 * Writes out an ambiguous record with comment. 1124 * @author Shane_Curcuru@lotus.com 1125 * @param comment to log with the ambg record. 1126 */ checkAmbiguous(String comment)1127 public void checkAmbiguous(String comment) 1128 { 1129 checkAmbiguous(comment, null); 1130 } 1131 1132 /** 1133 * Writes out a Fail record with comment. 1134 * @author Shane_Curcuru@lotus.com 1135 * @param comment comment to log with the fail record. 1136 */ checkFail(String comment)1137 public void checkFail(String comment) 1138 { 1139 checkFail(comment, null); 1140 } 1141 1142 1143 /** 1144 * Writes out an Error record with comment. 1145 * @author Shane_Curcuru@lotus.com 1146 * @param comment comment to log with the error record. 1147 */ checkErr(String comment)1148 public void checkErr(String comment) 1149 { 1150 checkErr(comment, null); 1151 } 1152 1153 /** 1154 * Writes out a Pass record with comment. 1155 * A Pass signifies that an individual test point has completed and has 1156 * been verified to have behaved correctly. 1157 * <p>If you need to do your own specific comparisons, you can 1158 * do them in your code and then just call checkPass or checkFail.</p> 1159 * <p>Derived classes must implement this to <B>both</B> report the 1160 * results out appropriately <B>and</B> to summate the results, if needed.</p> 1161 * <p>Pass results are a low priority, except for INCP (incomplete). Note 1162 * that if a test never calls check*(), it will have an incomplete result.</p> 1163 * @author Shane_Curcuru@lotus.com 1164 * @param comment to log with the pass record. 1165 * @param ID token to log with the pass record. 1166 */ checkPass(String comment, String id)1167 public void checkPass(String comment, String id) 1168 { 1169 1170 // Increment our results counters 1171 incrementResultCounter(CHECKS, PASS_RESULT); 1172 1173 // Special: only report it actually if needed 1174 if (getLoggingLevel() > FAILSONLY) 1175 { 1176 for (int i = 0; i < numLoggers; i++) 1177 { 1178 loggers[i].checkPass(comment, id); 1179 } 1180 } 1181 1182 caseResult = java.lang.Math.max(PASS_RESULT, caseResult); 1183 } 1184 1185 /** 1186 * Writes out an ambiguous record with comment. 1187 * <p>Ambiguous results are neither pass nor fail. Different test 1188 * libraries may have slightly different reasons for using ambg.</p> 1189 * <p>Derived classes must implement this to <B>both</B> report the 1190 * results out appropriately <B>and</B> to summate the results, if needed.</p> 1191 * <p>Ambg results have a middling priority, and take precedence over incomplete and pass.</p> 1192 * <p>An Ambiguous result may signify that the test point has completed and either 1193 * appears to have succeded, or that it has produced a result but there is no known 1194 * 'gold' result to compare it to.</p> 1195 * @author Shane_Curcuru@lotus.com 1196 * @param comment to log with the ambg record. 1197 * @param ID token to log with the pass record. 1198 */ checkAmbiguous(String comment, String id)1199 public void checkAmbiguous(String comment, String id) 1200 { 1201 1202 // Increment our results counters 1203 incrementResultCounter(CHECKS, AMBG_RESULT); 1204 1205 for (int i = 0; i < numLoggers; i++) 1206 { 1207 loggers[i].checkAmbiguous(comment, id); 1208 } 1209 1210 caseResult = java.lang.Math.max(AMBG_RESULT, caseResult); 1211 } 1212 1213 /** 1214 * Writes out a Fail record with comment. 1215 * <p>If you need to do your own specific comparisons, you can 1216 * do them in your code and then just call checkPass or checkFail.</p> 1217 * <p>Derived classes must implement this to <B>both</B> report the 1218 * results out appropriately <B>and</B> to summate the results, if needed.</p> 1219 * <p>Fail results have a high priority, and take precedence over incomplete, pass, and ambiguous.</p> 1220 * <p>A Fail signifies that an individual test point has completed and has 1221 * been verified to have behaved <B>in</B>correctly.</p> 1222 * @author Shane_Curcuru@lotus.com 1223 * @param comment to log with the fail record. 1224 * @param ID token to log with the pass record. 1225 */ checkFail(String comment, String id)1226 public void checkFail(String comment, String id) 1227 { 1228 1229 // Increment our results counters 1230 incrementResultCounter(CHECKS, FAIL_RESULT); 1231 1232 for (int i = 0; i < numLoggers; i++) 1233 { 1234 loggers[i].checkFail(comment, id); 1235 } 1236 1237 caseResult = java.lang.Math.max(FAIL_RESULT, caseResult); 1238 } 1239 1240 /** 1241 * Writes out an Error record with comment. 1242 * <p>Derived classes must implement this to <B>both</B> report the 1243 * results out appropriately <B>and</B> to summate the results, if needed.</p> 1244 * <p>Error results have the highest priority, and take precedence over 1245 * all other results.</p> 1246 * <p>An Error signifies that something unusual has gone wrong with the execution 1247 * of the test at this point - likely something that will require a human to 1248 * debug to see what really happened.</p> 1249 * @author Shane_Curcuru@lotus.com 1250 * @param comment to log with the error record. 1251 * @param ID token to log with the pass record. 1252 */ checkErr(String comment, String id)1253 public void checkErr(String comment, String id) 1254 { 1255 1256 // Increment our results counters 1257 incrementResultCounter(CHECKS, ERRR_RESULT); 1258 1259 for (int i = 0; i < numLoggers; i++) 1260 { 1261 loggers[i].checkErr(comment, id); 1262 } 1263 1264 caseResult = java.lang.Math.max(ERRR_RESULT, caseResult); 1265 } 1266 1267 //----------------------------------------------------- 1268 //-------- Simplified Performance Logging - beyond interface Reporter -------- 1269 //----------------------------------------------------- 1270 1271 /** NEEDSDOC Field DEFAULT_PERFLOGGING_LEVEL */ 1272 protected final boolean DEFAULT_PERFLOGGING_LEVEL = false; 1273 1274 /** 1275 * This determines if performance information is logged out to results. 1276 * <p>When true, extra performance records are written out to result files.</p> 1277 * @see #logPerfMsg(java.lang.String, long, java.lang.String) 1278 */ 1279 protected boolean perfLogging = DEFAULT_PERFLOGGING_LEVEL; 1280 1281 /** 1282 * Accessor for perfLogging, determines if we log performance info. 1283 * @todo add PerfLogging to Reporter interface 1284 * @return Whether or not we log performance info. 1285 */ getPerfLogging()1286 public boolean getPerfLogging() 1287 { 1288 return (perfLogging); 1289 } 1290 1291 /** 1292 * Accessor for perfLogging, determines if we log performance info. 1293 * @param Whether or not we log performance info. 1294 * 1295 * NEEDSDOC @param setPL 1296 */ setPerfLogging(boolean setPL)1297 public void setPerfLogging(boolean setPL) 1298 { 1299 perfLogging = setPL; 1300 } 1301 1302 /** 1303 * Constants used to mark performance records in output. 1304 */ 1305 1306 // Note: string representations are explicitly set to all be 1307 // 4 characters long to make it simpler to parse results 1308 public static final String TEST_START = "TSrt"; 1309 1310 /** NEEDSDOC Field TEST_STOP */ 1311 public static final String TEST_STOP = "TStp"; 1312 1313 /** NEEDSDOC Field CASE_START */ 1314 public static final String CASE_START = "CSrt"; 1315 1316 /** NEEDSDOC Field CASE_STOP */ 1317 public static final String CASE_STOP = "CStp"; 1318 1319 /** NEEDSDOC Field USER_TIMER */ 1320 public static final String USER_TIMER = "UTmr"; 1321 1322 /** NEEDSDOC Field USER_TIMESTAMP */ 1323 public static final String USER_TIMESTAMP = "UTim"; 1324 1325 /** NEEDSDOC Field USER_MEMORY */ 1326 public static final String USER_MEMORY = "UMem"; 1327 1328 /** NEEDSDOC Field PERF_SEPARATOR */ 1329 public static final String PERF_SEPARATOR = ";"; 1330 1331 /** 1332 * Logs out a performance statistic. 1333 * <p>Only logs times if perfLogging set to true.</p> 1334 * <p>As an optimization for record-based Loggers, this is a rather simplistic 1335 * way to log performance info - however it's sufficient for most purposes.</p> 1336 * @author Frank Bell 1337 * @param type type of performance statistic. 1338 * @param data long value of performance statistic. 1339 * @param msg comment to log out. 1340 */ logPerfMsg(String type, long data, String msg)1341 public void logPerfMsg(String type, long data, String msg) 1342 { 1343 1344 if (getPerfLogging()) 1345 { 1346 double dummy = 0; 1347 1348 for (int i = 0; i < numLoggers; i++) 1349 { 1350 1351 // NEEDSWORK: simply put it at the current loggingLevel we have set 1352 // Is there a better way to mesh performance output with the rest? 1353 loggers[i].logStatistic(loggingLevel, data, dummy, 1354 type + PERF_SEPARATOR + msg); 1355 } 1356 } 1357 } 1358 1359 /** 1360 * Captures current time in milliseconds, only if perfLogging. 1361 * @author Shane_Curcuru@lotus.com 1362 * @param msg comment to log out. 1363 */ 1364 protected Hashtable perfTimers = new Hashtable(); 1365 1366 /** 1367 * NEEDSDOC Method startTimer 1368 * 1369 * 1370 * NEEDSDOC @param msg 1371 */ startTimer(String msg)1372 public void startTimer(String msg) 1373 { 1374 1375 // Note optimization: only capture times if perfLogging 1376 if ((perfLogging) && (msg != null)) 1377 { 1378 perfTimers.put(msg, new Long(System.currentTimeMillis())); 1379 } 1380 } 1381 1382 /** 1383 * Captures current time in milliseconds and logs out difference. 1384 * Will only log times if perfLogging set to true. 1385 * <p>Only logs time if it finds a corresponding msg entry that was startTimer'd.</p> 1386 * @author Shane_Curcuru@lotus.com 1387 * @param msg comment to log out. 1388 */ stopTimer(String msg)1389 public void stopTimer(String msg) 1390 { 1391 1392 // Capture time immediately to reduce latency 1393 long stopTime = System.currentTimeMillis(); 1394 1395 // Note optimization: only use times if perfLogging 1396 if ((perfLogging) && (msg != null)) 1397 { 1398 Long startTime = (Long) perfTimers.get(msg); 1399 1400 logPerfMsg(USER_TIMER, (stopTime - startTime.longValue()), msg); 1401 perfTimers.remove(msg); 1402 } 1403 } 1404 1405 /** 1406 * Accessor for currently running test case number, read-only. 1407 * @return current test case number. 1408 */ getCurrentCaseNum()1409 public int getCurrentCaseNum() 1410 { 1411 return caseNum; 1412 } 1413 1414 /** 1415 * Accessor for current test case's result, read-only. 1416 * @return current test case result. 1417 */ getCurrentCaseResult()1418 public int getCurrentCaseResult() 1419 { 1420 return caseResult; 1421 } 1422 1423 /** 1424 * Accessor for current test case's description, read-only. 1425 * @return current test case result. 1426 */ getCurrentCaseComment()1427 public String getCurrentCaseComment() 1428 { 1429 return caseComment; 1430 } 1431 1432 /** 1433 * Accessor for overall test file result, read-only. 1434 * @return test file's overall result. 1435 */ getCurrentFileResult()1436 public int getCurrentFileResult() 1437 { 1438 return testResult; 1439 } 1440 1441 /** 1442 * Utility method to log out overall result counters. 1443 * 1444 * @param count number of this kind of result 1445 * @param desc description of this kind of result 1446 */ logResultsCounter(int count, String desc)1447 protected void logResultsCounter(int count, String desc) 1448 { 1449 1450 // Optimization: Only log the kinds of results we have 1451 if (count > 0) 1452 logStatistic(loggingLevel, count, 0, desc); 1453 } 1454 1455 /** Utility method to log out overall result counters. */ logResultsCounters()1456 public void logResultsCounters() 1457 { 1458 1459 // NEEDSWORK: what's the best format to display this stuff in? 1460 // NEEDSWORK: what loggingLevel should we use? 1461 // NEEDSWORK: temporarily skipping the 'files' since 1462 // we only have tests with one file being run 1463 // logResultsCounter(incpCount[FILES], "incpCount[FILES]"); 1464 logResultsCounter(incpCount[CASES], "incpCount[CASES]"); 1465 logResultsCounter(incpCount[CHECKS], "incpCount[CHECKS]"); 1466 1467 // logResultsCounter(passCount[FILES], "passCount[FILES]"); 1468 logResultsCounter(passCount[CASES], "passCount[CASES]"); 1469 logResultsCounter(passCount[CHECKS], "passCount[CHECKS]"); 1470 1471 // logResultsCounter(ambgCount[FILES], "ambgCount[FILES]"); 1472 logResultsCounter(ambgCount[CASES], "ambgCount[CASES]"); 1473 logResultsCounter(ambgCount[CHECKS], "ambgCount[CHECKS]"); 1474 1475 // logResultsCounter(failCount[FILES], "failCount[FILES]"); 1476 logResultsCounter(failCount[CASES], "failCount[CASES]"); 1477 logResultsCounter(failCount[CHECKS], "failCount[CHECKS]"); 1478 1479 // logResultsCounter(errrCount[FILES], "errrCount[FILES]"); 1480 logResultsCounter(errrCount[CASES], "errrCount[CASES]"); 1481 logResultsCounter(errrCount[CHECKS], "errrCount[CHECKS]"); 1482 } 1483 1484 /** 1485 * Utility method to store overall result counters. 1486 * 1487 * @return a Hashtable of various results items suitable for 1488 * passing to logElement as attrs 1489 */ createResultsStatusHash()1490 protected Hashtable createResultsStatusHash() 1491 { 1492 Hashtable resHash = new Hashtable(); 1493 if (incpCount[CASES] > 0) 1494 resHash.put(INCP + "-cases", new Integer(incpCount[CASES])); 1495 if (incpCount[CHECKS] > 0) 1496 resHash.put(INCP + "-checks", new Integer(incpCount[CHECKS])); 1497 1498 if (passCount[CASES] > 0) 1499 resHash.put(PASS + "-cases", new Integer(passCount[CASES])); 1500 if (passCount[CHECKS] > 0) 1501 resHash.put(PASS + "-checks", new Integer(passCount[CHECKS])); 1502 1503 if (ambgCount[CASES] > 0) 1504 resHash.put(AMBG + "-cases", new Integer(ambgCount[CASES])); 1505 if (ambgCount[CHECKS] > 0) 1506 resHash.put(AMBG + "-checks", new Integer(ambgCount[CHECKS])); 1507 1508 if (failCount[CASES] > 0) 1509 resHash.put(FAIL + "-cases", new Integer(failCount[CASES])); 1510 if (failCount[CHECKS] > 0) 1511 resHash.put(FAIL + "-checks", new Integer(failCount[CHECKS])); 1512 1513 if (errrCount[CASES] > 0) 1514 resHash.put(ERRR + "-cases", new Integer(errrCount[CASES])); 1515 if (errrCount[CHECKS] > 0) 1516 resHash.put(ERRR + "-checks", new Integer(errrCount[CHECKS])); 1517 return resHash; 1518 } 1519 1520 /** 1521 * Utility method to write out overall result counters. 1522 * 1523 * <p>This writes out both a testsummary element as well as 1524 * writing a separate marker file for the test's currently 1525 * rolled-up test results.</p> 1526 * 1527 * <p>Note if writeFile is true, we do a bunch of additional 1528 * processing, including deleting any potential marker 1529 * files, along with creating a new marker file. This section 1530 * of code explicitly does file creation and also includes 1531 * some basic XML-isms in it.</p> 1532 * 1533 * <p>Marker files look like: [testStat][testName].xml, where 1534 * testStat is the actual current status, like 1535 * Pass/Fail/Ambg/Errr/Incp, and testName comes from the 1536 * currently executing test; this may be overridden by 1537 * setting OPT_SUMMARYFILE.</p> 1538 * 1539 * @param writeFile if we should also write out a separate 1540 * Passname/Failname marker file as well 1541 */ writeResultsStatus(boolean writeFile)1542 public void writeResultsStatus(boolean writeFile) 1543 { 1544 final String DEFAULT_SUMMARY_NAME = "ResultsSummary.xml"; 1545 Hashtable resultsHash = createResultsStatusHash(); 1546 resultsHash.put("desc", testComment); 1547 resultsHash.put("testName", testName); 1548 //@todo the actual path in the property below may not necessarily 1549 // either exist or be the correct location vis-a-vis the file 1550 // that we're writing out - but it should be close 1551 resultsHash.put(OPT_LOGFILE, reporterProps.getProperty(OPT_LOGFILE, DEFAULT_SUMMARY_NAME)); 1552 try 1553 { 1554 resultsHash.put("baseref", System.getProperty("user.dir")); 1555 } 1556 catch (Exception e) { /* no-op, ignore */ } 1557 1558 String elementName = "teststatus"; 1559 String overallResult = resultToString(getCurrentFileResult()); 1560 // Ask each of our loggers to report this 1561 for (int i = 0; i < numLoggers; i++) 1562 { 1563 loggers[i].logElement(CRITICALMSG, elementName, resultsHash, overallResult); 1564 } 1565 1566 // Only continue if user asked us to 1567 if (!writeFile) 1568 return; 1569 1570 // Now write an actual file out as a marker for enclosing 1571 // harnesses and build environments 1572 1573 // Calculate the name relative to any logfile we have 1574 String logFileBase = null; 1575 try 1576 { 1577 // CanonicalPath gives a better path, especially if 1578 // you mix your path separators up 1579 logFileBase = (new File(reporterProps.getProperty(OPT_LOGFILE, DEFAULT_SUMMARY_NAME))).getCanonicalPath(); 1580 } 1581 catch (IOException ioe) 1582 { 1583 logFileBase = (new File(reporterProps.getProperty(OPT_LOGFILE, DEFAULT_SUMMARY_NAME))).getAbsolutePath(); 1584 } 1585 logFileBase = (new File(logFileBase)).getParent(); 1586 // Either use the testName or an optionally set summary name 1587 String summaryFileBase = reporterProps.getProperty(OPT_SUMMARYFILE, testName + ".xml"); 1588 final File[] summaryFiles = 1589 { 1590 // Note array is ordered; should be re-designed so this doesn't matter 1591 // Coordinate PASS name with results.marker in build.xml 1592 // File name rationale: put Pass/Fail/etc first, so they 1593 // all show up together in dir listing; include 1594 // testName so you know where it came from; make it 1595 // .xml since it is an XML file 1596 new File(logFileBase, INCP + "-" + summaryFileBase), 1597 new File(logFileBase, PASS + "-" + summaryFileBase), 1598 new File(logFileBase, AMBG + "-" + summaryFileBase), 1599 new File(logFileBase, FAIL + "-" + summaryFileBase), 1600 new File(logFileBase, ERRR + "-" + summaryFileBase) 1601 }; 1602 // Clean up any pre-existing files that might be confused 1603 // as markers from this testrun 1604 for (int i = 0; i < summaryFiles.length; i++) 1605 { 1606 if (summaryFiles[i].exists()) 1607 summaryFiles[i].delete(); 1608 } 1609 1610 File summaryFile = null; 1611 switch (getCurrentFileResult()) 1612 { 1613 case INCP_RESULT: 1614 summaryFile = summaryFiles[0]; 1615 break; 1616 case PASS_RESULT: 1617 summaryFile = summaryFiles[1]; 1618 break; 1619 case AMBG_RESULT: 1620 summaryFile = summaryFiles[2]; 1621 break; 1622 case FAIL_RESULT: 1623 summaryFile = summaryFiles[3]; 1624 break; 1625 case ERRR_RESULT: 1626 summaryFile = summaryFiles[4]; 1627 break; 1628 default: 1629 // Use error case, this should never happen 1630 summaryFile = summaryFiles[4]; 1631 break; 1632 } 1633 resultsHash.put(OPT_SUMMARYFILE, summaryFile.getPath()); 1634 // Now actually write out the summary file 1635 try 1636 { 1637 PrintWriter printWriter = new PrintWriter(new FileWriter(summaryFile)); 1638 // Fake the output of Logger.logElement mostly; except 1639 // we add an XML header so this is a legal XML doc 1640 printWriter.println("<?xml version=\"1.0\"?>"); 1641 printWriter.println("<" + elementName); 1642 for (Enumeration keys = resultsHash.keys(); 1643 keys.hasMoreElements(); /* no increment portion */ ) 1644 { 1645 Object key = keys.nextElement(); 1646 printWriter.println(key + "=\"" + resultsHash.get(key) + "\""); 1647 } 1648 printWriter.println(">"); 1649 printWriter.println(overallResult); 1650 printWriter.println("</" + elementName + ">"); 1651 printWriter.close(); 1652 } 1653 catch(Exception e) 1654 { 1655 logErrorMsg("writeResultsStatus: Can't write: " + summaryFile); 1656 } 1657 } 1658 1659 //----------------------------------------------------- 1660 //-------- Test results reporting check* routines -------- 1661 //----------------------------------------------------- 1662 1663 /** 1664 * Compares actual and expected, and logs the result, pass/fail. 1665 * The comment you pass is added along with the pass/fail, of course. 1666 * Currenly, you may pass a pair of any of these simple {type}: 1667 * <ui> 1668 * <li>boolean</li> 1669 * <li>byte</li> 1670 * <li>short</li> 1671 * <li>int</li> 1672 * <li>long</li> 1673 * <li>float</li> 1674 * <li>double</li> 1675 * <li>String</li> 1676 * </ui> 1677 * <p>While tests could simply call checkPass(comment), providing these convenience 1678 * method can save lines of code, since you can replace:</p> 1679 * <code>if (foo = bar) <BR> 1680 * checkPass(comment); <BR> 1681 * else <BR> 1682 * checkFail(comment);</code> 1683 * <p>With the much simpler:</p> 1684 * <code>check(foo, bar, comment);</code> 1685 * <p>Plus, you can either use or ignore the boolean return value.</p> 1686 * <p>Note that individual methods checkInt(...), checkLong(...), etc. also exist. 1687 * These type-independent overriden methods are provided as a convenience to 1688 * Java-only testwriters. JavaScript scripts must call the 1689 * type-specific checkInt(...), checkString(...), etc. methods directly.</p> 1690 * <p>Note that testwriters are free to ignore the boolean return value.</p> 1691 * @author Shane_Curcuru@lotus.com 1692 * @param actual value returned from your test code. 1693 * @param expected value that test should return to pass. 1694 * @param comment to log out with result. 1695 * @return status, true=pass, false otherwise 1696 * @see #checkPass 1697 * @see #checkFail 1698 * @see #checkObject 1699 */ check(boolean actual, boolean expected, String comment)1700 public boolean check(boolean actual, boolean expected, String comment) 1701 { 1702 return (checkBool(actual, expected, comment)); 1703 } 1704 1705 /** 1706 * NEEDSDOC Method check 1707 * 1708 * 1709 * NEEDSDOC @param actual 1710 * NEEDSDOC @param expected 1711 * NEEDSDOC @param comment 1712 * 1713 * NEEDSDOC (check) @return 1714 */ check(byte actual, byte expected, String comment)1715 public boolean check(byte actual, byte expected, String comment) 1716 { 1717 return (checkByte(actual, expected, comment)); 1718 } 1719 1720 /** 1721 * NEEDSDOC Method check 1722 * 1723 * 1724 * NEEDSDOC @param actual 1725 * NEEDSDOC @param expected 1726 * NEEDSDOC @param comment 1727 * 1728 * NEEDSDOC (check) @return 1729 */ check(short actual, short expected, String comment)1730 public boolean check(short actual, short expected, String comment) 1731 { 1732 return (checkShort(actual, expected, comment)); 1733 } 1734 1735 /** 1736 * NEEDSDOC Method check 1737 * 1738 * 1739 * NEEDSDOC @param actual 1740 * NEEDSDOC @param expected 1741 * NEEDSDOC @param comment 1742 * 1743 * NEEDSDOC (check) @return 1744 */ check(int actual, int expected, String comment)1745 public boolean check(int actual, int expected, String comment) 1746 { 1747 return (checkInt(actual, expected, comment)); 1748 } 1749 1750 /** 1751 * NEEDSDOC Method check 1752 * 1753 * 1754 * NEEDSDOC @param actual 1755 * NEEDSDOC @param expected 1756 * NEEDSDOC @param comment 1757 * 1758 * NEEDSDOC (check) @return 1759 */ check(long actual, long expected, String comment)1760 public boolean check(long actual, long expected, String comment) 1761 { 1762 return (checkLong(actual, expected, comment)); 1763 } 1764 1765 /** 1766 * NEEDSDOC Method check 1767 * 1768 * 1769 * NEEDSDOC @param actual 1770 * NEEDSDOC @param expected 1771 * NEEDSDOC @param comment 1772 * 1773 * NEEDSDOC (check) @return 1774 */ check(float actual, float expected, String comment)1775 public boolean check(float actual, float expected, String comment) 1776 { 1777 return (checkFloat(actual, expected, comment)); 1778 } 1779 1780 /** 1781 * NEEDSDOC Method check 1782 * 1783 * 1784 * NEEDSDOC @param actual 1785 * NEEDSDOC @param expected 1786 * NEEDSDOC @param comment 1787 * 1788 * NEEDSDOC (check) @return 1789 */ check(double actual, double expected, String comment)1790 public boolean check(double actual, double expected, String comment) 1791 { 1792 return (checkDouble(actual, expected, comment)); 1793 } 1794 1795 /** 1796 * NEEDSDOC Method check 1797 * 1798 * 1799 * NEEDSDOC @param actual 1800 * NEEDSDOC @param expected 1801 * NEEDSDOC @param comment 1802 * 1803 * NEEDSDOC (check) @return 1804 */ check(String actual, String expected, String comment)1805 public boolean check(String actual, String expected, String comment) 1806 { 1807 return (checkString(actual, expected, comment)); 1808 } 1809 1810 // No check(Object, Object, String) currently provided, please call checkObject(...) directly 1811 1812 /** 1813 * Compares actual and expected (Object), and logs the result, pass/fail. 1814 * <p><b>Special note for checkObject:</b></p> 1815 * <p>Since this takes an object reference and not a primitive type, 1816 * it works slightly differently than other check{Type} methods.</p> 1817 * <ui> 1818 * <li>If both are null, then Pass</li> 1819 * <li>Else If actual.equals(expected) than Pass</li> 1820 * <li>Else Fail</li> 1821 * </ui> 1822 * @author Shane_Curcuru@lotus.com 1823 * @param actual Object returned from your test code. 1824 * @param expected Object that test should return to pass. 1825 * @param comment to log out with result. 1826 * @see #checkPass 1827 * @see #checkFail 1828 * @see #check 1829 * 1830 * NEEDSDOC ($objectName$) @return 1831 */ checkObject(Object actual, Object expected, String comment)1832 public boolean checkObject(Object actual, Object expected, String comment) 1833 { 1834 1835 // Pass if both null, or both valid & equals 1836 if (actual != null) 1837 { 1838 if (actual.equals(expected)) 1839 { 1840 checkPass(comment); 1841 1842 return true; 1843 } 1844 else 1845 { 1846 checkFail(comment); 1847 1848 return false; 1849 } 1850 } 1851 else 1852 { // actual is null, so can't use .equals 1853 if (expected == null) 1854 { 1855 checkPass(comment); 1856 1857 return true; 1858 } 1859 else 1860 { 1861 checkFail(comment); 1862 1863 return false; 1864 } 1865 } 1866 } 1867 1868 /** 1869 * NEEDSDOC Method checkBool 1870 * 1871 * 1872 * NEEDSDOC @param actual 1873 * NEEDSDOC @param expected 1874 * NEEDSDOC @param comment 1875 * 1876 * NEEDSDOC (checkBool) @return 1877 */ checkBool(boolean actual, boolean expected, String comment)1878 public boolean checkBool(boolean actual, boolean expected, String comment) 1879 { 1880 1881 if (actual == expected) 1882 { 1883 checkPass(comment); 1884 1885 return true; 1886 } 1887 else 1888 { 1889 checkFail(comment); 1890 1891 return false; 1892 } 1893 } 1894 1895 /** 1896 * NEEDSDOC Method checkByte 1897 * 1898 * 1899 * NEEDSDOC @param actual 1900 * NEEDSDOC @param expected 1901 * NEEDSDOC @param comment 1902 * 1903 * NEEDSDOC (checkByte) @return 1904 */ checkByte(byte actual, byte expected, String comment)1905 public boolean checkByte(byte actual, byte expected, String comment) 1906 { 1907 1908 if (actual == expected) 1909 { 1910 checkPass(comment); 1911 1912 return true; 1913 } 1914 else 1915 { 1916 checkFail(comment); 1917 1918 return false; 1919 } 1920 } 1921 1922 /** 1923 * NEEDSDOC Method checkShort 1924 * 1925 * 1926 * NEEDSDOC @param actual 1927 * NEEDSDOC @param expected 1928 * NEEDSDOC @param comment 1929 * 1930 * NEEDSDOC (checkShort) @return 1931 */ checkShort(short actual, short expected, String comment)1932 public boolean checkShort(short actual, short expected, String comment) 1933 { 1934 1935 if (actual == expected) 1936 { 1937 checkPass(comment); 1938 1939 return true; 1940 } 1941 else 1942 { 1943 checkFail(comment); 1944 1945 return false; 1946 } 1947 } 1948 1949 /** 1950 * NEEDSDOC Method checkInt 1951 * 1952 * 1953 * NEEDSDOC @param actual 1954 * NEEDSDOC @param expected 1955 * NEEDSDOC @param comment 1956 * 1957 * NEEDSDOC (checkInt) @return 1958 */ checkInt(int actual, int expected, String comment)1959 public boolean checkInt(int actual, int expected, String comment) 1960 { 1961 1962 if (actual == expected) 1963 { 1964 checkPass(comment); 1965 1966 return true; 1967 } 1968 else 1969 { 1970 checkFail(comment); 1971 1972 return false; 1973 } 1974 } 1975 1976 /** 1977 * NEEDSDOC Method checkLong 1978 * 1979 * 1980 * NEEDSDOC @param actual 1981 * NEEDSDOC @param expected 1982 * NEEDSDOC @param comment 1983 * 1984 * NEEDSDOC (checkLong) @return 1985 */ checkLong(long actual, long expected, String comment)1986 public boolean checkLong(long actual, long expected, String comment) 1987 { 1988 1989 if (actual == expected) 1990 { 1991 checkPass(comment); 1992 1993 return true; 1994 } 1995 else 1996 { 1997 checkFail(comment); 1998 1999 return false; 2000 } 2001 } 2002 2003 /** 2004 * NEEDSDOC Method checkFloat 2005 * 2006 * 2007 * NEEDSDOC @param actual 2008 * NEEDSDOC @param expected 2009 * NEEDSDOC @param comment 2010 * 2011 * NEEDSDOC (checkFloat) @return 2012 */ checkFloat(float actual, float expected, String comment)2013 public boolean checkFloat(float actual, float expected, String comment) 2014 { 2015 2016 if (actual == expected) 2017 { 2018 checkPass(comment); 2019 2020 return true; 2021 } 2022 else 2023 { 2024 checkFail(comment); 2025 2026 return false; 2027 } 2028 } 2029 2030 /** 2031 * NEEDSDOC Method checkDouble 2032 * 2033 * 2034 * NEEDSDOC @param actual 2035 * NEEDSDOC @param expected 2036 * NEEDSDOC @param comment 2037 * 2038 * NEEDSDOC (checkDouble) @return 2039 */ checkDouble(double actual, double expected, String comment)2040 public boolean checkDouble(double actual, double expected, String comment) 2041 { 2042 2043 if (actual == expected) 2044 { 2045 checkPass(comment); 2046 2047 return true; 2048 } 2049 else 2050 { 2051 checkFail(comment); 2052 2053 return false; 2054 } 2055 } 2056 2057 /** 2058 * Compares actual and expected (String), and logs the result, pass/fail. 2059 * <p><b>Special note for checkString:</b></p> 2060 * <p>Since this takes a String object and not a primitive type, 2061 * it works slightly differently than other check{Type} methods.</p> 2062 * <ui> 2063 * <li>If both are null, then Pass</li> 2064 * <li>Else If actual.compareTo(expected) == 0 than Pass</li> 2065 * <li>Else Fail</li> 2066 * </ui> 2067 * @author Shane_Curcuru@lotus.com 2068 * @param actual String returned from your test code. 2069 * @param expected String that test should return to pass. 2070 * @param comment to log out with result. 2071 * @see #checkPass 2072 * @see #checkFail 2073 * @see #checkObject 2074 * 2075 * NEEDSDOC ($objectName$) @return 2076 */ checkString(String actual, String expected, String comment)2077 public boolean checkString(String actual, String expected, String comment) 2078 { 2079 2080 // Pass if both null, or both valid & equals 2081 if (actual != null) 2082 { 2083 2084 // .compareTo returns 0 if the strings match lexicographically 2085 if ((expected != null) && (actual.compareTo(expected) == 0)) 2086 { 2087 checkPass(comment); 2088 2089 return true; 2090 } 2091 else 2092 { 2093 checkFail(comment); 2094 2095 return false; 2096 } 2097 } 2098 else 2099 { // actual is null, so can't use .equals 2100 if (expected == null) 2101 { 2102 checkPass(comment); 2103 2104 return true; 2105 } 2106 else 2107 { 2108 checkFail(comment); 2109 2110 return false; 2111 } 2112 } 2113 } 2114 2115 /** 2116 * Uses an external CheckService to Compares actual and expected, 2117 * and logs the result, pass/fail. 2118 * <p>CheckServices may be implemented to do custom equivalency 2119 * checking between complex object types. It is the responsibility 2120 * of the CheckService to call back into us to report results.</p> 2121 * @author Shane_Curcuru@lotus.com 2122 * @param CheckService implementation to use 2123 * 2124 * @param service a non-null CheckService implementation for 2125 * this type of actual and expected object 2126 * @param actual Object returned from your test code. 2127 * @param expected Object that test should return to pass. 2128 * @param comment to log out with result. 2129 * @return status true if PASS_RESULT, false otherwise 2130 * @see #checkPass 2131 * @see #checkFail 2132 * @see #check 2133 */ check(CheckService service, Object actual, Object expected, String comment)2134 public boolean check(CheckService service, Object actual, 2135 Object expected, String comment) 2136 { 2137 2138 if (service == null) 2139 { 2140 checkErr("CheckService null for: " + comment); 2141 2142 return false; 2143 } 2144 2145 if (service.check(this, actual, expected, comment) == PASS_RESULT) 2146 return true; 2147 else 2148 return false; 2149 } 2150 2151 /** 2152 * Uses an external CheckService to Compares actual and expected, 2153 * and logs the result, pass/fail. 2154 */ check(CheckService service, Object actual, Object expected, String comment, String id)2155 public boolean check(CheckService service, Object actual, 2156 Object expected, String comment, String id) 2157 { 2158 2159 if (service == null) 2160 { 2161 checkErr("CheckService null for: " + comment); 2162 2163 return false; 2164 } 2165 2166 if (service.check(this, actual, expected, comment, id) == PASS_RESULT) 2167 return true; 2168 else 2169 return false; 2170 } 2171 2172 /** Flag to control internal debugging of Reporter; sends extra info to System.out. */ 2173 protected boolean debug = false; 2174 2175 /** 2176 * Accessor for internal debugging flag. 2177 * 2178 * NEEDSDOC ($objectName$) @return 2179 */ getDebug()2180 public boolean getDebug() 2181 { 2182 return (debug); 2183 } 2184 2185 /** 2186 * Accessor for internal debugging flag. 2187 * 2188 * NEEDSDOC @param setDbg 2189 */ setDebug(boolean setDbg)2190 public void setDebug(boolean setDbg) 2191 { 2192 2193 debug = setDbg; 2194 2195 debugPrintln("setDebug enabled"); // will only print if setDbg was true 2196 } 2197 2198 /** 2199 * Basic debugging output wrapper for Reporter. 2200 * 2201 * NEEDSDOC @param msg 2202 */ debugPrintln(String msg)2203 public void debugPrintln(String msg) 2204 { 2205 2206 if (!debug) 2207 return; 2208 2209 // If we have reporters, use them 2210 if (numLoggers > 0) 2211 logCriticalMsg("RI.dP: " + msg); 2212 2213 // Otherwise, just dump to the console 2214 else 2215 System.out.println("RI.dP: " + msg); 2216 } 2217 2218 /** 2219 * Utility method to increment result counters. 2220 * 2221 * NEEDSDOC @param ctrOffset 2222 * NEEDSDOC @param r 2223 */ incrementResultCounter(int ctrOffset, int r)2224 public void incrementResultCounter(int ctrOffset, int r) 2225 { 2226 2227 switch (r) 2228 { 2229 case INCP_RESULT : 2230 incpCount[ctrOffset]++; 2231 break; 2232 case PASS_RESULT : 2233 passCount[ctrOffset]++; 2234 break; 2235 case AMBG_RESULT : 2236 ambgCount[ctrOffset]++; 2237 break; 2238 case FAIL_RESULT : 2239 failCount[ctrOffset]++; 2240 break; 2241 case ERRR_RESULT : 2242 errrCount[ctrOffset]++; 2243 break; 2244 default : 2245 ; // NEEDSWORK: should we report this, or allow users to add their own counters? 2246 } 2247 } 2248 2249 /** 2250 * Utility method to translate an int result to a string. 2251 * 2252 * NEEDSDOC @param r 2253 * 2254 * NEEDSDOC ($objectName$) @return 2255 */ resultToString(int r)2256 public static String resultToString(int r) 2257 { 2258 2259 switch (r) 2260 { 2261 case INCP_RESULT : 2262 return (INCP); 2263 case PASS_RESULT : 2264 return (PASS); 2265 case AMBG_RESULT : 2266 return (AMBG); 2267 case FAIL_RESULT : 2268 return (FAIL); 2269 case ERRR_RESULT : 2270 return (ERRR); 2271 default : 2272 return ("Unkn"); // NEEDSWORK: should have better constant for this 2273 } 2274 } 2275 } // end of class Reporter 2276 2277