View Javadoc

1   /*
2    * $Id: AbstractMuleTestCase.java 22924 2011-09-12 22:08:33Z pablo.kraan $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.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.junit4;
12  
13  import org.mule.RequestContext;
14  import org.mule.tck.junit4.rule.WarningTimeout;
15  import org.mule.util.ClassUtils;
16  import org.mule.util.IOUtils;
17  import org.mule.util.MuleUrlStreamHandlerFactory;
18  import org.mule.util.StringMessageUtils;
19  import org.mule.util.StringUtils;
20  import org.mule.util.SystemUtils;
21  
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.net.URL;
25  import java.net.URLClassLoader;
26  import java.util.Iterator;
27  import java.util.Map;
28  
29  import org.apache.commons.collections.IteratorUtils;
30  import org.apache.commons.collections.Predicate;
31  import org.apache.commons.logging.Log;
32  import org.apache.commons.logging.LogFactory;
33  import org.hamcrest.BaseMatcher;
34  import org.hamcrest.Description;
35  import org.junit.After;
36  import org.junit.AfterClass;
37  import org.junit.Before;
38  import org.junit.Rule;
39  import org.junit.rules.TestName;
40  import org.junit.rules.TestRule;
41  import org.junit.rules.Timeout;
42  
43  import static org.junit.Assume.assumeThat;
44  
45  /**
46   * <code>AbstractMuleTestCase</code> is a base class for Mule test cases. This
47   * implementation provides services to test code for creating mock and test
48   * objects.
49   */
50  public abstract class AbstractMuleTestCase
51  {
52  
53      public static final int DEFAULT_TEST_TIMEOUT_SECS = 60;
54  
55      public static final String TEST_TIMEOUT_SYSTEM_PROPERTY = "mule.test.timeoutSecs";
56  
57      /**
58       * Indicates whether the text boxes will be logged when starting each test case.
59       */
60      private static final boolean verbose;
61  
62      /**
63       * Indicates if the current test class was excluded using the mule test
64       * exclusion files. Test are executed sequentially, so is not required to
65       * maintain a list of classes.
66       */
67      private static Boolean excluded = null;
68  
69      static
70      {
71          String muleOpts = SystemUtils.getenv("MULE_TEST_OPTS");
72          if (StringUtils.isNotBlank(muleOpts))
73          {
74              Map<String, String> parsedOpts = SystemUtils.parsePropertyDefinitions(muleOpts);
75              String optVerbose = parsedOpts.get("mule.verbose");
76              verbose = Boolean.valueOf(optVerbose);
77          }
78          else
79          {
80              verbose = true;
81          }
82  
83          // register the custom UrlStreamHandlerFactory.
84          MuleUrlStreamHandlerFactory.installUrlStreamHandlerFactory();
85      }
86  
87      protected final transient Log logger = LogFactory.getLog(this.getClass());
88  
89      /**
90       * Should be set to a string message describing any prerequisites not met.
91       */
92      private boolean offline = "true".equalsIgnoreCase(System.getProperty("org.mule.offline"));
93  
94      private int testTimeoutSecs = getTimeoutSystemProperty();
95  
96      @Rule
97      public TestName name = new TestName();
98  
99      @Rule
100     public TestRule globalTimeout = createTestTimeoutRule();
101 
102     public AbstractMuleTestCase()
103     {
104         if (excluded == null)
105         {
106             excluded = isTestIncludedInExclusionFile(this);
107         }
108     }
109 
110     /**
111      * Creates the timeout rule that will be used to run the test.
112      *
113      * @return the rule used to check for test execution timeouts.
114      */
115     protected TestRule createTestTimeoutRule()
116     {
117         int millisecondsTimeout = getTestTimeoutSecs() * 1000;
118 
119         if (isFailOnTimeout())
120         {
121             return new Timeout(millisecondsTimeout);
122         }
123         else
124         {
125             return new WarningTimeout(millisecondsTimeout);
126         }
127     }
128 
129     /**
130      * Reads the mule-exclusion file for the current test class and
131      * @param test
132      */
133     protected boolean isTestIncludedInExclusionFile(AbstractMuleTestCase test)
134     {
135         boolean result = false;
136 
137         final String testName = test.getClass().getName();
138         try
139         {
140             // We find the physical classpath root URL of the test class and
141             // use that to find the correct resource. Works fine everywhere,
142             // regardless of classloaders. See MULE-2414
143             URL classUrl = ClassUtils.getClassPathRoot(test.getClass());
144             URLClassLoader tempClassLoader = new URLClassLoader(new URL[] {classUrl});
145             URL fileUrl = tempClassLoader.getResource("mule-test-exclusions.txt");
146             if (fileUrl != null)
147             {
148                 InputStream in = null;
149                 try
150                 {
151                     in = fileUrl.openStream();
152 
153                     // this iterates over all lines in the exclusion file
154                     Iterator<?> lines = IOUtils.lineIterator(in, "UTF-8");
155 
156                     // ..and this finds non-comments that match the test case name
157                     result = IteratorUtils.filteredIterator(lines, new Predicate()
158                     {
159                         @Override
160                         public boolean evaluate(Object object)
161                         {
162                             return StringUtils.equals(testName, StringUtils.trimToEmpty((String) object));
163                         }
164                     }).hasNext();
165                 }
166                 finally
167                 {
168                     IOUtils.closeQuietly(in);
169                 }
170             }
171         }
172         catch (IOException ioex)
173         {
174             // ignore
175         }
176 
177         return result;
178     }
179 
180     /**
181      * Defines the number of seconds that a test has in order to run before
182      * throwing a timeout. If the property if not defined then uses the
183      * <code>DEFAULT_MULE_TEST_TIMEOUT_SECS</code> constant.
184      *
185      * @return the timeout value expressed in seconds
186      */
187     protected int getTimeoutSystemProperty()
188     {
189         String timeoutString = System.getProperty(TEST_TIMEOUT_SYSTEM_PROPERTY, null);
190         if (timeoutString == null)
191         {
192             // unix style: MULE_TEST_TIMEOUTSECS
193             String variableName = TEST_TIMEOUT_SYSTEM_PROPERTY.toUpperCase().replace(".", "_");
194             timeoutString = System.getenv(variableName);
195         }
196 
197         int result = DEFAULT_TEST_TIMEOUT_SECS;
198         if (timeoutString != null)
199         {
200             try
201             {
202                 result = Integer.parseInt(timeoutString);
203             }
204             catch (NumberFormatException e)
205             {
206                 // Uses the default value
207             }
208         }
209 
210         return result;
211     }
212 
213     /**
214      * Subclasses can override this method to skip the execution of the entire test class.
215      *
216      * @return <code>true</code> if the test class should not be run.
217      */
218     protected boolean isDisabledInThisEnvironment()
219     {
220         return false;
221     }
222 
223     /**
224      * Indicates whether this test has been explicitly disabled through the configuration
225      * file loaded by TestInfo.
226      *
227      * @return whether the test has been explicitly disabled
228      */
229     protected boolean isExcluded()
230     {
231         return excluded;
232     }
233 
234     /**
235      * Should this test run?
236      *
237      * @param testMethodName name of the test method
238      * @return whether the test should execute in the current environment
239      */
240     protected boolean isDisabledInThisEnvironment(String testMethodName)
241     {
242         return false;
243     }
244 
245     public boolean isOffline(String method)
246     {
247         if (offline)
248         {
249             logger.warn(StringMessageUtils.getBoilerPlate(
250                     "Working offline cannot run test: " + method, '=', 80));
251         }
252 
253         return offline;
254     }
255 
256     /**
257      * Defines the timeout in seconds that will be used to run the test.
258      *
259      * @return the timeout in seconds
260      */
261     public int getTestTimeoutSecs()
262     {
263         return testTimeoutSecs;
264     }
265 
266     @Before
267     public final void initializeMuleTest()
268     {
269         printTestHeader();
270         skipTestWhenExcluded();
271         skipTestWhenDisabledInCurrentEnvironment();
272     }
273 
274     private void printTestHeader()
275     {
276         if (verbose)
277         {
278             System.out.println(StringMessageUtils.getBoilerPlate(getTestHeader(), '=', 80));
279         }
280     }
281 
282     protected String getTestHeader()
283     {
284         return "Testing: " + name.getMethodName();
285     }
286 
287     private void skipTestWhenExcluded()
288     {
289         assumeThat(this, new BaseMatcher<AbstractMuleTestCase>()
290         {
291             @Override
292             public boolean matches(Object o)
293             {
294                 return !isExcluded();
295             }
296 
297             @Override
298             public void describeTo(Description description)
299             {
300                 description.appendText("Test " + name.getMethodName() + " is excluded");
301             }
302         });
303     }
304 
305     private void skipTestWhenDisabledInCurrentEnvironment()
306     {
307         assumeThat(this, new BaseMatcher<AbstractMuleTestCase>()
308         {
309             @Override
310             public boolean matches(Object o)
311             {
312                 return !(isDisabledInThisEnvironment() || isDisabledInThisEnvironment(name.getMethodName()));
313             }
314 
315             @Override
316             public void describeTo(Description description)
317             {
318                 description.appendText("Test " + name.getMethodName() + " disabled in this environment");
319             }
320         });
321     }
322 
323     /**
324      * Indicates whether the test should fail when a timeout is reached.
325      * <p/>
326      * This feature was added to support old test cases that depend on 3rd-party
327      * resources such as a public web service. In such cases it may be desirable
328      * to not fail the test upon timeout but rather to simply log a warning.
329      *
330      * @return true if it must fail on timeout and false otherwise. Default value
331      *         is true.
332      */
333     protected boolean isFailOnTimeout()
334     {
335         return true;
336     }
337 
338     @After
339     public final void clearRequestContext()
340     {
341         RequestContext.clear();
342     }
343 
344     @AfterClass
345     public static final void clearExcludedFlag()
346     {
347         excluded = null;
348     }
349 }