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 * TestImpl.java 25 * 26 */ 27 package org.apache.qetest; 28 29 import java.util.Properties; 30 31 /** 32 * Minimal class defining a test implementation, using a Reporter. 33 * <p>TestImpls generally interact with a Reporter, which reports 34 * out in various formats the results from this test. 35 * Most test classes should subclass from this test, as it adds 36 * structure that helps to define the conceptual logic of running 37 * a 'test'. It also provides useful default implementations.</p> 38 * <p>Users wishing a much simpler testing framework can simply 39 * implement the minimal methods in the Test interface, and use a 40 * Logger to report results instead of a Reporter.</p> 41 * @author Shane_Curcuru@lotus.com 42 * @version $Id$ 43 */ 44 public class TestImpl implements Test 45 { 46 47 /** 48 * Name (and description) of the current test. 49 * <p>Note that these are merely convenience variables - you do not need 50 * to use them. If you do use them, they should be initialized at 51 * construction time.</p> 52 */ 53 protected String testName = null; 54 55 /** 56 * Accesor method for the name of this test. 57 * 58 * NEEDSDOC ($objectName$) @return 59 */ getTestName()60 public String getTestName() 61 { 62 return testName; 63 } 64 65 /** (Name and) description of the current test. */ 66 protected String testComment = null; 67 68 /** 69 * Accesor method for a brief description of this test. 70 * 71 * NEEDSDOC ($objectName$) @return 72 */ getTestDescription()73 public String getTestDescription() 74 { 75 return testComment; 76 } 77 78 /** 79 * Default constructor - initialize testName, Comment. 80 */ TestImpl()81 public TestImpl() 82 { 83 84 // Only set them if they're not set 85 if (testName == null) 86 testName = "TestImpl.defaultName"; 87 88 if (testComment == null) 89 testComment = "TestImpl.defaultComment"; 90 } 91 92 /** Our Logger, who we tell all our secrets to. */ 93 protected Logger logger = null; 94 95 /** 96 * Accesor methods for our Logger. 97 * 98 * NEEDSDOC @param l 99 */ setLogger(Logger l)100 public void setLogger(Logger l) 101 { // no-op: our implementation always uses a Reporter 102 } 103 104 /** 105 * Accesor methods for our Logger. 106 * 107 * NEEDSDOC ($objectName$) @return 108 */ getLogger()109 public Logger getLogger() 110 { 111 return null; 112 } 113 114 /** Our Reporter, who we tell all our secrets to. */ 115 protected Reporter reporter; 116 117 /** 118 * Accesor methods for our Reporter. 119 * 120 * NEEDSDOC @param r 121 */ setReporter(Reporter r)122 public void setReporter(Reporter r) 123 { 124 if (r != null) 125 reporter = r; 126 } 127 128 /** 129 * Accesor methods for our Reporter. 130 * 131 * NEEDSDOC ($objectName$) @return 132 */ getReporter()133 public Reporter getReporter() 134 { 135 return reporter; 136 } 137 138 /** Flag to indicate a serious enough error that we should just give up. */ 139 protected boolean abortTest = false; 140 141 /** 142 * Accesor methods for our abort flag. 143 * 144 * NEEDSDOC @param a 145 */ setAbortTest(boolean a)146 public void setAbortTest(boolean a) 147 { 148 abortTest = a; 149 } 150 151 /** 152 * Accesor methods for our abort flag. 153 * 154 * NEEDSDOC ($objectName$) @return 155 */ getAbortTest()156 public boolean getAbortTest() 157 { 158 return (abortTest); 159 } 160 161 /** 162 * Run this test: main interface to cause the test to run itself. 163 * <p>A major goal of the TestImpl class is to separate the act and process 164 * of writing a test from it's actual runtime implementation. Testwriters 165 * should not need to know how their test is being executed.</p> 166 * <ul>They should simply focus on defining: 167 * <li>doTestFileInit: what setup has to be done before running the test</li> 168 * <li>testCase1, 2, ... n: individual, independent test cases</li> 169 * <li>doTestFileClose: what cleanup has to be done after running the test</li> 170 * </ul> 171 * <p>This method returns a simple boolean status as a convenience. In cases 172 * where you have a harness that runs a great many tests that normally pass, the 173 * harness can simply check this value for each test: if it's true, you could 174 * even delete any result logs then, and simply print out a meta-log stating 175 * that the test passed. Note that this does not provide any information about 176 * why a test failed (or caused an error, or whatever) - that's what the info in 177 * any Reporter's logs are for.</p> 178 * <p>If a test is aborted, then any containing harness needs not 179 * finish executing the test. Otherwise, even if part of a test fails, 180 * you should let the whole test run through.</p> 181 * <p>Harnesses should generally simply call runTest() to ask the 182 * test to run itself. In some cases a Harness might want to control 183 * the process more closely, in which case it should call: 184 * <code> 185 * test.setReporter(); // optional, depending on the test 186 * test.testFileInit(); 187 * test.runTestCases(); 188 * test.testFileClose(); 189 * </code> instead. 190 * @todo return TestResult instead of boolean flag 191 * @author Shane_Curcuru@lotus.com 192 * 193 * NEEDSDOC @param p 194 * @return status - true if test ran to completion and <b>all</b> 195 * cases passed, false otherwise 196 */ runTest(Properties p)197 public boolean runTest(Properties p) 198 { 199 200 boolean status = testFileInit(p); 201 202 if (getAbortTest()) 203 return status; 204 205 status &= runTestCases(p); 206 207 if (getAbortTest()) 208 return status; 209 210 status &= testFileClose(p); 211 212 return status; 213 } 214 215 /** 216 * Initialize this test - called once before running testcases. 217 * Predefined behavior - subclasses should <b>not</b> override this method. 218 * <p>This method is basically a composite that masks the most common 219 * implementation: creating a reporter or logger first, then initializing 220 * any data or product settings the test needs setup first. It does this 221 * by separating this method into three methods: 222 * <code> 223 * preTestFileInit(); // Create/initialize Reporter 224 * doTestFileInit(); // User-defined: initialize product under test 225 * postTestFileInit() // Report out we've completed initialization 226 * </code> 227 * </p> 228 * @author Shane_Curcuru@lotus.com 229 * @see #preTestFileInit(java.util.Properties) 230 * @see #doTestFileInit(java.util.Properties) 231 * @see #postTestFileInit(java.util.Properties) 232 * 233 * NEEDSDOC @param p 234 * 235 * NEEDSDOC ($objectName$) @return 236 */ testFileInit(Properties p)237 public boolean testFileInit(Properties p) 238 { 239 240 // Note: we don't want to use shortcut operators here, 241 // since we want each method to get called 242 // Pass the Property block to each method, so that 243 // subclasses can do initialization whenever 244 // is best for their design 245 return preTestFileInit(p) & doTestFileInit(p) & postTestFileInit(p); 246 } 247 248 /** 249 * Initialize this test - called once before running testcases. 250 * <p>Create and initialize a Reporter here.</p> 251 * <p>This implementation simply creates a default Reporter 252 * and adds a ConsoleLogger. Most test groups will want to override 253 * this method to create custom Reporters or Loggers.</p> 254 * @author Shane_Curcuru@lotus.com 255 * @see #testFileInit(java.util.Properties) 256 * 257 * NEEDSDOC @param p 258 * 259 * NEEDSDOC ($objectName$) @return 260 */ preTestFileInit(Properties p)261 public boolean preTestFileInit(Properties p) 262 { 263 264 // Pass our properties block directly to the reporter 265 // so it can use the same values in initialization 266 setReporter(new Reporter(p)); 267 reporter.addDefaultLogger(); 268 reporter.testFileInit(testName, testComment); 269 270 return true; 271 } 272 273 /** 274 * Initialize this test - called once before running testcases. 275 * <p>Subclasses <b>must</b> override this to do whatever specific 276 * processing they need to initialize their product under test.</p> 277 * <p>If for any reason the test should not continue, it <b>must</b> 278 * return false from this method.</p> 279 * @author Shane_Curcuru@lotus.com 280 * @see #testFileInit(java.util.Properties) 281 * 282 * NEEDSDOC @param p 283 * 284 * NEEDSDOC ($objectName$) @return 285 */ doTestFileInit(Properties p)286 public boolean doTestFileInit(Properties p) 287 { 288 289 // @todo implement in your subclass 290 reporter.logTraceMsg( 291 "TestImpl.doTestFileInit() default implementation - please override"); 292 293 return true; 294 } 295 296 /** 297 * Initialize this test - called once before running testcases. 298 * <p>Simply log out that our initialization has completed, 299 * so that structured-style logs will make it clear where startup 300 * code ends and testCase code begins.</p> 301 * @author Shane_Curcuru@lotus.com 302 * @see #testFileInit(java.util.Properties) 303 * 304 * NEEDSDOC @param p 305 * 306 * NEEDSDOC ($objectName$) @return 307 */ postTestFileInit(Properties p)308 public boolean postTestFileInit(Properties p) 309 { 310 311 reporter.logTraceMsg( 312 "TestImpl.postTestFileInit() initialization complete"); 313 314 return true; 315 } 316 317 /** 318 * Run all of our testcases. 319 * Subclasses must override this method. It should cause each testCase 320 * in the test to be executed independently, and then return true if and 321 * only if all testCases passed successfully. If any testCase failed or 322 * caused any unexpected errors, exceptions, etc., it should return false. 323 * @author Shane_Curcuru@lotus.com 324 * 325 * NEEDSDOC @param p 326 * @return true if all testCases passed, false otherwise 327 */ runTestCases(Properties p)328 public boolean runTestCases(Properties p) 329 { 330 331 // @todo implement in your subclass 332 reporter.logTraceMsg( 333 "TestImpl.runTestCases() default implementation - please override"); 334 335 return true; 336 } 337 338 /** 339 * Cleanup this test - called once after running testcases. 340 * @author Shane_Curcuru@lotus.com 341 * 342 * NEEDSDOC @param p 343 * @return true if cleanup successful, false otherwise 344 */ testFileClose(Properties p)345 public boolean testFileClose(Properties p) 346 { 347 348 // Note: we don't want to use shortcut operators here, 349 // since we want each method to get called 350 return preTestFileClose(p) & doTestFileClose(p) 351 & postTestFileClose(p); 352 } 353 354 /** 355 * Log a trace message - called once after running testcases. 356 * <p>Predefined behavior - subclasses should <B>not</B> override this method.</p> 357 * @todo currently is primarily here to mark that we're closing 358 * the test, in case doTestFileClose() blows up somehow. May not be needed. 359 * @author Shane_Curcuru@lotus.com 360 * @see #testFileClose() 361 * 362 * NEEDSDOC @param p 363 * 364 * NEEDSDOC ($objectName$) @return 365 */ preTestFileClose(Properties p)366 protected boolean preTestFileClose(Properties p) 367 { 368 369 // Have the reporter log a trace that the test is about to cleanup 370 reporter.logTraceMsg("TestImpl.preTestFileClose()"); 371 372 return true; 373 } 374 375 /** 376 * Cleanup this test - called once after running testcases. 377 * <p>Subclasses <b>must</b> override this to do whatever specific 378 * processing they need to cleanup after all testcases are run.</p> 379 * @author Shane_Curcuru@lotus.com 380 * 381 * NEEDSDOC @param p 382 * 383 * NEEDSDOC ($objectName$) @return 384 */ doTestFileClose(Properties p)385 public boolean doTestFileClose(Properties p) 386 { 387 388 // @todo implement in your subclass 389 reporter.logTraceMsg( 390 "TestImpl.doTestFileClose() default implementation - please override"); 391 392 return true; 393 } 394 395 /** 396 * Mark the test complete - called once after running testcases. 397 * <p>Predefined behavior - subclasses should <b>not</b> override 398 * this method. Currently just tells our reporter to log the 399 * testFileClose. This will calculate final results, and complete 400 * logging for any structured output logs (like XML files).</p> 401 * @author Shane_Curcuru@lotus.com 402 * @see #testFileClose() 403 * 404 * NEEDSDOC @param p 405 * 406 * NEEDSDOC ($objectName$) @return 407 */ postTestFileClose(Properties p)408 protected boolean postTestFileClose(Properties p) 409 { 410 411 // Have the reporter log out our completion 412 reporter.testFileClose(); 413 414 return true; 415 } 416 417 /** 418 * Main method to run test from the command line. 419 * Test subclasses <B>must</B> override, obviously. 420 * @author Shane Curcuru 421 * 422 * NEEDSDOC @param args 423 */ main(String[] args)424 public static void main(String[] args) 425 { 426 427 TestImpl app = new TestImpl(); 428 Properties p = new Properties(); 429 430 p.put(MAIN_CMDLINE, args); 431 app.runTest(p); 432 } 433 } // end of class Test 434 435