001package org.andromda.maven.plugin.cartridge;
002
003import java.io.File;
004import java.util.List;
005import junit.framework.TestCase;
006import org.apache.commons.io.FileUtils;
007import org.apache.commons.lang.StringUtils;
008
009/**
010 * Compares two files. It checks if both file do exist and if the contents of
011 * both files are equal.
012 *
013 * @author Chad Brandon
014 * @author Bob Fields
015 */
016public class FileComparator
017        extends TestCase
018{
019    private File expectedFile;
020    private File actualFile;
021    private boolean binary = false;
022    private boolean ignoreLineEndings = true;
023    private boolean ignoreWhitespace = false;
024    private List<String> ignoreLinesWithStrings = null;
025
026    /**
027     * Constructs a new instance of the FileComparator.
028     *
029     * @param testName     the name of the test to run
030     * @param expectedFile the location of the expected file
031     * @param actualFile   the location of the actual file.
032     * @param binary       whether or not the file is binary, if it is binary contents
033     *                     of the binary are not compared as Strings but as binary files.
034     */
035    public FileComparator(
036            String testName,
037            File expectedFile,
038            File actualFile,
039            boolean binary)
040    {
041        super();
042        this.setName(testName);
043        this.expectedFile = expectedFile;
044        this.actualFile = actualFile;
045        this.binary = binary;
046    }
047
048    /**
049     * Constructs a new instance of the FileComparator.
050     *
051     * @param testName the name of the test to run
052     * @param expectedFile the location of the expected file
053     * @param actualFile the location of the actual file.
054     * @param binary whether or not the file is binary, if it is binary contents
055     *        of the binary are not compared as Strings but as binary files.
056     * @param ignoreWhitespace Ignore whitespace in the comparison
057     * @param ignoreLineEndings Ignore Unix/Windows line ending differences \r vs \r\n
058     * @param ignoreLinesWithStrings Ignore lines containing the strings in the comparison
059     */
060    public FileComparator(
061        String testName,
062        File expectedFile,
063        File actualFile,
064        boolean binary,
065        boolean ignoreWhitespace,
066        boolean ignoreLineEndings,
067        List<String> ignoreLinesWithStrings)
068    {
069        this(testName, expectedFile, actualFile, binary);
070        this.ignoreWhitespace = ignoreWhitespace;
071        this.ignoreLineEndings = ignoreLineEndings;
072        this.ignoreLinesWithStrings = ignoreLinesWithStrings;
073    }
074
075    /**
076     * Test if actual and expected file contents are equal.
077     */
078    public void testEquals()
079    {
080        assertTrue(
081                "expected file <" + expectedFile.getPath() + "> doesn't exist",
082                expectedFile.exists());
083        assertTrue(
084                "actual file <" + actualFile.getPath() + "> doesn't exist",
085                actualFile.exists());
086        this.testContentsEqual();
087    }
088
089    /**
090     * Loads both the <code>actual</code> and <code>expected</code> files
091     * and tests the contents for equality.
092     */
093    protected void testContentsEqual()
094    {
095        try
096        {
097            String message = "actual file <" + actualFile + "> does not match\n";
098            if (this.binary)
099            {
100                assertTrue(
101                        message,
102                        FileUtils.contentEquals(
103                                expectedFile,
104                                actualFile));
105            }
106            else
107            {
108                String actualContents = FileUtils.readFileToString(actualFile);
109                String expectedContents = FileUtils.readFileToString(expectedFile);
110                //Ignore 'generated on ' date comments different between expected/actual
111                // Sometimes it says 'Autogenerated on 04/22/2010 14:49:09-0400 by AndroMDA', sometimes it says 'Generated by ' XX cartridge
112                // Remove the rest of the line from the comparison.
113                if (this.ignoreLinesWithStrings != null && !this.ignoreLinesWithStrings.isEmpty())
114                {
115                    expectedContents = removeLinesWithStrings(expectedContents, ignoreLinesWithStrings);
116                    actualContents = removeLinesWithStrings(actualContents, ignoreLinesWithStrings);
117                }
118                if (this.ignoreWhitespace)
119                {
120                    expectedContents = StringUtils.remove(expectedContents, ' ');
121                    expectedContents = StringUtils.remove(expectedContents, '\t');
122                    actualContents = StringUtils.remove(actualContents, ' ');
123                    actualContents = StringUtils.remove(actualContents, '\t');
124                }
125                if (this.ignoreLineEndings)
126                {
127                    expectedContents = StringUtils.remove(expectedContents, '\r');
128                    actualContents = StringUtils.remove(actualContents, '\r');
129                }
130                // TODO Tell me the line number where the difference is located.
131                assertEquals(
132                        message,
133                        expectedContents.trim(),
134                        actualContents.trim());
135            }
136        }
137        catch (final Throwable throwable)
138        {
139            fail(throwable.toString());
140        }
141    }
142
143    /**
144     * @param input
145     * @param patternList
146     * @return String input with the lines containing the string removed
147     */
148    public String removeLinesWithStrings(String input, List<String> patternList)
149    {
150        String tempString = input;
151        for (String pattern : patternList)
152        {
153            int patternIndex = tempString.indexOf(pattern);
154            while (patternIndex > 0)
155            {
156                String temp = tempString.substring(patternIndex, tempString.length());
157                temp = temp.substring(temp.indexOf('\n'));
158                tempString = tempString.substring(0, patternIndex) + temp;
159                patternIndex = tempString.indexOf(pattern);
160            }
161        }
162        return tempString;
163    }
164
165    /**
166     * Gets the actual file being compared.
167     *
168     * @return the file being compared.
169     */
170    public File getActualFile()
171    {
172        return this.actualFile;
173    }
174
175    /**
176     * Gets the file expected file (i.e. the file that
177     * the actual file is compared against).
178     *
179     * @return the expected file.
180     */
181    public File getExpectedFile()
182    {
183        return this.expectedFile;
184    }
185}