View Javadoc

1   /*
2    * $Id: AbstractMuleTestCase.java 11728 2008-05-13 07:31:11Z dirk.olmes $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.tck;
12  
13  import org.mule.MuleManager;
14  import org.mule.config.MuleConfiguration;
15  import org.mule.impl.MuleDescriptor;
16  import org.mule.tck.testmodels.mule.TestConnector;
17  import org.mule.umo.UMOComponent;
18  import org.mule.umo.UMOEvent;
19  import org.mule.umo.UMOEventContext;
20  import org.mule.umo.UMOException;
21  import org.mule.umo.UMOSession;
22  import org.mule.umo.endpoint.UMOEndpoint;
23  import org.mule.umo.endpoint.UMOImmutableEndpoint;
24  import org.mule.umo.manager.UMOManager;
25  import org.mule.umo.model.UMOModel;
26  import org.mule.umo.transformer.UMOTransformer;
27  import org.mule.util.FileUtils;
28  import org.mule.util.MuleUrlStreamHandlerFactory;
29  import org.mule.util.StringMessageUtils;
30  import org.mule.util.StringUtils;
31  import org.mule.util.SystemUtils;
32  
33  import java.io.IOException;
34  import java.net.URI;
35  import java.net.URISyntaxException;
36  import java.net.URL;
37  import java.net.URLClassLoader;
38  import java.security.CodeSource;
39  import java.util.Collections;
40  import java.util.HashMap;
41  import java.util.Iterator;
42  import java.util.Map;
43  
44  import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit;
45  import junit.framework.TestCase;
46  import junit.framework.TestResult;
47  import org.apache.commons.collections.IteratorUtils;
48  import org.apache.commons.collections.Predicate;
49  import org.apache.commons.logging.Log;
50  import org.apache.commons.logging.LogFactory;
51  
52  /**
53   * <code>AbstractMuleTestCase</code> is a base class for Mule testcases. This
54   * implementation provides services to test code for creating mock and test objects.
55   */
56  public abstract class AbstractMuleTestCase extends TestCase implements TestCaseWatchdogTimeoutHandler
57  {
58      /**
59       * Top-level directories under <code>.mule</code> which are not deleted on each
60       * test case recycle. This is required, e.g. to play nice with transaction manager
61       * recovery service object store.
62       */
63      public static final String[] IGNORED_DOT_MULE_DIRS = new String[] {"transaction-log"};
64  
65      /**
66       * This flag controls whether the text boxes will be logged when starting each test case.
67       */
68      private static final boolean verbose;
69  
70      protected final Log logger = LogFactory.getLog(this.getClass());
71  
72      // A Map of test case extension objects. JUnit creates a new TestCase instance for
73      // every method, so we need to record metainfo outside the test.
74      private static final Map testInfos = Collections.synchronizedMap(new HashMap());
75  
76      // Should be set to a string message describing any prerequisites not met.
77      private boolean offline = "true".equalsIgnoreCase(System.getProperty("org.mule.offline"));
78  
79      // Barks if the test exceeds its time limit
80      private TestCaseWatchdog watchdog;
81  
82      static
83      {
84          String muleOpts = SystemUtils.getenv("MULE_TEST_OPTS");
85          if (StringUtils.isNotBlank(muleOpts))
86          {
87              Map parsedOpts = SystemUtils.parsePropertyDefinitions(muleOpts);
88              String optVerbose = (String)parsedOpts.get("mule.verbose");
89              verbose = Boolean.valueOf(optVerbose).booleanValue();
90          }
91          else
92          {
93              // per default, revert to the old behaviour
94              verbose = true;
95          }
96  
97          // register the custom UrlStreamHandlerFactory.
98          MuleUrlStreamHandlerFactory.installUrlStreamHandlerFactory();
99      }
100 
101     public AbstractMuleTestCase()
102     {
103         super();
104 
105         TestInfo info = (TestInfo) testInfos.get(getClass().getName());
106         if (info == null)
107         {
108             info = this.createTestInfo();
109             testInfos.put(getClass().getName(), info);
110         }
111 
112         info.incTestCount();
113     }
114 
115     protected TestInfo createTestInfo()
116     {
117         return new TestInfo(this);
118     }
119 
120     protected TestInfo getTestInfo()
121     {
122         return (TestInfo) testInfos.get(this.getClass().getName());
123     }
124 
125     private void clearInfo()
126     {
127         testInfos.remove(this.getClass().getName());
128     }
129 
130     public String getName()
131     {
132         return super.getName().substring(4).replaceAll("([A-Z])", " $1").toLowerCase() + " ";
133     }
134 
135     public void run(TestResult result)
136     {
137         if (this.isExcluded())
138         {
139             if (verbose)
140             {
141                 logger.info(this.getClass().getName() + " excluded");
142             }
143             return;
144         }
145 
146         if (this.isDisabledInThisEnvironment())
147         {
148             if (verbose)
149             {
150                 logger.info(this.getClass().getName() + " disabled");
151             }
152             return;
153         }
154 
155         super.run(result);
156     }
157 
158     /**
159      * Shamelessly copy from Spring's ConditionalTestCase so in MULE-2.0 we can extend
160      * this class from ConditionalTestCase.
161      * <p/>
162      * Subclasses can override <code>isDisabledInThisEnvironment</code> to skip a single test.
163      */
164     public void runBare() throws Throwable
165     {
166         // getName will return the name of the method being run. Use the real JUnit implementation,
167         // this class has a different implementation
168         if (this.isDisabledInThisEnvironment(super.getName()))
169         {
170             logger.warn(this.getClass().getName() + "." + super.getName() + " disabled in this environment");
171             return;
172         }
173 
174         // Let JUnit handle execution
175         super.runBare();
176     }
177 
178     /**
179      * Subclasses can override this method to skip the execution of the entire test class.
180      *
181      * @return <code>true</code> if the test class should not be run.
182      */
183     protected boolean isDisabledInThisEnvironment()
184     {
185         return false;
186     }
187 
188     /**
189      * Indicates whether this test has been explicitly disabled through the configuration
190      * file loaded by TestInfo.
191      *
192      * @return whether the test has been explicitly disabled
193      */
194     protected boolean isExcluded()
195     {
196         return getTestInfo().isExcluded();
197     }
198 
199     /**
200      * Should this test run?
201      * @param testMethodName name of the test method
202      * @return whether the test should execute in the current envionment
203      */
204     protected boolean isDisabledInThisEnvironment(String testMethodName)
205     {
206         return false;
207     }
208 
209     public boolean isOffline(String method)
210     {
211         if (offline)
212         {
213             logger.warn(StringMessageUtils.getBoilerPlate(
214                 "Working offline cannot run test: " + method, '=', 80));
215         }
216         return offline;
217     }
218 
219     protected boolean isDisposeManagerPerSuite()
220     {
221         return getTestInfo().isDisposeManagerPerSuite();
222     }
223 
224     protected void setDisposeManagerPerSuite(boolean val)
225     {
226         getTestInfo().setDisposeManagerPerSuite(val);
227     }
228 
229     protected TestCaseWatchdog createWatchdog()
230     {
231         return new TestCaseWatchdog(10, TimeUnit.MINUTES, this);
232     }
233 
234     public void handleTimeout(long timeout, TimeUnit unit)
235     {
236         logger.fatal("Timeout of " + unit.toMillis(timeout) + "ms exceeded - exiting VM!");
237         Runtime.getRuntime().halt(1);
238     }
239 
240     protected final void setUp() throws Exception
241     {
242         // start a watchdog thread
243         watchdog = createWatchdog();
244         watchdog.start();
245 
246         if (verbose)
247         {
248             System.out.println(StringMessageUtils.getBoilerPlate("Testing: " + toString(), '=', 80));
249         }
250 
251         MuleManager.getConfiguration().getDefaultThreadingProfile().setDoThreading(false);
252         MuleManager.getConfiguration().setServerUrl(StringUtils.EMPTY);
253 
254         try
255         {
256             if (getTestInfo().getRunCount() == 0)
257             {
258                 if (getTestInfo().isDisposeManagerPerSuite())
259                 {
260                     // We dispose here jut in case
261                     disposeManager();
262                 }
263                 suitePreSetUp();
264             }
265             if (!getTestInfo().isDisposeManagerPerSuite())
266             {
267                 // We dispose here jut in case
268                 disposeManager();
269             }
270             doSetUp();
271             if (getTestInfo().getRunCount() == 0)
272             {
273                 suitePostSetUp();
274             }
275         }
276         catch (Exception e)
277         {
278             getTestInfo().incRunCount();
279             throw e;
280         }
281     }
282 
283     protected void suitePreSetUp() throws Exception
284     {
285         // nothing to do
286     }
287 
288     protected void suitePostSetUp() throws Exception
289     {
290         // nothing to do
291     }
292 
293     protected void suitePreTearDown() throws Exception
294     {
295         // nothing to do
296     }
297 
298     protected void suitePostTearDown() throws Exception
299     {
300         // nothing to do
301     }
302 
303     protected final void tearDown() throws Exception
304     {
305         try
306         {
307             if (getTestInfo().getRunCount() == getTestInfo().getTestCount())
308             {
309                 suitePreTearDown();
310             }
311 
312             doTearDown();
313 
314             if (!getTestInfo().isDisposeManagerPerSuite())
315             {
316                 disposeManager();
317             }
318         }
319         finally
320         {
321             try
322             {
323                 getTestInfo().incRunCount();
324                 if (getTestInfo().getRunCount() == getTestInfo().getTestCount())
325                 {
326                     try
327                     {
328                         suitePostTearDown();
329                     }
330                     finally
331                     {
332                         clearInfo();
333                         disposeManager();
334                     }
335                 }
336             }
337             finally
338             {
339                 // remove the watchdog thread in any case
340                 watchdog.cancel();
341             }
342         }
343     }
344 
345     protected void disposeManager() throws UMOException
346     {
347         if (MuleManager.isInstanciated())
348         {
349             MuleManager.getInstance().dispose();
350         }
351 
352         // do not delete TM recovery object store, everything else is good to go
353         FileUtils.deleteTree(FileUtils.newFile(MuleManager.getConfiguration().getWorkingDirectory()),
354                              IGNORED_DOT_MULE_DIRS);
355         FileUtils.deleteTree(FileUtils.newFile("./ActiveMQ"));
356         MuleManager.setConfiguration(new MuleConfiguration());
357     }
358 
359     protected void doSetUp() throws Exception
360     {
361         // template method
362     }
363 
364     protected void doTearDown() throws Exception
365     {
366         // template method
367     }
368 
369     public static UMOManager getManager(boolean disableAdminAgent) throws Exception
370     {
371         return MuleTestUtils.getManager(disableAdminAgent);
372     }
373 
374     public static UMOModel getDefaultModel() throws UMOException
375     {
376         return MuleTestUtils.getDefaultModel();
377     }
378 
379     public static UMOEndpoint getTestEndpoint(String name, String type) throws Exception
380     {
381         return MuleTestUtils.getTestEndpoint(name, type);
382     }
383 
384     public static UMOEvent getTestEvent(Object data) throws Exception
385     {
386         return MuleTestUtils.getTestEvent(data);
387     }
388 
389     public static UMOEventContext getTestEventContext(Object data) throws Exception
390     {
391         return MuleTestUtils.getTestEventContext(data);
392     }
393 
394     public static UMOTransformer getTestTransformer()
395     {
396         return MuleTestUtils.getTestTransformer();
397     }
398 
399     public static UMOEvent getTestEvent(Object data, MuleDescriptor descriptor) throws Exception
400     {
401         return MuleTestUtils.getTestEvent(data, descriptor);
402     }
403 
404     public static UMOEvent getTestEvent(Object data, UMOImmutableEndpoint endpoint) throws Exception
405     {
406         return MuleTestUtils.getTestEvent(data, endpoint);
407     }
408 
409     public static UMOEvent getTestEvent(Object data, MuleDescriptor descriptor, UMOImmutableEndpoint endpoint)
410         throws UMOException
411     {
412         return MuleTestUtils.getTestEvent(data, descriptor, endpoint);
413     }
414 
415     public static UMOSession getTestSession(UMOComponent component)
416     {
417         return MuleTestUtils.getTestSession(component);
418     }
419 
420     public static TestConnector getTestConnector()
421     {
422         return MuleTestUtils.getTestConnector();
423     }
424 
425     public static UMOComponent getTestComponent(MuleDescriptor descriptor)
426     {
427         return MuleTestUtils.getTestComponent(descriptor);
428     }
429 
430     public static MuleDescriptor getTestDescriptor(String name, String implementation) throws Exception
431     {
432         return MuleTestUtils.getTestDescriptor(name, implementation);
433     }
434 
435     public static UMOManager getTestManager() throws Exception
436     {
437         return MuleTestUtils.getManager(true);
438     }
439    
440     public static class TestInfo
441     {
442         /**
443          * Whether to dispose the manager after every method or once all tests for
444          * the class have run
445          */
446         private final String name;
447         private boolean disposeManagerPerSuite = false;
448         private boolean excluded = false;
449         private int testCount = 0;
450         private int runCount = 0;
451 
452         // this is a shorter version of the snippet from:
453         // http://www.davidflanagan.com/blog/2005_06.html#000060
454         // (see comments; DF's "manual" version works fine too)
455         public static URL getClassPathRoot(Class clazz)
456         {
457             CodeSource cs = clazz.getProtectionDomain().getCodeSource();
458             return (cs != null ? cs.getLocation() : null);
459         }
460 
461         public TestInfo(TestCase test)
462         {
463             this.name = test.getClass().getName();
464 
465             // load test exclusions
466             try
467             {
468                 // We find the physical classpath root URL of the test class and
469                 // use that to find the correct resource. Works fine everywhere,
470                 // regardless of classloaders. See MULE-2414
471                 URL[] urls = new URL[]{getClassPathRoot(test.getClass())};
472                 URL fileUrl = new URLClassLoader(urls).getResource("mule-test-exclusions.txt");
473 
474                 if (fileUrl != null)
475                 {
476                     // in case .txt is in jar
477                     URI fileUri = new URI(StringUtils.removeStart(fileUrl.toString(), "jar:"));
478 
479                     // this iterates over all lines in the exclusion file
480                     Iterator lines = FileUtils.lineIterator(FileUtils.newFile(fileUri));
481 
482                     // ..and this finds non-comments that match the test case name
483                     excluded = IteratorUtils.filteredIterator(lines, new Predicate()
484                     {
485                         public boolean evaluate(Object object)
486                         {
487                             return StringUtils.equals(name, StringUtils.trimToEmpty((String) object));
488                         }
489                     }).hasNext();
490                 }
491             }
492             catch (IOException ioex)
493             {
494                 // ignore
495             }
496             catch (URISyntaxException e)
497             {
498                 // ignore
499             }
500         }
501 
502         public int getTestCount()
503         {
504             return testCount;
505         }
506 
507         public void incTestCount()
508         {
509             testCount++;
510         }
511 
512         public int getRunCount()
513         {
514             return runCount;
515         }
516 
517         public void incRunCount()
518         {
519             runCount++;
520         }
521 
522         public String getName()
523         {
524             return name;
525         }
526 
527         public boolean isDisposeManagerPerSuite()
528         {
529             return disposeManagerPerSuite;
530         }
531 
532         public void setDisposeManagerPerSuite(boolean disposeManagerPerSuite)
533         {
534             this.disposeManagerPerSuite = disposeManagerPerSuite;
535         }
536 
537         public boolean isExcluded()
538         {
539             return excluded;
540         }
541 
542         public String toString()
543         {
544             StringBuffer buf = new StringBuffer();
545             return buf.append(name).append(", (").append(runCount).append(" / ").append(testCount).append(
546                 ") tests run, disposePerSuite=").append(disposeManagerPerSuite).toString();
547         }
548     }
549 }