diff --git a/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/PTAMain.java b/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/PTAMain.java index a915581347a5476d9b47a1441cb7610631c45770..1febab1b4e9f77000910b2c08ecaa12005e05ea7 100644 --- a/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/PTAMain.java +++ b/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/PTAMain.java @@ -85,6 +85,7 @@ public class PTAMain { parser.registerOption("glob", "*"); parser.registerOption("since", "last2weeks"); parser.registerOption("console"); + parser.registerOption("verbose"); String[] remainingArgs = parser.parseOptions(args); if (parser.has("help")) { @@ -151,13 +152,20 @@ public class PTAMain { LogManager.getLogManager().reset(); Logger rootLogger = Logger.getLogger(""); ConsoleHandler consoleHandler = new ConsoleHandler(); + + Level defaultLogLevel = Level.INFO; + if (parser.has("verbose")) { + defaultLogLevel = Level.ALL; + } + rootLogger.setLevel(defaultLogLevel); + if (parser.has("console")) { - consoleHandler.setLevel(Level.INFO); + consoleHandler.setLevel(defaultLogLevel); } else { // set log level of console handlers to SEVERE consoleHandler.setLevel(Level.SEVERE); FileHandler fileHandler = new FileHandler(LOG_FILE_NAME); - fileHandler.setLevel(Level.INFO); + fileHandler.setLevel(defaultLogLevel); fileHandler.setFormatter(new SimpleFormatter()); rootLogger.addHandler(fileHandler); } @@ -281,8 +289,12 @@ public class PTAMain { LogFile parseLogFile = lfParser.parseLogFile(); Collection<Problem> problems = parseLogFile.collectProblems(); + pkgTestRun.setSuccess(parseLogFile.isSuccess()); + + // log problems + LOGGER.fine("Overall test result: " + (pkgTestRun.isSuccess() ? "OK" : "FAILED")); for (Problem problem : problems) { - LOGGER.info(problem.toString()); + LOGGER.fine(problem.toString()); } return problems; diff --git a/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/RPackageTestRun.java b/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/RPackageTestRun.java index 72526ca7183a89c95cc8a32dceea96bb40073a66..2bc9a132de822b6a93f650ca25b24d57b0920ade 100644 --- a/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/RPackageTestRun.java +++ b/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/RPackageTestRun.java @@ -31,6 +31,9 @@ public class RPackageTestRun { private final int nr; private Collection<Problem> problems; + /** The overall outcome of the package test run as reported. */ + private boolean success; + protected RPackageTestRun(RPackage pkg, int nr) { this.pkg = pkg; this.nr = nr; @@ -60,4 +63,46 @@ public class RPackageTestRun { return pkg + "/" + nr; } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + nr; + result = prime * result + ((pkg == null) ? 0 : pkg.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + RPackageTestRun other = (RPackageTestRun) obj; + if (nr != other.nr) { + return false; + } + if (pkg == null) { + if (other.pkg != null) { + return false; + } + } else if (!pkg.equals(other.pkg)) { + return false; + } + return true; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public boolean isSuccess() { + return success; + } + } diff --git a/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/dump/AbstractDumper.java b/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/dump/AbstractDumper.java index 76cae7fb6eae58bb6f60218259b03d5e650470ce..b4912482bea6939ca516250791a1f1e0acfd9f78 100644 --- a/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/dump/AbstractDumper.java +++ b/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/dump/AbstractDumper.java @@ -29,6 +29,7 @@ import java.util.stream.Collectors; import com.oracle.truffle.r.test.packages.analyzer.Problem; import com.oracle.truffle.r.test.packages.analyzer.RPackage; +import com.oracle.truffle.r.test.packages.analyzer.RPackageTestRun; public abstract class AbstractDumper { @@ -42,4 +43,8 @@ public abstract class AbstractDumper { return problems.stream().collect(Collectors.groupingBy(p -> p.getPackageTestRun().getPackage())); } + protected Map<RPackageTestRun, List<Problem>> groupByTestRuns(Collection<Problem> problems) { + return problems.stream().collect(Collectors.groupingBy(p -> p.getPackageTestRun())); + } + } diff --git a/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/dump/HtmlDumper.java b/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/dump/HtmlDumper.java index 7763747e920f7f4074cd691ff0143d09c0096564..62af7b87822dea3717c04213ac07a190a555c081 100644 --- a/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/dump/HtmlDumper.java +++ b/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/dump/HtmlDumper.java @@ -38,6 +38,7 @@ import java.util.stream.Collectors; import com.oracle.truffle.r.test.packages.analyzer.Location; import com.oracle.truffle.r.test.packages.analyzer.Problem; import com.oracle.truffle.r.test.packages.analyzer.RPackage; +import com.oracle.truffle.r.test.packages.analyzer.RPackageTestRun; import com.oracle.truffle.r.test.packages.analyzer.dump.html.HTMLBuilder; import com.oracle.truffle.r.test.packages.analyzer.dump.html.HTMLBuilder.Tag; @@ -77,8 +78,8 @@ public class HtmlDumper extends AbstractDumper { try (BufferedWriter bw = Files.newBufferedWriter(indexFile, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE)) { HTMLBuilder builder = new HTMLBuilder(new PrintWriter(bw)); - Tag errorDistributionTable = generateDistributionTable(builder, groupByType(problems)); - Tag pkgDistributionTable = generatePkgDistributionTable(builder, groupByPkg(problems)); + Tag errorDistributionTable = generateTypeDistributionTable(builder, groupByType(problems)); + Tag pkgDistributionTable = generateTestRunDistributionTable(builder, groupByTestRuns(problems)); builder.html(builder.head(builder.title(TITLE)), builder.body(builder.h1(TITLE), builder.h2("Distribution by Problem Type"), errorDistributionTable, builder.h2("Distribution by Package"), pkgDistributionTable)); @@ -88,19 +89,32 @@ public class HtmlDumper extends AbstractDumper { } } - private Tag generatePkgDistributionTable(HTMLBuilder builder, Map<RPackage, List<Problem>> groupByPkg) { - List<RPackage> collect = groupByPkg.keySet().stream().sorted((a, b) -> Integer.compare(groupByPkg.get(b).size(), groupByPkg.get(a).size())).collect(Collectors.toList()); - - Tag table = builder.table(); - for (RPackage pkg : collect) { - String pkgFileName = dumpRPackage(pkg, groupByPkg); - table.addChild(builder.tr(builder.td(builder.a(pkgFileName, pkg.toString())), - builder.td(Integer.toString(groupByPkg.get(pkg).size())))); + private Tag generateTestRunDistributionTable(HTMLBuilder builder, Map<RPackageTestRun, List<Problem>> groupByPkg) { + List<RPackageTestRun> collect = groupByPkg.keySet().stream().sorted((a, b) -> Integer.compare(groupByPkg.get(b).size(), groupByPkg.get(a).size())).collect(Collectors.toList()); + + Tag table = builder.table(builder.tr( + builder.th("Package"), + builder.th("Test Run"), + builder.th("Result"), + builder.th("Problem Count"))); + + for (RPackageTestRun testRun : collect) { + String pkgFileName = dumpRPackage(testRun, groupByPkg); + int n = groupByPkg.get(testRun).size(); + Tag tableRow = builder.tr( + builder.td(builder.a(pkgFileName, testRun.getPackage().toString())), + builder.td(Integer.toString(testRun.getNr())), + builder.td(testRun.isSuccess() ? "OK" : "FAILED"), + builder.td(Integer.toString(n))); + if (testRun.isSuccess() && n > 0) { + tableRow.addAttribute("bgcolor", "#fdae61"); + } + table.addChild(tableRow); } return table; } - private Tag generateDistributionTable(HTMLBuilder builder, Map<Class<? extends Problem>, List<Problem>> groupByType) { + private Tag generateTypeDistributionTable(HTMLBuilder builder, Map<Class<? extends Problem>, List<Problem>> groupByType) { List<Class<? extends Problem>> collect = groupByType.keySet().stream().sorted((a, b) -> Integer.compare(groupByType.get(b).size(), groupByType.get(a).size())).collect(Collectors.toList()); Tag table = builder.table(); @@ -111,9 +125,9 @@ public class HtmlDumper extends AbstractDumper { return table; } - private String dumpRPackage(RPackage pkg, Map<RPackage, List<Problem>> groupByPkg) { + private String dumpRPackage(RPackageTestRun pkg, Map<RPackageTestRun, List<Problem>> groupByPkg) { - Path problemClassFile = destDir.resolve(pkg.toString() + ".html"); + Path problemClassFile = destDir.resolve(pkg.getPackage().toString() + "_" + pkg.getNr() + ".html"); try (BufferedWriter bw = Files.newBufferedWriter(problemClassFile, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE)) { HTMLBuilder builder = new HTMLBuilder(new PrintWriter(bw)); diff --git a/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/dump/html/HTMLBuilder.java b/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/dump/html/HTMLBuilder.java index 2dc24da79960e589373937e7e8c7daec97104f19..b870ecfd9eec8f47984118f3f4344d4222a73c70 100644 --- a/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/dump/html/HTMLBuilder.java +++ b/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/dump/html/HTMLBuilder.java @@ -74,6 +74,14 @@ public class HTMLBuilder { return generic("tr", children); } + public Tag th(String content) { + return generic("th", content); + } + + public Tag th(Tag... children) { + return generic("th", children); + } + public Tag td(String content) { return generic("td", content); } diff --git a/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/parser/LogFileParser.java b/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/parser/LogFileParser.java index e3d6a9713002cf363e50e0181315e54e3dbfca69..d828fc464bf82ad7111e0e9ca31fe20e89a04e36 100644 --- a/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/parser/LogFileParser.java +++ b/com.oracle.truffle.r.test.packages.analyzer/src/com/oracle/truffle/r/test/packages/analyzer/parser/LogFileParser.java @@ -33,6 +33,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.logging.Logger; import java.util.stream.Collectors; import com.oracle.truffle.r.test.packages.analyzer.Location; @@ -44,6 +45,8 @@ import com.oracle.truffle.r.test.packages.analyzer.parser.DiffParser.DiffChunk; public class LogFileParser { + private static final Logger LOGGER = Logger.getLogger(LogFileParser.class.getName()); + private RPackageTestRun pkg; private BufferedReader reader; private Line curLine; @@ -83,12 +86,12 @@ public class LogFileParser { this.reader = r; consumeLine(); Section installTest = parseInstallTest(); - if (!installTest.success()) { + if (!installTest.isSuccess()) { return logFile; } - logFile.sections.add(parseInstallTest()); - logFile.sections.add(parseCheckResults()); - logFile.success = parseOverallStatus(); + logFile.addSection(parseInstallTest()); + logFile.addSection(parseCheckResults()); + logFile.setSuccess(parseOverallStatus()); expectEOF(); } finally { this.reader = null; @@ -118,6 +121,7 @@ public class LogFileParser { consumeLine(); Section checkResults = new Section(logFile, Token.BEGIN_CHECKING.linePrefix, curLine.lineNr); + checkResults.problems = new LinkedList<>(); // TODO depending on the result, parse other files if (curLine.text.contains(Token.FAIL_OUTPUT_GNUR.linePrefix)) { @@ -133,7 +137,20 @@ public class LogFileParser { // format: <pkg name>: FastR output mismatch: <out file name> int idx = curLine.text.indexOf(Token.OUTPUT_MISMATCH_FASTR.linePrefix); String fileNameStr = curLine.text.substring(idx + Token.OUTPUT_MISMATCH_FASTR.linePrefix.length()).trim(); - checkResults.problems = parseOutputFile(logFile.path.resolveSibling(fileNameStr)); + Path outputFile = logFile.path.resolveSibling(fileNameStr); + + // report the problem + checkResults.problems.add(new OutputMismatchProblem(pkg, getCurrentLocation(), fileNameStr)); + + if (!Files.isReadable(outputFile)) { + LOGGER.warning("Cannot read output file " + outputFile); + + // consume any lines to be able to continue + collectBody(Token.END_CHECKING); + } else { + checkResults.problems.addAll(applyDetectors(Token.OUTPUT_MISMATCH_FASTR, outputFile, 0, Files.readAllLines(outputFile))); + } + checkResults.setSuccess(false); } else { throw new LogFileParseException("Unexpected checking message: " + curLine.text); } @@ -142,12 +159,8 @@ public class LogFileParser { return checkResults; } - private Collection<Problem> parseOutputFile(Path path) throws IOException { - if (!Files.isReadable(path)) { - throw new LogFileParseException("Cannot read output file " + path); - } - - return applyDetectors(Token.OUTPUT_MISMATCH_FASTR, path, 0, Files.readAllLines(path)); + private Location getCurrentLocation() { + return new Location(logFile.path, curLine.lineNr); } private Section parseInstallTest() throws IOException { @@ -161,13 +174,13 @@ public class LogFileParser { } Section installationTask = parseInstallationTask(); - installTest.subsections.add(installationTask); + installTest.addSection(installationTask); if ("FastR".equals(mode)) { - installTest.success = parseInstallStatus() && success; + installTest.setSuccess(parseInstallStatus() && success); } parseInstallSuggests(); - installTest.success = parseTesting() && success; + installTest.setSuccess(parseTesting() && success); expect(Token.END_INSTALL_TEST); return installTest; @@ -254,7 +267,7 @@ public class LogFileParser { expect(Token.BEGIN_INSTALLATION); Section installation = new Section(logFile, Token.BEGIN_INSTALLATION.linePrefix, curLine.lineNr); Section processing = parseProcessingTask(); - installation.subsections.add(processing); + installation.addSection(processing); expect(Token.END_INSTALLATION); return installation; } @@ -511,22 +524,44 @@ public class LogFileParser { } - public static class LogFile { + public abstract static class AbstractSection { + private List<Section> sections = new LinkedList<>(); + private boolean success; + + public List<Section> getSections() { + return sections; + } + + public void addSection(Section sub) { + sections.add(sub); + } + + public abstract Collection<Problem> collectProblems(); + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + } + + public static class LogFile extends AbstractSection { public LogFile(Path path) { this.path = path; } private Path path; - private List<Section> sections = new LinkedList<>(); - private boolean success; @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("LogFile ").append(path); - if (!sections.isEmpty()) { + if (!getSections().isEmpty()) { sb.append("("); - for (Section section : sections) { + for (Section section : getSections()) { sb.append(section).append(", "); } sb.append(")"); @@ -534,44 +569,31 @@ public class LogFileParser { return sb.toString(); } - public Path getPath() { - return path; - } - - public List<Section> getSections() { - return sections; + @Override + public Collection<Problem> collectProblems() { + return getSections().stream().flatMap(s -> s.collectProblems().stream()).collect(Collectors.toList()); } - public boolean isSuccess() { - return success; + public Path getPath() { + return path; } - public Collection<Problem> collectProblems() { - List<Problem> problems = new LinkedList<>(); - for (Section sec : sections) { - problems.addAll(Section.collectProblems(sec)); - } - return problems; - } } - public static class Section { + public static class Section extends AbstractSection { private String name; - private LogFile logFile; private int startLine; + private AbstractSection parent; Collection<Problem> problems; - Collection<Section> subsections = new LinkedList<>(); - private boolean success; - protected Section(LogFile logFile, String name, int startLine) { - this.logFile = logFile; + protected Section(AbstractSection parent, String name, int startLine) { + this.parent = parent; this.startLine = startLine; this.name = name; - logFile.sections.add(this); } - public boolean success() { - return success; + public AbstractSection getParent() { + return parent; } @Override @@ -579,19 +601,43 @@ public class LogFileParser { return String.format("Section %s (start: %d, problems: %d)", name, startLine, problems != null ? problems.size() : 0); } - public static Collection<Problem> collectProblems(Section s) { - List<Problem> problems = new LinkedList<>(); - if (s.problems != null) { - problems.addAll(s.problems); + @Override + public Collection<Problem> collectProblems() { + Collection<Problem> collected = new ArrayList<>(); + if (problems != null) { + collected.addAll(problems); } - for (Section sec : s.subsections) { - if (sec.problems != null) { - problems.addAll(sec.problems); - } + for (Section child : getSections()) { + collected.addAll(child.collectProblems()); } - return problems; + return collected; } } + public static class OutputMismatchProblem extends Problem { + + private final String details; + + protected OutputMismatchProblem(RPackageTestRun pkg, Location location, String details) { + super(pkg, location); + this.details = details; + } + + @Override + public String getSummary() { + return Token.OUTPUT_MISMATCH_FASTR.linePrefix; + } + + @Override + public String getDetails() { + return details; + } + + @Override + public String toString() { + return getSummary() + details; + } + } + }