1   package org.xrn.ant;
2   
3   import java.io.BufferedReader;
4   import java.io.File;
5   import java.io.FileNotFoundException;
6   import java.io.FileOutputStream;
7   import java.io.IOException;
8   import java.io.StringReader;
9   import java.util.HashMap;
10  import java.util.HashSet;
11  import java.util.Map;
12  import java.util.Set;
13  
14  import junit.framework.AssertionFailedError;
15  import junit.framework.TestCase;
16  
17  import org.apache.tools.ant.BuildEvent;
18  import org.apache.tools.ant.BuildException;
19  import org.apache.tools.ant.BuildListener;
20  import org.apache.tools.ant.Project;
21  import org.apache.tools.ant.ProjectHelper;
22  import org.apache.tools.ant.Target;
23  
24  /***
25   * An AntTestCase is a TestCase specialization for unit testing Ant tasks.
26   */
27  public abstract class AntTestCase extends TestCase implements BuildListener
28  {
29      // Instance Variables ------------------------------------------------------
30  
31      /***
32       * The Ant project.
33       */
34      private Project _project;
35  
36      /***
37       * The name of the test build file.
38       */
39      private String _buildFile;
40  
41      /***
42       * Buffer containing all messages logged by Ant. Keys correspond to the 
43       * message priority as <code>java.lang.Integer</code>, the values are are
44       * <code>java.lang.StringBuffer</code>s containing the actual log messages.
45       */
46      private Map _log = new HashMap();
47  
48      /***
49       * File where to log Ant outputs for debugging. If no file location has 
50       * been passed, there will be no file logging.
51       */
52      private FileOutputStream _outputStream;
53      
54      /***
55       * The targets the have been executed.
56       */
57      private Set _executedTargets = new HashSet();
58  
59      // Constructors ------------------------------------------------------------
60  
61      /***
62       * @param theBuildFile The Ant build file corresponding to the test fixture
63       */
64      public AntTestCase(String theBuildFile)
65      {
66          _buildFile = theBuildFile;
67          
68          String outputString = System.getProperty("logfile");
69          if (outputString != null)
70          {
71              try
72              {
73                  _outputStream = new FileOutputStream(outputString);
74              }
75              catch (FileNotFoundException e)
76              {
77                  // Silently ignore error when creating output stream
78              }
79          }
80      }
81  
82      // BuildListener Implementation --------------------------------------------
83  
84      /***
85       * @see BuildListener#buildStarted
86       */
87      public final void buildStarted(BuildEvent theEvent)
88      {
89      }
90  
91      /***
92       * @see BuildListener#buildFinished
93       */
94      public final void buildFinished(BuildEvent theEvent)
95      {
96      }
97  
98      /***
99       * @see BuildListener#targetStarted
100      */
101     public final void targetStarted(BuildEvent theEvent)
102     {
103     }
104 
105     /***
106      * @see BuildListener#targetFinished
107      */
108     public final void targetFinished(BuildEvent theEvent)
109     {
110         _executedTargets.add(theEvent.getTarget().getName());
111     }
112 
113     /***
114      * @see BuildListener#taskStarted
115      */
116     public final void taskStarted(BuildEvent theEvent)
117     {
118     }
119 
120     /***
121      * @see BuildListener#taskFinished
122      */
123     public final void taskFinished(BuildEvent theEvent)
124     {
125     }
126 
127     /***
128      * @see BuildListener#messageLogged
129      */
130     public final void messageLogged(BuildEvent theEvent)
131     {
132         StringBuffer buffer = (StringBuffer)
133             _log.get(new Integer(theEvent.getPriority()));
134         if (buffer == null)
135         {
136             buffer = new StringBuffer();
137             _log.put(new Integer(theEvent.getPriority()), buffer);
138         }
139         buffer.append(theEvent.getMessage()).append("\n");
140         if (_outputStream != null)
141         {
142             try
143             {
144                 _outputStream.write(theEvent.getMessage().getBytes());
145                 _outputStream.write('\n');
146             }
147             catch (IOException e)
148             {
149                 // Silently ignore log error
150             }
151         }
152     }
153 
154     // TestCase Implementation -------------------------------------------------
155 
156     /***
157      * Initializes a fresh Ant project with a target named after the name of the
158      * test case.
159      * 
160      * @see junit.framework.TestCase#setUp()
161      */
162     protected void setUp() throws Exception
163     {
164         _project = new Project();
165         _project.addBuildListener(this);
166         _project.init();
167         File buildFile = getBuildFile(_buildFile);
168         _project.setUserProperty("ant.file", buildFile.getAbsolutePath());
169         ProjectHelper helper = ProjectHelper.getProjectHelper();
170         helper.parse(_project, buildFile);
171         if (getProject().getTargets().get("setUp") != null)
172         {
173             getProject().executeTarget("setUp");
174         }
175     }
176 
177     /***
178      * @see junit.framework.TestCase#tearDown()
179      */
180     protected void tearDown() throws Exception
181     {
182         if (getProject().getTargets().get("tearDown") != null)
183         {
184             try
185             {
186                 getProject().executeTarget("tearDown");
187             }
188             catch (BuildException be)
189             {
190                 // exception has been logged
191             }
192         }
193     }
194 
195     // Protected Methods -------------------------------------------------------
196 
197     /***
198      * @return the buffer containing all Ant logs for the specified
199      *         log level
200      * @param theLogLevel The log level of the message
201      */
202     protected String getLog(int theLogLevel)
203     {
204         String result = null;
205         StringBuffer buffer = (StringBuffer) _log.get(
206             new Integer(theLogLevel));
207         if (buffer != null)
208         {
209             result = buffer.toString();
210         }
211         return result;
212     }
213     
214     /***
215      * Asserts that a specific message has been logged at a specific log level.
216      * 
217      * @param theMessage The message to check for
218      * @param theLogLevel The log level of the message
219      * @throws IOException If an error occurred reading the log buffer
220      */
221     protected final void assertMessageLogged(String theMessage, int theLogLevel)
222         throws IOException
223     {
224         String buffer = getLog(theLogLevel);
225         if (buffer != null)
226         {
227             BufferedReader reader =
228                 new BufferedReader(new StringReader(buffer));
229             String line = null;
230             while ((line = reader.readLine()) != null)
231             {
232                 if (line.equals(theMessage))
233                 {
234                     return;
235                 }
236             }
237         }
238         throw new AssertionFailedError(
239             "Expected log message '" + theMessage + "'");
240     }
241 
242     /***
243      * Asserts that a message containing the specified substring has been logged
244      * at a specific log level.
245      * 
246      * @param theSubstring The substring to check for
247      * @param theLogLevel The log level of the message
248      * @throws IOException If an error occurred reading the log buffer
249      */
250     protected final void assertMessageLoggedContaining(String theSubstring,
251         int theLogLevel)
252         throws IOException
253     {
254         StringBuffer buffer = (StringBuffer) _log.get(new Integer(theLogLevel));
255         if (buffer != null)
256         {
257             BufferedReader reader =
258                 new BufferedReader(new StringReader(buffer.toString()));
259             String line = null;
260             while ((line = reader.readLine()) != null)
261             {
262                 if (line.indexOf(theSubstring) >= 0)
263                 {
264                     return;
265                 }
266             }
267         }
268         throw new AssertionFailedError(
269             "Expected log message containing '" + theSubstring + "'");
270     }
271 
272     /***
273      * Asserts that a named target has been executed.
274      * 
275      * @param theName The name of the target
276      */
277     protected final void assertTargetExecuted(String theName)
278     {
279         assertTrue("Target '" + theName + "' should have been executed",
280             _executedTargets.contains(theName));
281     }
282 
283     /***
284      * Executes the target in the project that corresponds to the current test
285      * case.
286      */
287     protected final void executeTestTarget()
288     {
289         System.out.println("execute Test Target : " + getName());
290         _project.executeTarget(getName());
291     }
292 
293     /***
294      * Returns the Ant project.
295      * 
296      * @return The project
297      */
298     protected final Project getProject()
299     {
300         return _project;
301     }
302 
303     /***
304      * Returns the base directory of the Ant project.
305      * 
306      * @return The base directory
307      */
308     protected final File getProjectDir()
309     {
310         return _project.getBaseDir();
311     }
312 
313     /***
314      * Returns the target in the project that corresponds to the current test
315      * case.
316      * 
317      * @return The test target
318      */
319     protected final Target getTestTarget()
320     {
321         return (Target) getProject().getTargets().get(getName());
322     }
323 
324     // Private Methods ---------------------------------------------------------
325 
326     /***
327      * Returns a file from the test inputs directory, which is determined by the
328      * system property <code>testinput.dir</code>.
329      * 
330      * @param theFileName The name of the file relative to the test input
331      *        directory 
332      * @return The file from the test input directory
333      */
334     private File getBuildFile(String theFileName)
335     {
336         String testInputDirProperty = System.getProperty("testinput.dir");
337         assertTrue("The system property 'testinput.dir' must be set",
338             testInputDirProperty != null);
339         File testInputDir = new File(testInputDirProperty);
340         assertTrue("The system property 'testinput.dir' must point to an "
341             + "existing directory", testInputDir.isDirectory());
342         File buildFile = new File(testInputDir, theFileName);
343         assertTrue("The test input " + theFileName + " does not exist",
344             buildFile.exists());
345         return buildFile;
346     }
347 
348 }