package fitnesse.junit;
import java.io.Closeable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import fitnesse.reporting.BaseFormatter;
import fitnesse.reporting.FormatterException;
import fitnesse.testsystems.TestPage;
import fitnesse.testsystems.TestSummary;
import util.FileUtil;
/**
* Used to run tests from a JUnit test suite.
*
* @see fitnesse.junit.FitNesseRunner
*/
public class JavaFormatter extends BaseFormatter implements Closeable {
private String mainPageName;
private boolean isSuite = true;
public interface ResultsRepository extends Closeable {
void open(String string) throws IOException;
void write(String content) throws IOException;
}
public static class FileCopier {
public static void copy(String src, File dst) throws IOException {
InputStream in = null;
OutputStream out = null;
try {
in = FileCopier.class.getResourceAsStream(src);
out = new FileOutputStream(dst);
// Transfer bytes from in to out
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
} finally {
FileUtil.close(in);
FileUtil.close(out);
}
}
}
public static class TestResultPage {
private OutputStreamWriter currentWriter;
public TestResultPage(String outputPath, String testName) throws IOException {
File outputFile = new File(outputPath, testName + ".html");
currentWriter = new OutputStreamWriter(new FileOutputStream(outputFile), FileUtil.CHARENCODING);
writeHeaderFor(testName);
}
public void appendResultChunk(String content) throws IOException {
currentWriter.write(content.replace("src=\"/files/images/", "src=\"images/"));
}
private void writeHeaderFor(String testName) throws IOException {
currentWriter.write("<html><head><title>");
currentWriter.write(testName);
currentWriter
.write("</title><meta http-equiv='Content-Type' content='text/html;charset=" + FileUtil.CHARENCODING + "'/>"
+ "<link rel='stylesheet' type='text/css' href='css/fitnesse.css'/>"
+ "<script src='javascript/jquery-1.11.3.min.js' type='text/javascript'></script>"
+ "<script src='javascript/fitnesse.js' type='text/javascript'></script>" + "</head><body><header><h2>");
currentWriter.write(testName);
currentWriter.write("</h2></header><article>");
}
public void finish() throws IOException {
if (currentWriter != null) {
currentWriter.write("</article></body></html>");
currentWriter.close();
}
}
}
public static class FolderResultsRepository implements ResultsRepository {
private String outputPath;
private TestResultPage testResultPage;
public FolderResultsRepository(String outputPath) throws IOException {
this.outputPath = outputPath;
copyAssets();
}
@Override
public void close() throws IOException {
testResultPage.finish();
}
@Override
public void open(String testName) throws IOException {
testResultPage = new TestResultPage(outputPath, testName);
}
@Override
public void write(String content) throws IOException {
testResultPage.appendResultChunk(content);
}
public void addFile(String resource, String relativeFilePath) throws IOException {
File dst = new File(outputPath, relativeFilePath);
dst.getParentFile().mkdirs();
FileCopier.copy(resource, dst);
}
private void copyAssets() throws IOException {
String base = "/fitnesse/resources/";
String cssDir = base + "css/";
addFile(cssDir + "fitnesse.css", "css/fitnesse.css");
addFile(cssDir + "fitnesse_wiki.css", "css/fitnesse_wiki.css");
addFile(cssDir + "fitnesse_pages.css", "css/fitnesse_pages.css");
addFile(cssDir + "fitnesse_straight.css", "css/fitnesse_straight.css");
String javascriptDir = base + "javascript/";
addFile(javascriptDir + "jquery-1.11.3.min.js", "javascript/jquery-1.11.3.min.js");
addFile(javascriptDir + "fitnesse.js", "javascript/fitnesse.js");
String imagesDir = base + "images/";
addFile(imagesDir + "collapsibleOpen.png", "images/collapsibleOpen.png");
addFile(imagesDir + "collapsibleClosed.png", "images/collapsibleClosed.png");
}
}
private TestSummary totalSummary = new TestSummary();
private List<String> visitedTestPages = new ArrayList<>();
private Map<String, TestSummary> testSummaries = new HashMap<>();
@Override
public void testStarted(TestPage test) {
try {
resultsRepository.open(test.getFullPath());
} catch (IOException e) {
throw new FormatterException("Could not open new report file for " + test.getFullPath(), e);
}
}
@Override
public void testComplete(TestPage test, TestSummary testSummary) {
String fullPath = test.getFullPath();
visitedTestPages.add(fullPath);
totalSummary.add(testSummary);
testSummaries.put(fullPath, new TestSummary(testSummary));
FileUtil.close(resultsRepository);
isSuite = isSuite && (!mainPageName.equals(fullPath));
}
TestSummary getTestSummary(String testPath) {
return testSummaries.get(testPath);
}
@Override
public void testOutputChunk(String output) {
try {
resultsRepository.write(output);
} catch (IOException e) {
throw new FormatterException("could not write output chunk", e);
}
}
private ResultsRepository resultsRepository;
public TestSummary getTotalSummary() {
return totalSummary;
}
public void setTotalSummary(TestSummary testSummary) {
totalSummary = testSummary;
}
public void setResultsRepository(ResultsRepository mockResultsRepository) {
this.resultsRepository = mockResultsRepository;
}
/**
* package-private to prevent instantiation apart from getInstance and tests
*/
JavaFormatter(String suiteName) {
this.mainPageName = suiteName;
}
@Override
public void close() throws IOException {
if (isSuite)
writeSummary(mainPageName);
}
public void writeSummary(String suiteName) throws IOException {
resultsRepository.open(suiteName);
resultsRepository.write(new TestResultsSummaryTable(visitedTestPages, testSummaries).toString());
resultsRepository.close();
}
public static class TestResultsSummaryTableRow {
private String testName;
private TestSummary testSummary;
public TestResultsSummaryTableRow(String testName, TestSummary testSummary) {
this.testName = testName;
this.testSummary = testSummary;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("<tr class=\"").append(getCssClass(testSummary)).append("\"><td>").append(
"<a href=\"").append(testName).append(".html\">").append(testName).append("</a>").append(
"</td><td>").append(testSummary.getRight()).append("</td><td>").append(testSummary.getWrong())
.append("</td><td>").append(testSummary.getExceptions()).append("</td></tr>");
return sb.toString();
}
private String getCssClass(TestSummary ts) {
if (ts.getExceptions() > 0)
return "error";
if (ts.getWrong() > 0)
return "fail";
if (ts.getRight() > 0)
return "pass";
return "plain";
}
}
public static class TestResultsSummaryTable {
public static final String SUMMARY_FOOTER = "</table>";
public static final String SUMMARY_HEADER = "<table><tr><td>Name</td><td>Right</td><td>Wrong</td><td>Exceptions</td></tr>";
private List<String> visitedTestPages;
private Map<String, TestSummary> testSummaries;
public TestResultsSummaryTable(List<String> visitedTestPages, Map<String, TestSummary> testSummaries) {
this.visitedTestPages = visitedTestPages;
this.testSummaries = testSummaries;
}
public String summaryRow(String testName, TestSummary testSummary) {
return new TestResultsSummaryTableRow(testName, testSummary).toString();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(SUMMARY_HEADER);
for (String s : visitedTestPages) {
sb.append(summaryRow(s, testSummaries.get(s)));
}
sb.append(SUMMARY_FOOTER);
return sb.toString();
}
}
}