1 // $Header: /home/cvs/jakarta-jmeter/src/core/org/apache/jmeter/junit/JMeterTest.java,v 1.63.2.1 2005/08/17 22:35:35 sebb Exp $ 2 /* 3 * Copyright 2002-2004 The Apache Software Foundation. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 package org.apache.jmeter.junit; 20 21 import java.io.ByteArrayInputStream; 22 import java.io.ByteArrayOutputStream; 23 import java.io.ObjectInputStream; 24 import java.io.ObjectOutputStream; 25 import java.io.Serializable; 26 import java.io.StringReader; 27 import java.io.StringWriter; 28 import java.rmi.RemoteException; 29 import java.util.Collection; 30 import java.util.HashMap; 31 import java.util.Iterator; 32 import java.util.LinkedList; 33 import java.util.List; 34 import java.util.Map; 35 import java.util.Set; 36 37 import javax.swing.JComponent; 38 39 import junit.framework.Test; 40 import junit.framework.TestSuite; 41 42 import org.apache.jmeter.engine.util.CompoundVariable; 43 import org.apache.jmeter.functions.Function; 44 import org.apache.jmeter.gui.GuiPackage; 45 import org.apache.jmeter.gui.JMeterGUIComponent; 46 import org.apache.jmeter.gui.UnsharedComponent; 47 import org.apache.jmeter.gui.action.ActionRouter; 48 import org.apache.jmeter.gui.tree.JMeterTreeListener; 49 import org.apache.jmeter.gui.tree.JMeterTreeModel; 50 import org.apache.jmeter.gui.tree.JMeterTreeNode; 51 import org.apache.jmeter.save.SaveService; 52 import org.apache.jmeter.testbeans.TestBean; 53 import org.apache.jmeter.testbeans.gui.TestBeanGUI; 54 import org.apache.jmeter.testelement.TestElement; 55 import org.apache.jmeter.testelement.property.JMeterProperty; 56 import org.apache.jmeter.testelement.property.PropertyIterator; 57 import org.apache.jmeter.util.JMeterUtils; 58 import org.apache.jorphan.logging.LoggingManager; 59 import org.apache.jorphan.reflect.ClassFinder; 60 import org.apache.jorphan.util.JOrphanUtils; 61 import org.apache.log.Logger; 62 import org.jdom.Document; 63 import org.jdom.Element; 64 import org.jdom.input.SAXBuilder; 65 66 /** 67 * @version $Revision: 1.63.2.1 $ Last update $Date: 2005/08/17 22:35:35 $ 68 */ 69 public class JMeterTest extends JMeterTestCase { 70 private static Logger log = LoggingManager.getLoggerForClass(); 71 72 private static Map guiTitles; 73 74 private static Map guiTags; 75 76 private static Map funcTitles; 77 78 public JMeterTest(String name) { 79 super(name); 80 } 81 82 /* 83 * The suite() method creates separate test suites for each of the types of 84 * test. The suitexxx() methods create a list of items to be tested, and 85 * create a new test instance for each. 86 * 87 * Each test type has its own constructor, which saves the item to be tested 88 * 89 * Note that the suite() method must be static, and the methods to run the 90 * tests must be instance methods so that they can pick up the item value 91 * which was saved by the constructor. 92 * 93 */ 94 // Constructor for TestElement tests 95 private TestElement testItem; 96 97 public JMeterTest(String testName, TestElement te) { 98 super(testName);// Save the method name 99 testItem = te; 100 } 101 102 // Constructor for Serializable tests 103 private Serializable serObj; 104 105 public JMeterTest(String testName, Serializable ser) { 106 super(testName);// Save the method name 107 serObj = ser; 108 } 109 110 // Constructor for GUI tests 111 private JMeterGUIComponent guiItem; 112 113 public JMeterTest(String testName, JMeterGUIComponent gc) { 114 super(testName);// Save the method name 115 guiItem = gc; 116 } 117 118 // Constructor for Function tests 119 private Function funcItem; 120 121 private static boolean classPathShown = false;// Only show classpath once 122 123 public JMeterTest(String testName, Function fi) { 124 super(testName);// Save the method name 125 funcItem = fi; 126 } 127 128 /* 129 * Use a suite to allow the tests to be generated at run-time 130 */ 131 public static Test suite() throws Exception { 132 // ensure the GuiPackage is initialized. 133 JMeterTreeModel treeModel = new JMeterTreeModel(); 134 JMeterTreeListener treeLis = new JMeterTreeListener(treeModel); 135 treeLis.setActionHandler(ActionRouter.getInstance()); 136 GuiPackage.getInstance(treeLis, treeModel); 137 try { 138 // The GuiPackage needs a MainFrame to work: 139 org.apache.jmeter.gui.MainFrame main = new org.apache.jmeter.gui.MainFrame(ActionRouter.getInstance(), 140 treeModel, treeLis); 141 } catch (RuntimeException e) { 142 System.out.println("Cannot create MainFrame: " + e); 143 } 144 145 TestSuite suite = new TestSuite(); 146 suite.addTest(new JMeterTest("createTitleSet")); 147 suite.addTest(new JMeterTest("createTagSet")); 148 suite.addTest(suiteGUIComponents()); 149 suite.addTest(suiteSerializableElements()); 150 suite.addTest(suiteTestElements()); 151 suite.addTest(suiteBeanComponents()); 152 suite.addTest(new JMeterTest("createFunctionSet")); 153 suite.addTest(suiteFunctions()); 154 suite.addTest(new JMeterTest("checkGuiSet")); 155 suite.addTest(new JMeterTest("checkFunctionSet")); 156 return suite; 157 } 158 159 /* 160 * Extract titles from component_reference.xml 161 */ 162 public void createTitleSet() throws Exception { 163 guiTitles = new HashMap(90); 164 165 String compref = "../xdocs/usermanual/component_reference.xml"; 166 SAXBuilder bldr = new SAXBuilder(); 167 Document doc; 168 doc = bldr.build(compref); 169 Element root = doc.getRootElement(); 170 Element body = root.getChild("body"); 171 List sections = body.getChildren("section"); 172 for (int i = 0; i < sections.size(); i++) { 173 List components = ((Element) sections.get(i)).getChildren("component"); 174 for (int j = 0; j < components.size(); j++) { 175 Element comp = (Element) components.get(j); 176 String nm=comp.getAttributeValue("name"); 177 if (!nm.equals("SSL Manager")){// Not a true GUI component 178 guiTitles.put(nm.replace(' ','_'), Boolean.FALSE); 179 } 180 } 181 } 182 // Add titles that don't need to be documented 183 //guiTitles.put("Root", Boolean.FALSE); 184 guiTitles.put("Example Sampler", Boolean.FALSE); 185 } 186 187 /* 188 * Extract titles from component_reference.xml 189 */ 190 public void createTagSet() throws Exception { 191 guiTags = new HashMap(90); 192 193 String compref = "../xdocs/usermanual/component_reference.xml"; 194 SAXBuilder bldr = new SAXBuilder(); 195 Document doc; 196 doc = bldr.build(compref); 197 Element root = doc.getRootElement(); 198 Element body = root.getChild("body"); 199 List sections = body.getChildren("section"); 200 for (int i = 0; i < sections.size(); i++) { 201 List components = ((Element) sections.get(i)).getChildren("component"); 202 for (int j = 0; j < components.size(); j++) { 203 Element comp = (Element) components.get(j); 204 guiTags.put(comp.getAttributeValue("tag"), Boolean.FALSE); 205 } 206 } 207 } 208 209 /* 210 * Extract titles from functions.xml 211 */ 212 public void createFunctionSet() throws Exception { 213 funcTitles = new HashMap(20); 214 215 String compref = "../xdocs/usermanual/functions.xml"; 216 SAXBuilder bldr = new SAXBuilder(); 217 Document doc; 218 doc = bldr.build(compref); 219 Element root = doc.getRootElement(); 220 Element body = root.getChild("body"); 221 Element section = body.getChild("section"); 222 List sections = section.getChildren("subsection"); 223 for (int i = 0; i < sections.size(); i++) { 224 List components = ((Element) sections.get(i)).getChildren("component"); 225 for (int j = 0; j < components.size(); j++) { 226 Element comp = (Element) components.get(j); 227 funcTitles.put(comp.getAttributeValue("name"), Boolean.FALSE); 228 } 229 } 230 } 231 232 private int scanprintMap(Map m, String t) { 233 Set s = m.keySet(); 234 int unseen = 0; 235 if (s.size() == 0) 236 return 0; 237 Iterator i = s.iterator(); 238 while (i.hasNext()) { 239 Object key = i.next(); 240 if (!m.get(key).equals(Boolean.TRUE)) { 241 if (unseen == 0)// first time 242 { 243 System.out.println("\nNames remaining in " + t + " Map:"); 244 } 245 unseen++; 246 System.out.println(key); 247 } 248 } 249 return unseen; 250 } 251 252 public void checkGuiSet() throws Exception { 253 guiTitles.remove("Example Sampler");// We don't mind if this is left 254 // over 255 assertEquals("Should not have any names left over", 0, scanprintMap(guiTitles, "GUI")); 256 } 257 258 public void checkFunctionSet() throws Exception { 259 assertEquals("Should not have any names left over", 0, scanprintMap(funcTitles, "Function")); 260 } 261 262 /* 263 * Test GUI elements - create the suite of tests 264 */ 265 private static Test suiteGUIComponents() throws Exception { 266 TestSuite suite = new TestSuite("GuiComponents"); 267 Iterator iter = getObjects(JMeterGUIComponent.class).iterator(); 268 while (iter.hasNext()) { 269 JMeterGUIComponent item = (JMeterGUIComponent) iter.next(); 270 if (item instanceof JMeterTreeNode) { 271 System.out.println("INFO: JMeterGUIComponent: skipping all tests " + item.getClass().getName()); 272 continue; 273 } 274 TestSuite ts = new TestSuite(item.getClass().getName()); 275 ts.addTest(new JMeterTest("GUIComponents1", item)); 276 if (item instanceof TestBeanGUI) { 277 System.out.println("INFO: JMeterGUIComponent: skipping some tests " + item.getClass().getName()); 278 } else { 279 ts.addTest(new JMeterTest("GUIComponents2", item)); 280 ts.addTest(new JMeterTest("runGUITitle", item)); 281 } 282 suite.addTest(ts); 283 } 284 return suite; 285 } 286 287 /* 288 * Test Functions - create the suite of tests 289 */ 290 private static Test suiteFunctions() throws Exception { 291 TestSuite suite = new TestSuite("Functions"); 292 Iterator iter = getObjects(Function.class).iterator(); 293 while (iter.hasNext()) { 294 Object item = iter.next(); 295 if (item.getClass().equals(CompoundVariable.class)) { 296 continue; 297 } 298 TestSuite ts = new TestSuite(item.getClass().getName()); 299 ts.addTest(new JMeterTest("runFunction", (Function) item)); 300 ts.addTest(new JMeterTest("runFunction2", (Function) item)); 301 suite.addTest(ts); 302 } 303 return suite; 304 } 305 306 /* 307 * Test GUI elements - create the suite of tests 308 */ 309 private static Test suiteBeanComponents() throws Exception { 310 TestSuite suite = new TestSuite("BeanComponents"); 311 Iterator iter = getObjects(TestBean.class).iterator(); 312 while (iter.hasNext()) { 313 Class c = iter.next().getClass(); 314 try { 315 JMeterGUIComponent item = new TestBeanGUI(c); 316 // JMeterGUIComponent item = (JMeterGUIComponent) iter.next(); 317 TestSuite ts = new TestSuite(item.getClass().getName()); 318 ts.addTest(new JMeterTest("GUIComponents2", item)); 319 ts.addTest(new JMeterTest("runGUITitle", item)); 320 suite.addTest(ts); 321 } catch (IllegalArgumentException e) { 322 System.out.println("Cannot create test for " + c.getName() + " " + e); 323 e.printStackTrace(System.out); 324 } 325 } 326 return suite; 327 } 328 329 /* 330 * Test GUI elements - run the test 331 */ 332 public void runGUITitle() throws Exception { 333 if (guiTitles.size() > 0) { 334 String title = guiItem.getDocAnchor(); 335 boolean ct = guiTitles.containsKey(title); 336 if (ct) 337 guiTitles.put(title, Boolean.TRUE);// So we can detect extra 338 // entries 339 if (// Is this a work in progress or an internal GUI component? 340 (title != null && title.length() > 0) // Will be "" for internal components 341 && (title.toUpperCase().indexOf("(ALPHA") == -1) && (title.toUpperCase().indexOf("(BETA") == -1) 342 && (!title.equals("Example1")) // Skip the example samplers 343 // ... 344 && (!title.equals("Example2"))) {// No, not a work in 345 // progress ... 346 String s = "component_reference.xml needs '" + title + "' anchor for " + guiItem.getClass().getName(); 347 if (!ct) 348 log.warn(s); // Record in log as well 349 assertTrue(s, ct); 350 } 351 } 352 } 353 354 /* 355 * run the function test 356 */ 357 public void runFunction() throws Exception { 358 if (funcTitles.size() > 0) { 359 String title = funcItem.getReferenceKey(); 360 boolean ct = funcTitles.containsKey(title); 361 if (ct) 362 funcTitles.put(title, Boolean.TRUE);// For detecting extra 363 // entries 364 if (// Is this a work in progress ? 365 title.indexOf("(ALPHA") == -1 && title.indexOf("(EXPERIMENTAL") == -1) {// No, 366 // not 367 // a 368 // work 369 // in 370 // progress 371 // ... 372 String s = "function.xml needs '" + title + "' entry for " + funcItem.getClass().getName(); 373 if (!ct) 374 log.warn(s); // Record in log as well 375 assertTrue(s, ct); 376 } 377 } 378 } 379 380 /* 381 * Check that function descriptions are OK 382 */ 383 public void runFunction2() throws Exception { 384 Iterator i = funcItem.getArgumentDesc().iterator(); 385 while (i.hasNext()) { 386 Object o = i.next(); 387 assertTrue("Description must be a String", o instanceof String); 388 assertFalse("Description must not start with [refkey", ((String) o).startsWith("[refkey")); 389 } 390 } 391 392 /* 393 * Test GUI elements - run the test 394 */ 395 public void GUIComponents1() throws Exception { 396 String name = guiItem.getClass().getName(); 397 398 assertEquals("Name should be same as static label for " + name, guiItem.getStaticLabel(), guiItem.getName()); 399 if (!name.endsWith("TestBeanGUI")) { 400 try { 401 String label = guiItem.getLabelResource(); 402 assertTrue(label.length() > 0); 403 if (!label.equals("unused")) { // TODO use constant 404 assertFalse("'" + label + "' should be in resource file for " + name, JMeterUtils.getResString( 405 label).startsWith(JMeterUtils.RES_KEY_PFX)); 406 } 407 } catch (UnsupportedOperationException uoe) { 408 log.warn("Class has not yet implemented getLabelResource " + name); 409 } 410 } 411 } 412 413 /* 414 * Test GUI elements - run the test 415 */ 416 public void GUIComponents2() throws Exception { 417 String name = guiItem.getClass().getName(); 418 419 // TODO these assertions should be separate tests 420 421 TestElement el = guiItem.createTestElement(); 422 assertNotNull(name + ".createTestElement should be non-null ", el); 423 assertEquals("GUI-CLASS: Failed on " + name, name, el.getPropertyAsString(TestElement.GUI_CLASS)); 424 425 assertEquals("NAME: Failed on " + name, guiItem.getName(), el.getPropertyAsString(TestElement.NAME)); 426 assertEquals("TEST-CLASS: Failed on " + name, el.getClass().getName(), el 427 .getPropertyAsString(TestElement.TEST_CLASS)); 428 TestElement el2 = guiItem.createTestElement(); 429 el.setProperty(TestElement.NAME, "hey, new name!:"); 430 el.setProperty("NOT", "Shouldn't be here"); 431 if (!(guiItem instanceof UnsharedComponent)) { 432 assertEquals("SHARED: Failed on " + name, "", el2.getPropertyAsString("NOT")); 433 } 434 log.debug("Saving element: " + el.getClass()); 435 StringWriter writer = new StringWriter(); 436 SaveService.saveElement(el, writer); 437 el = (TestElement) SaveService.loadElement(new StringReader(writer.toString())); 438 log.debug("Successfully saved"); 439 guiItem.configure(el); 440 assertEquals("CONFIGURE-TEST: Failed on " + name, el.getPropertyAsString(TestElement.NAME), guiItem.getName()); 441 guiItem.modifyTestElement(el2); 442 assertEquals("Modify Test: Failed on " + name, "hey, new name!:", el2.getPropertyAsString(TestElement.NAME)); 443 } 444 445 /* 446 * Test serializable elements - create the suite of tests 447 */ 448 private static Test suiteSerializableElements() throws Exception { 449 TestSuite suite = new TestSuite("SerializableElements"); 450 Iterator iter = getObjects(Serializable.class).iterator(); 451 while (iter.hasNext()) { 452 Serializable serObj = (Serializable) iter.next(); 453 if (serObj.getClass().getName().endsWith("_Stub")) { 454 continue; 455 } 456 TestSuite ts = new TestSuite(serObj.getClass().getName()); 457 ts.addTest(new JMeterTest("runSerialTest", serObj)); 458 suite.addTest(ts); 459 } 460 return suite; 461 } 462 463 /* 464 * Test serializable elements - test the object 465 */ 466 public void runSerialTest() throws Exception { 467 if (!(serObj instanceof JComponent)) { 468 try { 469 ByteArrayOutputStream bytes = new ByteArrayOutputStream(); 470 ObjectOutputStream out = new ObjectOutputStream(bytes); 471 out.writeObject(serObj); 472 out.close(); 473 ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes.toByteArray())); 474 Object readObject = in.readObject(); 475 in.close(); 476 assertEquals("deserializing class: " + serObj.getClass().getName(), serObj.getClass(), readObject 477 .getClass()); 478 } catch (Throwable e) { 479 fail("serialization of " + serObj.getClass().getName() + " failed: " + e); 480 } 481 } 482 } 483 484 /* 485 * Test TestElements - create the suite 486 */ 487 private static Test suiteTestElements() throws Exception { 488 TestSuite suite = new TestSuite("TestElements"); 489 Iterator iter = getObjects(TestElement.class).iterator(); 490 while (iter.hasNext()) { 491 TestElement item = (TestElement) iter.next(); 492 TestSuite ts = new TestSuite(item.getClass().getName()); 493 ts.addTest(new JMeterTest("runTestElement", item)); 494 suite.addTest(ts); 495 } 496 return suite; 497 } 498 499 /* 500 * Test TestElements - implement the test case 501 */ 502 public void runTestElement() throws Exception { 503 checkElementCloning(testItem); 504 assertTrue(testItem.getClass().getName() + " must implement Serializable", testItem instanceof Serializable); 505 } 506 507 private static Collection getObjects(Class extendsClass) throws Exception { 508 String exName = extendsClass.getName(); 509 Object myThis = ""; 510 Iterator classes = ClassFinder 511 .findClassesThatExtend(JMeterUtils.getSearchPaths(), new Class[] { extendsClass }).iterator(); 512 List objects = new LinkedList(); 513 String n = ""; 514 boolean caughtError = true; 515 Throwable caught = null; 516 try { 517 while (classes.hasNext()) { 518 n = (String) classes.next(); 519 // TODO - improve this check 520 if (n.endsWith("RemoteJMeterEngineImpl")) { 521 continue; // Don't try to instantiate remote server 522 } 523 Class c = null; 524 try { 525 c = Class.forName(n); 526 try { 527 // Try with a parameter-less constructor first 528 objects.add(c.newInstance()); 529 } catch (InstantiationException e) { 530 caught = e; 531 // System.out.println(e.toString()); 532 try { 533 // Events often have this constructor 534 objects.add(c.getConstructor(new Class[] { Object.class }).newInstance( 535 new Object[] { myThis })); 536 } catch (NoSuchMethodException f) { 537 // no luck. Ignore this class 538 System.out.println("WARN: " + exName + ": NoSuchMethodException " + n); 539 } 540 } 541 } catch (NoClassDefFoundError e) { 542 // no luck. Ignore this class 543 System.out.println("WARN: " + exName + ": NoClassDefFoundError " + n); 544 } catch (IllegalAccessException e) { 545 caught = e; 546 System.out.println("WARN: " + exName + ": IllegalAccessException " + n); 547 // We won't test restricted-access classes. 548 } 549 // JDK1.4: catch (java.awt.HeadlessException e) 550 // JDK1.4: { 551 // JDK1.4: System.out.println("Error creating "+n+" 552 // "+e.toString()); 553 // JDK1.4: } 554 catch (Exception e) { 555 caught = e; 556 if ((e instanceof RemoteException) || e.getClass().getName().equals("java.awt.HeadlessException")// for 557 // JDK1.3 558 ) { 559 System.out.println("WARN: " + "Error creating " + n + " " + e.toString()); 560 } else { 561 throw new Exception("Error creating " + n + " " + e.toString()); 562 } 563 } 564 } 565 caughtError = false; 566 } finally { 567 if (caughtError) { 568 System.out.println("Last class=" + n); 569 System.out.println("objects.size=" + objects.size()); 570 System.out.println("Last error=" + caught); 571 } 572 } 573 574 if (objects.size() == 0) { 575 System.out.println("No classes found that extend " + exName + ". Check the following:"); 576 System.out.println("Search paths are:"); 577 String ss[] = JMeterUtils.getSearchPaths(); 578 for (int i = 0; i < ss.length; i++) { 579 System.out.println(ss[i]); 580 } 581 if (!classPathShown) {// Only dump it once 582 System.out.println("Class path is:"); 583 String cp = System.getProperty("java.class.path"); 584 String cpe[] = JOrphanUtils.split(cp, java.io.File.pathSeparator); 585 for (int i = 0; i < cpe.length; i++) { 586 System.out.println(cpe[i]); 587 } 588 classPathShown = true; 589 } 590 } 591 return objects; 592 } 593 594 private static void cloneTesting(TestElement item, TestElement clonedItem) { 595 assertTrue(item != clonedItem); 596 assertEquals("CLONE-SAME-CLASS: testing " + item.getClass().getName(), item.getClass().getName(), clonedItem 597 .getClass().getName()); 598 } 599 600 private static void checkElementCloning(TestElement item) { 601 TestElement clonedItem = (TestElement) item.clone(); 602 cloneTesting(item, clonedItem); 603 PropertyIterator iter2 = item.propertyIterator(); 604 while (iter2.hasNext()) { 605 JMeterProperty item2 = iter2.next(); 606 // [sebb] assertEquals(item2, 607 // clonedItem.getProperty(item2.getName())); 608 assertEquals(item2.getStringValue(), clonedItem.getProperty(item2.getName()).getStringValue()); 609 assertTrue(item2 != clonedItem.getProperty(item2.getName())); 610 } 611 } 612 }