View Javadoc
1   package org.andromda.maven.plugin.cartridge;
2   
3   import java.io.File;
4   import java.util.ArrayList;
5   import java.util.Arrays;
6   import java.util.Collection;
7   import java.util.Collections;
8   import java.util.Iterator;
9   import java.util.List;
10  import junit.framework.Test;
11  import junit.framework.TestCase;
12  import junit.framework.TestSuite;
13  import org.apache.commons.io.FileUtils;
14  import org.apache.commons.io.FilenameUtils;
15  import org.apache.commons.io.filefilter.TrueFileFilter;
16  import org.apache.commons.lang.StringUtils;
17  import org.apache.log4j.Logger;
18  
19  /**
20   * <p>
21   * This is the entry point of the cartridge test suite for AndroMDA. The
22   * test checks for a list of expected files that a file with the same name and
23   * the same package was generated by AndroMDA and that the APIs of the expected
24   * file and the generated file are equal. <code>CartridgeTest</code> acts as
25   * the test director which creates the list of files to be compared. The actual
26   * API comparison is carried out by instances of {@link FileComparator}.
27   * </p>
28   *
29   * @author Ralf Wirdemann
30   * @author Chad Brandon
31   * @author Michail Plushnikov
32   * @author Bob Fields
33   */
34  public class CartridgeTest
35      extends TestCase
36  {
37      private static final Logger logger = Logger.getLogger(CartridgeTest.class);
38  
39      /**
40       * Points to the directory where the expected files are stored which will be
41       * compared to the generated ones.
42       */
43      public static final String EXPECTED_DIRECTORY = "expected.dir";
44  
45      /**
46       * Points to the directory where the generated files are located.
47       */
48      public static final String ACTUAL_DIRECTORY = "actual.dir";
49  
50      /**
51       * Defines the suffixes of binary files (files that will be not be
52       * compared as strings).
53       */
54      public static final String BINARY_SUFFIXES = "binary.suffixes";
55  
56      /**
57       * The shared instance of this class.
58       */
59      private static CartridgeTest instance;
60  
61      /**
62       * Retrieves the shared instance of this class.
63       *
64       * @return the shared instance.
65       */
66      public static final CartridgeTest instance()
67      {
68          if (instance == null)
69          {
70              instance = new CartridgeTest();
71          }
72          return instance;
73      }
74  
75      private CartridgeTest()
76      {
77          super();
78      }
79  
80      /**
81       * Stores a comma separated list of binary suffixes.
82       */
83      private String binarySuffixes = StringUtils.trimToEmpty(System.getProperty(BINARY_SUFFIXES));
84  
85      /**
86       * Sets the value of the suffixes that indicate a binary file (binary files
87       * are not compared as text).
88       *
89       * @param binarySuffixes a comma separated list of binary suffixes.
90       */
91      public void setBinarySuffixes(final String binarySuffixes)
92      {
93          this.binarySuffixes = binarySuffixes;
94      }
95  
96      /**
97       * Stores the actual directory.
98       */
99      private String actualOutputPath = StringUtils.trimToEmpty(System.getProperty(ACTUAL_DIRECTORY));
100 
101     /**
102      * Sets the path to the <em>actual</em> directory (that is the
103      * directory which contains the actual output being tested.
104      *
105      * @param actualOutputPath the path to the actual directory.
106      */
107     public void setActualOutputPath(final String actualOutputPath)
108     {
109         this.actualOutputPath = actualOutputPath;
110     }
111 
112     /**
113      * Stores the path to the excepted directory.
114      */
115     private String expectedOutputPath = StringUtils.trimToEmpty(System.getProperty(EXPECTED_DIRECTORY));
116 
117     /**
118      * Sets the path to the <em>expected</em> directory.  This is the directory
119      * to which the expected output is extracted.
120      *
121      * @param expectedOutputPath the path to the expected output directory.
122      */
123     public void setExpectedOutputPath(final String expectedOutputPath)
124     {
125         this.expectedOutputPath = expectedOutputPath;
126     }
127 
128     /**
129      * @param name
130      */
131     public CartridgeTest(final String name)
132     {
133         super(name);
134     }
135 
136     /**
137      * @return suite
138      */
139     public static Test suite()
140     {
141         TestSuite suite = new TestSuite();
142         instance().addTests(suite);
143         return suite;
144     }
145 
146     /**
147      * Adds tests which compare all actual generated files against the expected
148      * files.
149      *
150      * @param suite the test suite to which we'll add the tests.
151      */
152     private void addTests(final TestSuite suite)
153     {
154         Collection<File> expectedFiles = Collections.emptyList();
155 
156         final File directory = this.getExpectedOutputDirectory();
157         if (null!=directory && directory.exists())
158         {
159             expectedFiles = FileUtils.listFiles(
160                     directory.isDirectory()? directory : directory.getParentFile(),
161                     TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE);
162         }
163 
164         final Iterator<File> iterator = expectedFiles.iterator();
165         logger.info(" --- Expecting " + expectedFiles.size() + " Generated Files --- ");
166         logger.info("ExpectedOutputDirectory --> " + this.getExpectedOutputDirectory());
167         final List<File> missingFiles = new ArrayList<File>();
168         for (int ctr = 1; iterator.hasNext(); ctr++)
169         {
170             final File expectedFile = iterator.next();
171             final File actualFile = getActualFile(expectedFile);
172             if (!actualFile.exists())
173             {
174                 missingFiles.add(actualFile);
175             }
176             final boolean binary = isBinary(actualFile);
177             if (logger.isDebugEnabled())
178             {
179                 logger.debug(ctr + ") binary = " + binary);
180                 logger.debug("expected --> '" + expectedFile + '\'');
181                 logger.debug("actual   --> '" + actualFile + '\'');
182             }
183             //Ignore 'generated on ' date comments different between expected/actual
184             // Sometimes it says 'Autogenerated on 04/22/2010 14:49:09-0400 by AndroMDA', sometimes it says 'Generated by ' XX cartridge
185             // Remove the rest of the line from the comparison.
186             // Ignore line ending differences between Windows and Unix
187             List<String> strings = new ArrayList<String>(2);
188             strings.add("enerated by ");
189             strings.add("enerated on ");
190             suite.addTest(new FileComparator(
191                     "testEquals",
192                     expectedFile,
193                     actualFile,
194                     binary,
195                     false,
196                     true,
197                     strings));
198         }
199         if (!missingFiles.isEmpty())
200         {
201             Collections.sort(missingFiles);
202             StringBuilder failureMessage = new StringBuilder("\n--- The following ");
203             failureMessage.append(missingFiles.size());
204             failureMessage.append(" expected files do not exist ----\n");
205             Iterator<File> missingFileIterator = missingFiles.iterator();
206             for (int ctr = 1; missingFileIterator.hasNext(); ctr++)
207             {
208                 failureMessage.append(ctr).append(") ");
209                 failureMessage.append(missingFileIterator.next());//.append('\n');
210                 if (missingFileIterator.hasNext())
211                 {
212                     failureMessage.append('\n');
213                 }
214             }
215             TestCase.fail(failureMessage.toString());
216         }
217     }
218 
219     /**
220      * Constructs the expected file path from the <code>actualFile</code> and
221      * the <code>expectedDir</code> path.
222      *
223      * @param actualFile the actual generated file
224      * @return the new expected file.
225      */
226     private File getActualFile(final File expectedFile)
227     {
228         String actualFile;
229         final File actualOutputDirectory = this.getActualOutputDirectory();
230         final File expectedOutputDirectory = this.getExpectedOutputDirectory();
231         final String path = expectedFile.getPath();
232         if (expectedFile.getPath().startsWith(actualOutputDirectory.getPath()))
233         {
234             actualFile = path.substring(
235                 actualOutputDirectory.getPath().length(),
236                     path.length());
237             actualFile = expectedOutputDirectory + expectedFile.toString();
238         }
239         else
240         {
241             actualFile = path.substring(
242                 expectedOutputDirectory.getPath().length(),
243                     path.length());
244             actualFile = actualOutputDirectory + actualFile;
245         }
246         return new File(actualFile);
247     }
248 
249     /**
250      * The expected output directory.
251      */
252     private File expectedOutputDirectory;
253 
254     /**
255      * Retrieves the expected output directory.
256      *
257      * @return the file representing the directory.
258      */
259     private File getExpectedOutputDirectory()
260     {
261        if (this.expectedOutputDirectory == null)
262        {
263            this.expectedOutputDirectory = this.getDirectory(this.expectedOutputPath);
264        }
265        return this.expectedOutputDirectory;
266     }
267 
268     /**
269      * Resets the expected output directory.
270      *
271      * @param file the file representing the directory.
272      */
273     protected void setExpectedOutputDirectory(File file)
274     {
275        if (file == null)
276        {
277            this.expectedOutputDirectory = this.getDirectory(this.expectedOutputPath);
278        }
279     }
280 
281     /**
282      * The actual output directory.
283      */
284     private File actualOutputDirectory;
285 
286     private File getActualOutputDirectory()
287     {
288         if (this.actualOutputDirectory == null)
289         {
290             this.actualOutputDirectory = this.getDirectory(this.actualOutputPath);
291         }
292         return this.actualOutputDirectory;
293     }
294 
295     /**
296      * Used when resetting the cartridge test instance, to avoid results overlap
297      * @param file
298      */
299     protected void setActualOutputDirectory(File file)
300     {
301         if (file == null)
302         {
303             this.actualOutputDirectory = this.getDirectory(this.actualOutputPath);
304         }
305     }
306 
307     /**
308      * Gets the directory from the system property key.
309      *
310      * @param path the system property key name.
311      * @return the directory as a File instance.
312      */
313     private File getDirectory(final String path)
314     {
315         File directory = new File(path);
316         if (!directory.exists() || !directory.isDirectory())
317         {
318             throw new RuntimeException("directory <" + path + "> doesn't exist");
319         }
320         return directory;
321     }
322 
323     /**
324      * Checks whether or not the <code>file</code> is a binary file. Does this
325      * by checking to see if the suffix is found in the list of binary suffixes.
326      *
327      * @param file the file to check
328      * @return true/false
329      */
330     private boolean isBinary(final File file)
331     {
332         String suffix = FilenameUtils.getExtension(file.getName());
333         return this.getBinarySuffixes().contains(suffix);
334     }
335 
336     private Collection<String> binarySuffixCollection;
337 
338     /**
339      * Gets the binary suffixes for the <code>binary.suffixes</code> system
340      * property. Returns an empty collection if none are found.
341      *
342      * @return the Collection of binary suffixes. (ie. jpg, jar, zip, etc).
343      */
344     private Collection<String> getBinarySuffixes()
345     {
346         if (this.binarySuffixCollection == null)
347         {
348             final String suffixes = this.binarySuffixes != null ? this.binarySuffixes.trim() : "";
349             final String[] suffixArray = suffixes.split("\\s*,\\s*");
350             this.binarySuffixCollection = Arrays.asList(suffixArray);
351         }
352         return this.binarySuffixCollection;
353     }
354 
355     /**
356      * Releases any resources held by this cartridge test instance.
357      */
358     public void shutdown()
359     {
360         CartridgeTest.instance = null;
361     }
362 }