001package org.andromda.maven.plugin.cartridge;
002
003import java.io.File;
004import java.io.IOException;
005import java.io.PrintWriter;
006import java.io.StringWriter;
007import java.util.ArrayList;
008import java.util.Collection;
009import junit.framework.AssertionFailedError;
010import junit.framework.Test;
011import junit.framework.TestListener;
012import org.apache.commons.io.FileUtils;
013
014/**
015 * Formats the cartridge test results into the correct format.
016 *
017 * @author Chad Brandon
018 * @author Bob Fields
019 */
020public class CartridgeTestFormatter
021    implements TestListener
022{
023    /**
024     * Stores the report file location.
025     */
026    private File reportFile;
027
028    /**
029     * Stores the contents of the report.
030     */
031    private StringWriter report;
032
033    /**
034     * The print writer for the report.
035     */
036    private PrintWriter reportWriter;
037
038    /**
039     * Ignore test failures, just show failure messages
040     */
041    private boolean testFailureIgnore = false;
042
043    /**
044     *
045     */
046    public CartridgeTestFormatter()
047    {
048        this.report = new StringWriter();
049        this.reportWriter = new PrintWriter(this.report);
050    }
051
052    /**
053     * Sets the file that will contain the report results (i.e.
054     * the results of the cartridge test run).
055     *
056     * @param reportFile the file to which the report output will be written.
057     */
058    public void setReportFile(final File reportFile)
059    {
060        this.reportFile = reportFile;
061    }
062
063    /**
064     * Keeps track of the number of errors.
065     */
066    private int numberOfErrors = 0;
067
068    /**
069     * @see junit.framework.TestListener#addError(junit.framework.Test, Throwable)
070     */
071    public void addError(
072        Test test,
073        Throwable throwable)
074    {
075        this.numberOfErrors++;
076        this.collectFailure(
077            test,
078            throwable);
079    }
080
081    /**
082     * Keeps track of the number of failures.
083     */
084    private int numberOfFailures = 0;
085
086    /**
087     * @see junit.framework.TestListener#addFailure(junit.framework.Test, junit.framework.AssertionFailedError)
088     */
089    public void addFailure(
090        Test test,
091        AssertionFailedError failure)
092    {
093        this.numberOfFailures++;
094        this.collectFailure(
095            test,
096            failure);
097    }
098
099    /**
100     * @see junit.framework.TestListener#endTest(junit.framework.Test)
101     */
102    public void endTest(Test test)
103    {
104    }
105
106    /**
107     * Keeps track of the number of tests run.
108     */
109    private int numberOfTests = 0;
110
111    /**
112     * @see junit.framework.TestListener#startTest(junit.framework.Test)
113     */
114    public void startTest(Test test)
115    {
116        this.numberOfTests++;
117    }
118
119    /**
120     * Stores the start time of the test suite
121     */
122    private long startTime = 0;
123
124    /**
125     * Stores the new line separator.
126     */
127    private static final String newLine = System.getProperty("line.separator");
128
129    /**
130     * The testsuite started.
131     * @param name
132     */
133    public void startTestSuite(final String name)
134    {
135        startTime = System.currentTimeMillis();
136        this.reportWriter.println("-------------------------------------------------------------------------------");
137        this.reportWriter.println(name + " Test Suite");
138        this.reportWriter.println("-------------------------------------------------------------------------------");
139    }
140
141    /**
142     * Adds a failure to the current <code>failures</code>
143     * collection (these are rendered at the end of test suite
144     * execution).
145     *
146     * @param test the actual test.
147     * @param throwable the failure information.
148     */
149    private void collectFailure(
150        Test test,
151        Throwable throwable)
152    {
153        this.failures.add(new Failure(
154                test,
155                throwable));
156    }
157
158    private Collection<Failure> failures = new ArrayList<Failure>();
159
160    /**
161     * Signifies the test suite ended and returns the summary of the
162     * test.
163     *
164     * @param test the test suite being run.
165     * @return the test summary.
166     */
167    String endTestSuite(Test test)
168    {
169        final double elapsed = ((System.currentTimeMillis() - this.startTime) / 1000.0);
170        final StringBuilder summary = new StringBuilder("Tests: " + String.valueOf(this.numberOfTests) + ", ");
171        summary.append("Failures: ").append(String.valueOf(this.numberOfFailures)).append(", ");
172        summary.append("Errors: ").append(String.valueOf(this.numberOfErrors)).append(", ");
173        summary.append("Time elapsed: ").append(elapsed).append(" sec");
174        summary.append(newLine);
175        this.reportWriter.print(summary);
176        if (this.numberOfFailures > 0)
177        {
178            this.reportWriter.println("Failures: " + this.numberOfFailures);
179            this.reportWriter.println("-------------------------------------------------------------------------------");
180            int ctr = 1;
181            for (final Failure failure : this.failures)
182            {
183                final Throwable information = failure.information;
184                if (information instanceof AssertionFailedError)
185                {
186                    FileComparator comparator = (FileComparator)failure.test;
187                    this.reportWriter.println(ctr + ") " + comparator.getActualFile());
188                }
189            }
190            this.reportWriter.println("-------------------------------------------------------------------------------");
191            this.reportWriter.println();
192        }
193
194        for (final Failure failure : this.failures)
195        {
196            FileComparator comparator = (FileComparator) failure.test;
197            final Throwable information = failure.information;
198            if (information instanceof AssertionFailedError)
199            {
200                this.reportWriter.println("FAILURE: " + comparator.getActualFile());
201                this.reportWriter.println(failure.information.getMessage());
202            }
203            else
204            {
205                this.reportWriter.println("ERROR:");
206                information.printStackTrace(this.reportWriter);
207            }
208            this.reportWriter.println();
209        }
210
211        if (this.reportFile != null)
212        {
213            try
214            {
215                FileUtils.writeStringToFile(this.reportFile, report.toString());
216            }
217            catch (IOException exception)
218            {
219                if (isTestFailureIgnore())
220                {
221                    this.reportWriter.println(exception);
222                }
223                else
224                {
225                    throw new RuntimeException(exception);
226                }
227            }
228        }
229        return summary.toString();
230    }
231
232    /**
233     * Stores the information about a test failure.
234     */
235    private static final class Failure
236    {
237        Test test;
238        Throwable information;
239
240        Failure(
241            final Test test,
242            final Throwable information)
243        {
244            this.test = test;
245            this.information = information;
246        }
247    }
248
249    /**
250     * @return the testFailureIgnore
251     */
252    public boolean isTestFailureIgnore()
253    {
254        return this.testFailureIgnore;
255    }
256
257    /**
258     * @param testFailureIgnore the testFailureIgnore to set
259     */
260    public void setTestFailureIgnore(boolean testFailureIgnore)
261    {
262        this.testFailureIgnore = testFailureIgnore;
263    }
264}