1   /*
2    * Copyright  2001-2004 The Apache Software Foundation
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   *
16   */
17  
18  package bexee.ant;
19  
20  import java.io.File;
21  import java.io.PrintStream;
22  import java.net.URL;
23  
24  import junit.framework.TestCase;
25  
26  import org.apache.tools.ant.BuildEvent;
27  import org.apache.tools.ant.BuildException;
28  import org.apache.tools.ant.BuildListener;
29  import org.apache.tools.ant.Project;
30  import org.apache.tools.ant.ProjectHelper;
31  
32  /***
33   * A BuildFileTest is a TestCase which executes targets from an Ant buildfile
34   * for testing.
35   *
36   * This class provides a number of utility methods for particular build file
37   * tests which extend this class.
38   *
39   * @author Nico Seessle <nico@seessle.de>
40   * @author Conor MacNeill
41   */
42  public abstract class BuildFileTest extends TestCase {
43  
44      protected Project project;
45  
46      private StringBuffer logBuffer;
47      private StringBuffer fullLogBuffer;
48      private StringBuffer outBuffer;
49      private StringBuffer errBuffer;
50      private BuildException buildException;
51  
52      /***
53       *  Constructor for the BuildFileTest object
54       *
55       * @param  name string to pass up to TestCase constructor
56       */
57      public BuildFileTest(String name) {
58          super(name);
59      }
60  
61      /***
62       *  run a target, expect for any build exception
63       *
64       *@param  target target to run
65       *@param  cause  information string to reader of report
66       */
67      protected void expectBuildException(String target, String cause) {
68          expectSpecificBuildException(target, cause, null);
69      }
70  
71      /***
72       * Assert that only the given message has been logged with a
73       * priority &gt;= INFO when running the given target.
74       */
75      protected void expectLog(String target, String log) {
76          executeTarget(target);
77          String realLog = getLog();
78          assertEquals(log, realLog);
79      }
80  
81      /***
82       * Assert that the given substring is in the log messages
83       */
84  
85      protected void assertLogContaining(String substring) {
86          String realLog = getLog();
87          assertTrue("expecting log to contain \"" + substring + "\" log was \""
88                     + realLog + "\"",
89                     realLog.indexOf(substring) >= 0);
90      }
91  
92      /***
93       * Assert that the given message has been logged with a priority
94       * &gt;= INFO when running the given target.
95       */
96      protected void expectLogContaining(String target, String log) {
97          executeTarget(target);
98          assertLogContaining(log);
99      }
100 
101     /***
102      *  Gets the log the BuildFileTest object.
103      *  only valid if configureProject() has
104      *  been called.
105      * @pre logBuffer!=null
106      * @return    The log value
107      */
108     protected String getLog() {
109         return logBuffer.toString();
110     }
111 
112     /***
113      * Assert that the given message has been logged with a priority
114      * &gt;= DEBUG when running the given target.
115      */
116     protected void expectDebuglog(String target, String log) {
117         executeTarget(target);
118         String realLog = getFullLog();
119         assertEquals(log, realLog);
120     }
121 
122     /***
123      *  Gets the log the BuildFileTest object.
124      *  only valid if configureProject() has
125      *  been called.
126      * @pre fullLogBuffer!=null
127      * @return    The log value
128      */
129     protected String getFullLog() {
130         return fullLogBuffer.toString();
131     }
132 
133     /***
134      *  execute the target, verify output matches expectations
135      *
136      *@param  target  target to execute
137      *@param  output  output to look for
138      */
139 
140     protected void expectOutput(String target, String output) {
141         executeTarget(target);
142         String realOutput = getOutput();
143         assertEquals(output, realOutput.trim());
144     }
145 
146     /***
147      *  execute the target, verify output matches expectations
148      *  and that we got the named error at the end
149      *@param  target  target to execute
150      *@param  output  output to look for
151      *@param  error   Description of Parameter
152      */
153 
154     protected void expectOutputAndError(String target, String output, String error) {
155         executeTarget(target);
156         String realOutput = getOutput();
157         assertEquals(output, realOutput);
158         String realError = getError();
159         assertEquals(error, realError);
160     }
161 
162     protected String getOutput() {
163         return cleanBuffer(outBuffer);
164     }
165 
166     protected String getError() {
167         return cleanBuffer(errBuffer);
168     }
169 
170     protected BuildException getBuildException() {
171         return buildException;
172     }
173 
174     private String cleanBuffer(StringBuffer buffer) {
175         StringBuffer cleanedBuffer = new StringBuffer();
176         boolean cr = false;
177         for (int i = 0; i < buffer.length(); i++) {
178             char ch = buffer.charAt(i);
179             if (ch == '\r') {
180                 cr = true;
181                 continue;
182             }
183 
184             if (!cr) {
185                 cleanedBuffer.append(ch);
186             } else {
187                 cleanedBuffer.append(ch);
188             }
189         }
190         return cleanedBuffer.toString();
191     }
192 
193     /***
194      *  set up to run the named project
195      *
196      * @param  filename name of project file to run
197      */
198     protected void configureProject(String filename) throws BuildException {
199         configureProject(filename, Project.MSG_DEBUG);
200     }
201 
202     /***
203      *  set up to run the named project
204      *
205      * @param  filename name of project file to run
206      */
207     protected void configureProject(String filename, int logLevel)
208         throws BuildException {
209         logBuffer = new StringBuffer();
210         fullLogBuffer = new StringBuffer();
211         project = new Project();
212         project.init();
213         project.setUserProperty( "ant.file" , new File(filename).getAbsolutePath() );
214         project.addBuildListener(new AntTestListener(logLevel));
215         ProjectHelper helper = ProjectHelper.getProjectHelper();
216         project.addReference("ant.projectHelper", helper);
217         helper.parse(project, new File(filename));
218     }
219 
220     /***
221      *  execute a target we have set up
222      * @pre configureProject has been called
223      * @param  targetName  target to run
224      */
225     protected void executeTarget(String targetName) {
226         PrintStream sysOut = System.out;
227         PrintStream sysErr = System.err;
228         try {
229             sysOut.flush();
230             sysErr.flush();
231             outBuffer = new StringBuffer();
232             PrintStream out = new PrintStream(new AntOutputStream(outBuffer));
233             System.setOut(out);
234             errBuffer = new StringBuffer();
235             PrintStream err = new PrintStream(new AntOutputStream(errBuffer));
236             System.setErr(err);
237             logBuffer = new StringBuffer();
238             fullLogBuffer = new StringBuffer();
239             buildException = null;
240             project.executeTarget(targetName);
241         } finally {
242             System.setOut(sysOut);
243             System.setErr(sysErr);
244         }
245 
246     }
247 
248     /***
249      * Get the project which has been configured for a test.
250      *
251      * @return the Project instance for this test.
252      */
253     protected Project getProject() {
254         return project;
255     }
256 
257     /***
258      * get the directory of the project
259      * @return the base dir of the project
260      */
261     protected File getProjectDir() {
262         return project.getBaseDir();
263     }
264 
265     /***
266      *  run a target, wait for a build exception
267      *
268      *@param  target target to run
269      *@param  cause  information string to reader of report
270      *@param  msg    the message value of the build exception we are waiting for
271               set to null for any build exception to be valid
272      */
273     protected void expectSpecificBuildException(String target, String cause, String msg) {
274         try {
275             executeTarget(target);
276         } catch (org.apache.tools.ant.BuildException ex) {
277             buildException = ex;
278             if ((null != msg) && (!ex.getMessage().equals(msg))) {
279                 fail("Should throw BuildException because '" + cause
280                         + "' with message '" + msg
281                         + "' (actual message '" + ex.getMessage() + "' instead)");
282             }
283             return;
284         }
285         fail("Should throw BuildException because: " + cause);
286     }
287 
288     /***
289      *  run a target, expect an exception string
290      *  containing the substring we look for (case sensitive match)
291      *
292      *@param  target target to run
293      *@param  cause  information string to reader of report
294      *@param  contains  substring of the build exception to look for
295      */
296     protected void expectBuildExceptionContaining(String target, String cause, String contains) {
297         try {
298             executeTarget(target);
299         } catch (org.apache.tools.ant.BuildException ex) {
300             buildException = ex;
301             if ((null != contains) && (ex.getMessage().indexOf(contains) == -1)) {
302                 fail("Should throw BuildException because '" + cause + "' with message containing '" + contains + "' (actual message '" + ex.getMessage() + "' instead)");
303             }
304             return;
305         }
306         fail("Should throw BuildException because: " + cause);
307     }
308 
309 
310     /***
311      * call a target, verify property is as expected
312      *
313      * @param target build file target
314      * @param property property name
315      * @param value expected value
316      */
317 
318     protected void expectPropertySet(String target, String property, String value) {
319         executeTarget(target);
320         assertPropertyEquals(property, value);
321     }
322 
323     /***
324      * assert that a property equals a value; comparison is case sensitive.
325      * @param property property name
326      * @param value expected value
327      */
328     protected void assertPropertyEquals(String property, String value) {
329         String result = project.getProperty(property);
330         assertEquals("property " + property,value,result);
331     }
332 
333     /***
334      * assert that a property equals &quot;true&quot;
335      * @param property property name
336      */
337     protected void assertPropertySet(String property) {
338         assertPropertyEquals(property, "true");
339     }
340 
341     /***
342      * assert that a property is null
343      * @param property property name
344      */
345     protected void assertPropertyUnset(String property) {
346         assertPropertyEquals(property, null);
347     }
348 
349 
350     /***
351      * call a target, verify named property is "true".
352      *
353      * @param target build file target
354      * @param property property name
355      */
356     protected void expectPropertySet(String target, String property) {
357         expectPropertySet(target, property, "true");
358     }
359 
360 
361     /***
362      * call a target, verify property is null
363      * @param target build file target
364      * @param property property name
365      */
366     protected void expectPropertyUnset(String target, String property) {
367         expectPropertySet(target, property, null);
368     }
369 
370     /***
371      * Retrieve a resource from the caller classloader to avoid
372      * assuming a vm working directory. The resource path must be
373      * relative to the package name or absolute from the root path.
374      * @param resource the resource to retrieve its url.
375      * @throws AssertionFailureException if resource is not found.
376      */
377     protected URL getResource(String resource){
378         URL url = getClass().getResource(resource);
379         assertNotNull("Could not find resource :" + resource, url);
380         return url;
381     }
382 
383     /***
384      * an output stream which saves stuff to our buffer.
385      */
386     private static class AntOutputStream extends java.io.OutputStream {
387         private StringBuffer buffer;
388 
389         public AntOutputStream( StringBuffer buffer ) {
390             this.buffer = buffer;
391         }
392 
393         public void write(int b) {
394             buffer.append((char)b);
395         }
396     }
397 
398     /***
399      * our own personal build listener
400      */
401     private class AntTestListener implements BuildListener {
402         private int logLevel;
403 
404         /***
405          * Constructs a test listener which will ignore log events
406          * above the given level
407          */
408         public AntTestListener(int logLevel) {
409             this.logLevel = logLevel;
410         }
411 
412         /***
413          *  Fired before any targets are started.
414          */
415         public void buildStarted(BuildEvent event) {
416         }
417 
418         /***
419          *  Fired after the last target has finished. This event
420          *  will still be thrown if an error occured during the build.
421          *
422          *  @see BuildEvent#getException()
423          */
424         public void buildFinished(BuildEvent event) {
425         }
426 
427         /***
428          *  Fired when a target is started.
429          *
430          *  @see BuildEvent#getTarget()
431          */
432         public void targetStarted(BuildEvent event) {
433             //System.out.println("targetStarted " + event.getTarget().getName());
434         }
435 
436         /***
437          *  Fired when a target has finished. This event will
438          *  still be thrown if an error occured during the build.
439          *
440          *  @see BuildEvent#getException()
441          */
442         public void targetFinished(BuildEvent event) {
443             //System.out.println("targetFinished " + event.getTarget().getName());
444         }
445 
446         /***
447          *  Fired when a task is started.
448          *
449          *  @see BuildEvent#getTask()
450          */
451         public void taskStarted(BuildEvent event) {
452             //System.out.println("taskStarted " + event.getTask().getTaskName());
453         }
454 
455         /***
456          *  Fired when a task has finished. This event will still
457          *  be throw if an error occured during the build.
458          *
459          *  @see BuildEvent#getException()
460          */
461         public void taskFinished(BuildEvent event) {
462             //System.out.println("taskFinished " + event.getTask().getTaskName());
463         }
464 
465         /***
466          *  Fired whenever a message is logged.
467          *
468          *  @see BuildEvent#getMessage()
469          *  @see BuildEvent#getPriority()
470          */
471         public void messageLogged(BuildEvent event) {
472             if (event.getPriority() > logLevel) {
473                 // ignore event
474                 return;
475             }
476 
477             if (event.getPriority() == Project.MSG_INFO ||
478                 event.getPriority() == Project.MSG_WARN ||
479                 event.getPriority() == Project.MSG_ERR) {
480                 logBuffer.append(event.getMessage());
481             }
482             fullLogBuffer.append(event.getMessage());
483 
484         }
485     }
486 
487 
488 }