001package org.andromda.maven.plugin.cartridge; 002 003import java.io.File; 004import java.util.ArrayList; 005import java.util.Arrays; 006import java.util.Collection; 007import java.util.Collections; 008import java.util.Iterator; 009import java.util.List; 010import junit.framework.Test; 011import junit.framework.TestCase; 012import junit.framework.TestSuite; 013import org.apache.commons.io.FileUtils; 014import org.apache.commons.io.FilenameUtils; 015import org.apache.commons.io.filefilter.TrueFileFilter; 016import org.apache.commons.lang.StringUtils; 017import org.apache.log4j.Logger; 018 019/** 020 * <p> 021 * This is the entry point of the cartridge test suite for AndroMDA. The 022 * test checks for a list of expected files that a file with the same name and 023 * the same package was generated by AndroMDA and that the APIs of the expected 024 * file and the generated file are equal. <code>CartridgeTest</code> acts as 025 * the test director which creates the list of files to be compared. The actual 026 * API comparison is carried out by instances of {@link FileComparator}. 027 * </p> 028 * 029 * @author Ralf Wirdemann 030 * @author Chad Brandon 031 * @author Michail Plushnikov 032 * @author Bob Fields 033 */ 034public class CartridgeTest 035 extends TestCase 036{ 037 private static final Logger logger = Logger.getLogger(CartridgeTest.class); 038 039 /** 040 * Points to the directory where the expected files are stored which will be 041 * compared to the generated ones. 042 */ 043 public static final String EXPECTED_DIRECTORY = "expected.dir"; 044 045 /** 046 * Points to the directory where the generated files are located. 047 */ 048 public static final String ACTUAL_DIRECTORY = "actual.dir"; 049 050 /** 051 * Defines the suffixes of binary files (files that will be not be 052 * compared as strings). 053 */ 054 public static final String BINARY_SUFFIXES = "binary.suffixes"; 055 056 /** 057 * The shared instance of this class. 058 */ 059 private static CartridgeTest instance; 060 061 /** 062 * Retrieves the shared instance of this class. 063 * 064 * @return the shared instance. 065 */ 066 public static final CartridgeTest instance() 067 { 068 if (instance == null) 069 { 070 instance = new CartridgeTest(); 071 } 072 return instance; 073 } 074 075 private CartridgeTest() 076 { 077 super(); 078 } 079 080 /** 081 * Stores a comma separated list of binary suffixes. 082 */ 083 private String binarySuffixes = StringUtils.trimToEmpty(System.getProperty(BINARY_SUFFIXES)); 084 085 /** 086 * Sets the value of the suffixes that indicate a binary file (binary files 087 * are not compared as text). 088 * 089 * @param binarySuffixes a comma separated list of binary suffixes. 090 */ 091 public void setBinarySuffixes(final String binarySuffixes) 092 { 093 this.binarySuffixes = binarySuffixes; 094 } 095 096 /** 097 * Stores the actual directory. 098 */ 099 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}