diff --git a/apache-rat-core/src/it/java/org/apache/rat/ReportTest.java b/apache-rat-core/src/it/java/org/apache/rat/ReportTest.java index b695134a6..027619288 100644 --- a/apache-rat-core/src/it/java/org/apache/rat/ReportTest.java +++ b/apache-rat-core/src/it/java/org/apache/rat/ReportTest.java @@ -142,7 +142,7 @@ public void integrationTest(String testName, Document commandLineDoc) throws Exc try { Object value = shell.run(groovyScript, new String[]{outputFile.getAbsolutePath(), logFile.getAbsolutePath()}); if (value != null) { - fail(String.format("%s", value)); + fail(String.format("%s: %s", testName, value)); } } catch (AssertionError e) { throw new AssertionError(String.format("%s: %s", testName, e.getMessage()), e); diff --git a/apache-rat-core/src/it/resources/ReportTest/RAT_14/verify.groovy b/apache-rat-core/src/it/resources/ReportTest/RAT_14/verify.groovy index 226394df0..c0027ba13 100644 --- a/apache-rat-core/src/it/resources/ReportTest/RAT_14/verify.groovy +++ b/apache-rat-core/src/it/resources/ReportTest/RAT_14/verify.groovy @@ -66,9 +66,9 @@ myArgs[3] = src.getAbsolutePath() ReportConfiguration configuration = OptionCollection.parseCommands(src, myArgs, { opts -> }) assertNotNull(configuration) -configuration.validate(DefaultLog.getInstance().&error) +configuration.validate() Reporter reporter = new Reporter(configuration) -ClaimStatistic statistic = reporter.execute() +ClaimStatistic statistic = reporter.execute().getStatistic() assertEquals(3, statistic.getCounter(ClaimStatistic.Counter.APPROVED)) assertEquals(2, statistic.getCounter(ClaimStatistic.Counter.ARCHIVES)) diff --git a/apache-rat-core/src/main/java/org/apache/rat/Report.java b/apache-rat-core/src/main/java/org/apache/rat/Report.java index 5f5f4cb35..ce019b782 100644 --- a/apache-rat-core/src/main/java/org/apache/rat/Report.java +++ b/apache-rat-core/src/main/java/org/apache/rat/Report.java @@ -19,8 +19,14 @@ package org.apache.rat; import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; import org.apache.commons.cli.Options; +import org.apache.commons.io.function.IOSupplier; +import org.apache.rat.commandline.ArgumentContext; import org.apache.rat.document.RatDocumentAnalysisException; import org.apache.rat.help.Help; import org.apache.rat.utils.DefaultLog; @@ -42,34 +48,66 @@ public final class Report { public static void main(final String[] args) throws Exception { DefaultLog.getInstance().info(new VersionInfo().toString()); + if (args == null || args.length == 0) { DefaultLog.getInstance().info("Please use the \"--help\" option to see a " + "list of valid commands and options, as you did not provide any arguments."); System.exit(0); } - ReportConfiguration configuration = OptionCollection.parseCommands(new File("."), args, Report::printUsage); - if (configuration != null) { - configuration.validate(DefaultLog.getInstance()::error); - Reporter reporter = new Reporter(configuration); - reporter.output(); - reporter.writeSummary(DefaultLog.getInstance().asWriter()); + Reporter.Output result = generateReport(CLIOptionCollection.INSTANCE, new File("."), args); + if (result != null) { + result.writeSummary(DefaultLog.getInstance().asWriter()); - if (configuration.getClaimValidator().hasErrors()) { - configuration.getClaimValidator().logIssues(reporter.getClaimsStatistic()); + if (result.getConfiguration().getClaimValidator().hasErrors()) { + result.getConfiguration().getClaimValidator().logIssues(result.getStatistic()); throw new RatDocumentAnalysisException(format("Issues with %s", String.join(", ", - configuration.getClaimValidator().listIssues(reporter.getClaimsStatistic())))); + result.getConfiguration().getClaimValidator().listIssues(result.getStatistic())))); } } } /** - * Prints the usage message on {@code System.out}. - * @param opts The defined options. + * Prints the usage message on the specified output stream. + * @param out The OutputStream supplier */ - private static void printUsage(final Options opts) { - new Help(System.out).printUsage(opts); + private static void printUsage(final Options options, final IOSupplier out) { + try (OutputStream stream = out.get(); + PrintWriter writer = new PrintWriter(stream, false, StandardCharsets.UTF_8)) { + new Help(writer).printUsage(options); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Generates the report + * @param workingDirectory the directory that we are executing in + * @param args the arguments from the command line. + * @return The Client output. + * @throws Exception on error. + */ + static Reporter.Output generateReport(final CLIOptionCollection optionCollection, final File workingDirectory, final String[] args) throws Exception { + Reporter.Output output = null; + OptionCollectionParser optionParser = new OptionCollectionParser(optionCollection); + ArgumentContext argumentContext = optionParser.parseCommands(workingDirectory, args); + ReportConfiguration configuration = argumentContext.getConfiguration(); + if (configuration != null) { + if (argumentContext.getCommandLine().hasOption(CLIOptionCollection.HELP)) { + printUsage(optionCollection.getOptions(), argumentContext.getConfiguration().getOutput()); + } else if (!configuration.hasSource()) { + String msg = "No directories or files specified for scanning. Did you forget to close a multi-argument option?"; + DefaultLog.getInstance().error(msg); + printUsage(optionCollection.getOptions(), argumentContext.getConfiguration().getOutput()); + } else { + configuration.validate(); + Reporter reporter = new Reporter(configuration); + output = reporter.execute(); + output.format(configuration); + } + } + return output; } private Report() { diff --git a/apache-rat-core/src/main/java/org/apache/rat/ReportConfiguration.java b/apache-rat-core/src/main/java/org/apache/rat/ReportConfiguration.java index 799dfaf53..82ce49204 100644 --- a/apache-rat-core/src/main/java/org/apache/rat/ReportConfiguration.java +++ b/apache-rat-core/src/main/java/org/apache/rat/ReportConfiguration.java @@ -34,18 +34,25 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.SortedSet; -import java.util.function.Consumer; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; import org.apache.commons.io.function.IOSupplier; import org.apache.commons.io.output.CloseShieldOutputStream; +import org.apache.commons.lang3.StringUtils; import org.apache.rat.analysis.IHeaderMatcher; +import org.apache.rat.api.RatException; import org.apache.rat.commandline.StyleSheets; import org.apache.rat.config.AddLicenseHeaders; import org.apache.rat.config.exclusion.ExclusionProcessor; import org.apache.rat.config.exclusion.StandardCollection; import org.apache.rat.config.results.ClaimValidator; +import org.apache.rat.configuration.XMLConfigurationReader; import org.apache.rat.configuration.builders.AnyBuilder; import org.apache.rat.document.DocumentName; import org.apache.rat.document.DocumentNameMatcher; @@ -55,11 +62,17 @@ import org.apache.rat.license.LicenseSetFactory; import org.apache.rat.license.LicenseSetFactory.LicenseFilter; import org.apache.rat.report.IReportable; +import org.apache.rat.report.RatReport; +import org.apache.rat.report.claim.ClaimStatistic; +import org.apache.rat.report.xml.writer.IXmlWriter; +import org.apache.rat.report.xml.writer.XmlWriter; import org.apache.rat.utils.DefaultLog; import org.apache.rat.utils.Log.Level; import org.apache.rat.utils.ReportingSet; import org.apache.rat.walker.FileListWalker; import org.apache.rat.walker.IReportableListWalker; +import org.w3c.dom.Node; +import org.xml.sax.SAXException; /** * A configuration object is used by the front end to invoke the @@ -68,6 +81,8 @@ */ public class ReportConfiguration { + /** The IODescriptor for System.out */ + public static final IODescriptor SYSTEM_OUT = new IODescriptor<>("System.out", () -> CloseShieldOutputStream.wrap(System.out)); /** * The styles of processing for various categories of documents. */ @@ -117,11 +132,11 @@ public String desc() { /** * The IOSupplier that provides the output stream to write the report to. */ - private IOSupplier out; + private IODescriptor out; /** * The IOSupplier that provides the stylesheet to style the XML output. */ - private IOSupplier styleSheet; + private IODescriptor styleSheet; /** * A list of files to read file names from. @@ -176,6 +191,10 @@ public ReportConfiguration() { reportables = new ArrayList<>(); } + public Serde serde() { + return new Serde(); + } + /** * Report the excluded files to the appendable object. * @param appendable the appendable object to write to. @@ -427,7 +446,7 @@ public void addIncludedPatterns(final Iterable patterns) { } /** - * Get the DocumentNameMatcher that excludes files found in the directory tree.. + * Get the DocumentNameMatcher that excludes files found in the directory tree. * @param baseDir the DocumentName for the base directory. * @return the DocumentNameMatcher for the base directory. */ @@ -441,6 +460,15 @@ public DocumentNameMatcher getDocumentExcluder(final DocumentName baseDir) { * the report with. */ public IOSupplier getStyleSheet() { + return styleSheet.ioSupplier(); + } + + /** + * Gets the IODescriptor of the style sheet. + * @return the Supplier of the InputStream that is the XSLT style sheet to style + * the report with. + */ + public IODescriptor getStyleSheetDescriptor() { return styleSheet; } @@ -450,7 +478,7 @@ public IOSupplier getStyleSheet() { * multiple times. * @param styleSheet the XSLT style sheet to style the report with. */ - public void setStyleSheet(final IOSupplier styleSheet) { + public void setStyleSheet(final IODescriptor styleSheet) { this.styleSheet = styleSheet; } @@ -462,7 +490,7 @@ public void setStyleSheet(final IOSupplier styleSheet) { */ public void setFrom(final Defaults defaults) { licenseSetFactory.add(defaults.getLicenseSetFactory()); - if (getStyleSheet() == null) { + if (getStyleSheetDescriptor() == null) { setStyleSheet(StyleSheets.PLAIN.getStyleSheet()); } defaults.getStandardExclusion().forEach(this::addExcludedCollection); @@ -498,19 +526,18 @@ public void setStyleSheet(final URI styleSheet) { */ public void setStyleSheet(final URL styleSheet) { Objects.requireNonNull(styleSheet, "Stylesheet file must not be null"); - setStyleSheet(styleSheet::openStream); + setStyleSheet(new IODescriptor<>(styleSheet.toString(), styleSheet::openStream)); } /** * Sets the supplier for the output stream. The supplier may be called multiple * times to provide the stream. Suppliers should prepare streams that are * appended to and that can be closed. If an {@code OutputStream} should not be - * closed consider wrapping it in a {@code CloseShieldOutputStream} + * closed consider wrapping it in a {@code NoCloseOutputStream} * @param out The OutputStream supplier that provides the output stream to write * the report to. A null value will use System.out. - * @see CloseShieldOutputStream */ - public void setOut(final IOSupplier out) { + public void setOut(final IODescriptor out) { this.out = out; } @@ -518,7 +545,7 @@ public void setOut(final IOSupplier out) { * Sets the OutputStream supplier to use the specified file. The file may be * opened and closed several times. File is deleted first and then may be * repeatedly opened in append mode. - * @see #setOut(IOSupplier) + * @see #setOut(IODescriptor) * @param file The file to create the supplier with. */ public void setOut(final File file) { @@ -534,7 +561,7 @@ public void setOut(final File file) { if (!parent.mkdirs() && !parent.isDirectory()) { DefaultLog.getInstance().warn("Unable to create directory: " + file.getParentFile()); } - setOut(() -> new FileOutputStream(file, true)); + setOut(new IODescriptor<>(file.toString(), () -> new FileOutputStream(file, true))); } /** @@ -543,7 +570,15 @@ public void setOut(final File file) { * @return The supplier of the output stream to write the report to. */ public IOSupplier getOutput() { - return out == null ? () -> CloseShieldOutputStream.wrap(System.out) : out; + return getOutputDescriptor().ioSupplier(); + } + + /** + * Returns the output IODescriptor. + * @return The IODescriptor of the output stream to write the report to. + */ + public IODescriptor getOutputDescriptor() { + return out == null ? SYSTEM_OUT : out; } /** @@ -827,19 +862,145 @@ public LicenseSetFactory getLicenseSetFactory() { /** * Validates that the configuration is valid. - * @param logger String consumer to log warning messages to. * @throws ConfigurationException on configuration error. */ - public void validate(final Consumer logger) { + public void validate() { if (!hasSource()) { String msg = "At least one source must be specified"; - logger.accept(msg); + DefaultLog.getInstance().error(msg); throw new ConfigurationException(msg); } - if (licenseSetFactory.getLicenses(LicenseFilter.ALL).isEmpty()) { - String msg = "You must specify at least one license"; - logger.accept(msg); - throw new ConfigurationException(msg); + licenseSetFactory.validate(); + } + + public class Serde { + /** + * Writes the configuration as an XML document to the appendable. + * + * @param appendable the Appendable to write to. + * @throws IOException on error. + */ + public void serialize(final Appendable appendable) throws IOException { + try (IXmlWriter writer = new XmlWriter(appendable)) { + writer.openElement("ReportConfiguration") + .attribute("addingLicenses", Boolean.toString(addingLicenses)) + .attribute("addingLicensesForced", Boolean.toString(addingLicensesForced)) + .attribute("listFamilies", listFamilies.name()) + .attribute("listLicenses", listLicenses.name()) + .attribute("dryRun", Boolean.toString(dryRun)) + .attribute("archiveProcessing", getArchiveProcessing().name()) + .attribute("standardProcessing", getStandardProcessing().name()) + .attribute("stylesheet", styleSheet.name()) + .attribute("output", out.name()); + if (StringUtils.isNotEmpty(copyrightMessage)) { + writer.openElement("copyrightMessage").content(copyrightMessage).closeElement(); + } + writer.openElement("sources"); + for (File f : sources) { + writer.openElement("source").attribute("name", f.getName()).closeElement(); + } + writer.closeElement("sources").openElement("reportables"); + for (IReportable reportable : reportables) { + writer.openElement("reportable") + .attribute("baseName", reportable.name().getBaseName()) + .attribute("name", reportable.name().toString()) + .attribute("class", reportable.getClass().getName()).closeElement(); + } + writer.closeElement(); + + exclusionProcessor.serde().serialize(writer); + + writer.openElement("claimValidator"); + for (ClaimStatistic.Counter counter : ClaimStatistic.Counter.values()) { + writer.openElement("claimCounter") + .attribute("name", counter.name()).attribute("min", Integer.toString(claimValidator.getMin(counter))) + .attribute("max", Integer.toString(claimValidator.getMax(counter))).closeElement(); + } + writer.closeElement(); + } catch (IOException e) { + throw e; + } catch (Exception e) { + throw new IOException(e); + } + } + + public void deserialize(final IOSupplier inputStreamSupplier, final DocumentName workingDirectory) throws IOException { + DocumentBuilder builder; + try { + builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + } catch (ParserConfigurationException e) { + throw new ConfigurationException("Unable to create DOM builder", e); + } + org.w3c.dom.Document document; + try (InputStream stream = inputStreamSupplier.get()) { + document = builder.parse(stream); + } catch (SAXException e) { + throw new IOException("Unable to read input", e); + } + Node node = document.getDocumentElement(); + if (!node.getNodeName().equals("ReportConfiguration")) { + throw new IOException("Invalid ReportConfiguration"); + } + Map attributes = XMLConfigurationReader.attributes(node); + addingLicensesForced = Boolean.parseBoolean(attributes.get("addingLicensesForced")); + listFamilies = LicenseFilter.valueOf(attributes.get("listFamilies")); + listLicenses = LicenseFilter.valueOf(attributes.get("listLicenses")); + dryRun = Boolean.parseBoolean(attributes.get("dryRun")); + archiveProcessing = Processing.valueOf(attributes.get("archiveProcessing")); + standardProcessing = Processing.valueOf(attributes.get("standardProcessing")); + String styleName = attributes.get("stylesheet"); + if (styleName != null) { + styleSheet = StyleSheets.getStyleSheet(styleName, workingDirectory); + } + String outputName = attributes.get("output"); + if (outputName != null) { + if (outputName.equals(ReportConfiguration.SYSTEM_OUT.name())) { + out = ReportConfiguration.SYSTEM_OUT; + } else { + out = IODescriptor.output(outputName, workingDirectory); + } + } + + XMLConfigurationReader.nodeListConsumer(document.getElementsByTagName("source"), lNode -> { + Map nAttributes = XMLConfigurationReader.attributes(lNode); + addSource(new File(nAttributes.get("name"))); + }); + + XMLConfigurationReader.nodeListConsumer(document.getElementsByTagName("reportable"), lNode -> { + Map nAttributes = XMLConfigurationReader.attributes(lNode); + DocumentName documentName = DocumentName.builder().setBaseName(nAttributes.get("baseName")) + .setName(nAttributes.get("name")).build(); + addSource(new DeserializedReportable(documentName)); + }); + + exclusionProcessor.serde().deserialize(document.getElementsByTagName("ExclusionProcessor").item(0)); + + XMLConfigurationReader.nodeListConsumer(document.getElementsByTagName("claimCounter"), lNode -> { + Map nAttributes = XMLConfigurationReader.attributes(lNode); + ClaimStatistic.Counter counter = ClaimStatistic.Counter.valueOf(nAttributes.get("name")); + claimValidator.setMin(counter, Integer.parseInt(nAttributes.get("min"))); + claimValidator.setMax(counter, Integer.parseInt(nAttributes.get("max"))); + }); + } + } + + private record DeserializedReportable(DocumentName name) implements IReportable { + @Override + public void run(final RatReport report) throws RatException { + throw new RatException("Attempt to run a deserialized reportable"); } } + + public record IODescriptor(String name, IOSupplier ioSupplier) { + /** + * Creates an output IODescriptor for the file name within the working directory. + * @param name the name of the file to open. + * @param workingDirectory the working directory for the file. + * @return the Output IODescriptor. + */ + static IODescriptor output(final String name, final DocumentName workingDirectory) { + DocumentName docName = workingDirectory.resolve(name); + return new IODescriptor(name, () -> new FileOutputStream(docName.asFile())); + } + }; } diff --git a/apache-rat-core/src/main/java/org/apache/rat/Reporter.java b/apache-rat-core/src/main/java/org/apache/rat/Reporter.java index 2e58c5829..3159c926f 100644 --- a/apache-rat-core/src/main/java/org/apache/rat/Reporter.java +++ b/apache-rat-core/src/main/java/org/apache/rat/Reporter.java @@ -18,17 +18,19 @@ */ package org.apache.rat; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; -import java.io.Writer; +import java.io.StringReader; import java.nio.charset.StandardCharsets; +import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; @@ -39,6 +41,7 @@ import org.apache.commons.io.function.IOSupplier; import org.apache.rat.api.RatException; +import org.apache.rat.document.DocumentName; import org.apache.rat.license.LicenseSetFactory.LicenseFilter; import org.apache.rat.report.RatReport; import org.apache.rat.report.claim.ClaimStatistic; @@ -46,6 +49,8 @@ import org.apache.rat.report.xml.writer.IXmlWriter; import org.apache.rat.report.xml.writer.XmlWriter; import org.w3c.dom.Document; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; /** * Class that executes the report as defined in a {@link ReportConfiguration} and stores @@ -56,15 +61,14 @@ public class Reporter { /** Format used for listing licenses. */ private static final String LICENSE_FORMAT = "%s:\t%s%n\t\t%s%n"; - /** The XML output document */ - private Document document; - - /** Statistics generated as the report was built */ - private ClaimStatistic statistic; - /** The configuration for the report */ private final ReportConfiguration configuration; + /** + * The output from the execution. + */ + private Output output; + /** * Create the reporter. * @@ -76,83 +80,39 @@ public Reporter(final ReportConfiguration configuration) { /** * Executes the report and builds the output. - * This method will build the internal XML document if it does not already exist. - * If this method or either of the {@link #output()} methods have already been called this method will return - * the previous results. - * @return the claim statistics. + * @return the Output object. * @throws RatException on error. */ - public ClaimStatistic execute() throws RatException { - if (document == null || statistic == null) { - try { - if (configuration.hasSource()) { - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - Writer outputWriter = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8); - try (IXmlWriter writer = new XmlWriter(outputWriter)) { - statistic = new ClaimStatistic(); - RatReport report = XmlReportFactory.createStandardReport(writer, statistic, configuration); - report.startReport(); - configuration.getSources().build().run(report); - report.endReport(); - } - InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray()); - document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream); - } else { - document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); - statistic = new ClaimStatistic(); + public Output execute() throws RatException { + try { + Output.Builder builder = Output.builder().configuration(configuration); + if (configuration.hasSource()) { + StringBuilder sb = new StringBuilder(); + try (IXmlWriter writer = new XmlWriter(sb)) { + writer.startDocument(); + ClaimStatistic statistic = new ClaimStatistic(); + builder.statistic(statistic); + RatReport report = XmlReportFactory.createStandardReport(writer, statistic, configuration); + report.startReport(); + configuration.getSources().build().run(report); + report.endReport(); + InputSource inputSource = new InputSource(new StringReader(sb.toString())); + builder.document(DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputSource)); } - } catch (Exception e) { - throw RatException.makeRatException(e); } + this.output = builder.build(); + return output; + } catch (Exception e) { + throw RatException.makeRatException(e); } - return statistic; } /** - * Get the claim statistics from the run. - * - * @return the claim statistics. + * Gets the output from the last {@link #execute} call or {@code null} if {@link #execute} has not been called. + * @return the output */ - public ClaimStatistic getClaimsStatistic() { - return statistic; - } - - /** - * Outputs the report using the stylesheet and output specified in the configuration. - * @return the Claim statistic from the run. - * @throws RatException on error. - */ - public ClaimStatistic output() throws RatException { - return output(configuration.getStyleSheet(), configuration.getOutput()); - } - - /** - * Outputs the report to the specified output using the stylesheet. It is safe to call this method more than once - * in order to generate multiple reports from the same run. - * - * @param stylesheet the style sheet to use for XSLT formatting. - * @param output the output stream to write to. - * @return the Claim statistic for the run. - * @throws RatException on error. - */ - public ClaimStatistic output(final IOSupplier stylesheet, final IOSupplier output) throws RatException { - ClaimStatistic result = execute(); - TransformerFactory tf = TransformerFactory.newInstance(); - Transformer transformer; - try (OutputStream out = output.get(); - InputStream styleIn = stylesheet.get()) { - transformer = tf.newTransformer(new StreamSource(styleIn)); - transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); - transformer.setOutputProperty(OutputKeys.METHOD, "xml"); - transformer.setOutputProperty(OutputKeys.INDENT, "yes"); - transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); - transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); - transformer.transform(new DOMSource(document), - new StreamResult(new OutputStreamWriter(out, StandardCharsets.UTF_8))); - return result; - } catch (TransformerException | IOException e) { - throw new RatException(e); - } + public Output getOutput() { + return output; } /** @@ -172,24 +132,172 @@ public static void listLicenses(final ReportConfiguration configuration, final L } /** - * Writes a text summary of issues with the run. - * @param appendable the appendable to write to. - * @throws IOException on error. + * The output from a report run. */ - public void writeSummary(final Appendable appendable) throws IOException { - appendable.append("RAT summary:").append(System.lineSeparator()); - for (ClaimStatistic.Counter counter : ClaimStatistic.Counter.values()) { - appendable.append(" ").append(counter.displayName()).append(": ") - .append(Integer.toString(getClaimsStatistic().getCounter(counter))) - .append(System.lineSeparator()); + public static final class Output { + /** The XML output document */ + private final Document document; + /** + * The claim statics from the execution that generated the document. + * May be empty if the Document was read from disk. + */ + private final ClaimStatistic statistic; + /** + * The configuration that generated the document + */ + private final ReportConfiguration configuration; + + /** + * Create an output with statistics. + * @param builder the Builder + */ + private Output(final Builder builder) { + this.document = builder.document; + this.statistic = builder.statistic == null ? new ClaimStatistic() : builder.statistic; + this.configuration = builder.configuration == null ? new ReportConfiguration() : builder.configuration; } - } - /** - * Gets the document that was generated during execution. - * @return the document that was generated during execution. - */ - public Document getDocument() { - return document; + public static Builder builder() { + return new Builder(); + } + + /** + * Gets the document that was generated during execution. + * @return the document that was generated during execution. + */ + public Document getDocument() { + return document; + } + + public ClaimStatistic getStatistic() { + return statistic; + } + + public ReportConfiguration getConfiguration() { + return configuration; + } + /** + * Formats the report to the output and using the stylesheet found in the report configuration. + * + * @param config s RAT report configuration. + * @throws RatException on error. + */ + public void format(final ReportConfiguration config) throws RatException { + format(config.getStyleSheet(), config.getOutput()); + } + + /** + * Formats the report to the specified output using the stylesheet. It is safe to call this method more than once + * in order to generate multiple reports from the same run. + * + * @param stylesheet the style sheet to use for XSLT formatting. + * @param output the output stream to write to. + * @throws RatException on error. + */ + public void format(final IOSupplier stylesheet, final IOSupplier output) throws RatException { + TransformerFactory tf = TransformerFactory.newInstance(); + Transformer transformer; + try (OutputStream out = output.get(); + InputStream styleIn = stylesheet.get()) { + transformer = tf.newTransformer(new StreamSource(styleIn)); + transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); + transformer.setOutputProperty(OutputKeys.METHOD, "xml"); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); + transformer.transform(new DOMSource(document), + new StreamResult(new OutputStreamWriter(out, StandardCharsets.UTF_8))); + } catch (TransformerException | IOException e) { + throw new RatException(e); + } + } + + /** + * Writes a text summary of issues with the run. + * @param appendable the appendable to write to. + * @throws IOException on error. + */ + public void writeSummary(final Appendable appendable) throws IOException { + appendable.append("RAT summary:").append(System.lineSeparator()); + for (ClaimStatistic.Counter counter : ClaimStatistic.Counter.values()) { + appendable.append(" ").append(counter.displayName()).append(": ") + .append(Integer.toString(statistic.getCounter(counter))) + .append(System.lineSeparator()); + } + } + + public static final class Builder { + /** The document that was generated */ + private Document document; + /** + * The claim statistic from the execution that generated the document. + * May be empty if the Document was read from disk. + */ + private ClaimStatistic statistic; + /** + * The configuration that generated the document + */ + private ReportConfiguration configuration; + + public Builder document(final Document document) { + this.document = document; + return this; + } + + public Builder document(final String fileName, final DocumentName workingDirectory) { + DocumentBuilder builder; + try { + builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + } catch (ParserConfigurationException e) { + throw new ConfigurationException("Unable to create DOM builder", e); + } + + File inputFile = workingDirectory.resolve(fileName).asFile(); + try (InputStream inputStream = new FileInputStream(inputFile)) { + this.document = builder.parse(inputStream); + } catch (SAXException | IOException e) { + throw new ConfigurationException("Unable to read file: " + inputFile, e); + } + return this; + } + + public Output build() { + return new Output(this); + } + + public Builder statistic(final ClaimStatistic statistic) { + this.statistic = statistic; + return this; + } + + public Builder statistic(final String fileName, final DocumentName workingDirectory) { + File sourceFile = workingDirectory.resolve(fileName).asFile(); + try { + ClaimStatistic statistic = new ClaimStatistic(); + statistic.serde().deserialize(() -> new FileInputStream(sourceFile)); + this.statistic = statistic; + return this; + } catch (IOException e) { + throw new ConfigurationException("Unable to read file: " + sourceFile, e); + } + } + + public Builder configuration(final ReportConfiguration configuration) { + this.configuration = configuration; + return this; + } + + public Builder configuration(final String fileName, final DocumentName workingDirectory) { + File configurationFile = workingDirectory.resolve(fileName).asFile(); + try { + ReportConfiguration config = new ReportConfiguration(); + config.serde().deserialize(() -> new FileInputStream(configurationFile), workingDirectory); + this.configuration = config; + return this; + } catch (IOException e) { + throw new ConfigurationException("Unable to read file: " + configurationFile, e); + } + } + } } } diff --git a/apache-rat-core/src/main/java/org/apache/rat/commandline/Arg.java b/apache-rat-core/src/main/java/org/apache/rat/commandline/Arg.java index 069009aa9..91c9c0d0d 100644 --- a/apache-rat-core/src/main/java/org/apache/rat/commandline/Arg.java +++ b/apache-rat-core/src/main/java/org/apache/rat/commandline/Arg.java @@ -38,7 +38,6 @@ import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.commons.io.IOUtils; -import org.apache.commons.io.output.CloseShieldOutputStream; import org.apache.commons.lang3.tuple.Pair; import org.apache.rat.ConfigurationException; import org.apache.rat.Defaults; @@ -518,14 +517,14 @@ public enum Arg { if ("x".equals(key)) { // display deprecated message. context.getCommandLine().hasOption("x"); - context.getConfiguration().setStyleSheet(StyleSheets.getStyleSheet("xml")); + context.getConfiguration().setStyleSheet(StyleSheets.getStyleSheet("xml", context.getWorkingDirectory())); } else { String[] style = context.getCommandLine().getOptionValues(selected); if (style.length != 1) { DefaultLog.getInstance().error("Please specify a single stylesheet"); throw new ConfigurationException("Please specify a single stylesheet"); } - context.getConfiguration().setStyleSheet(StyleSheets.getStyleSheet(style[0])); + context.getConfiguration().setStyleSheet(StyleSheets.getStyleSheet(style[0], context.getWorkingDirectory())); } }), @@ -624,7 +623,7 @@ public enum Arg { } catch (ParseException e) { // we write to system out by default. context.logParseException(e, selected, "System.out"); - context.getConfiguration().setOut(() -> CloseShieldOutputStream.wrap(System.out)); // NOSONAR + context.getConfiguration().setOut(ReportConfiguration.SYSTEM_OUT); } }), diff --git a/apache-rat-core/src/main/java/org/apache/rat/commandline/StyleSheets.java b/apache-rat-core/src/main/java/org/apache/rat/commandline/StyleSheets.java index c73a9e4ef..2e110a7db 100644 --- a/apache-rat-core/src/main/java/org/apache/rat/commandline/StyleSheets.java +++ b/apache-rat-core/src/main/java/org/apache/rat/commandline/StyleSheets.java @@ -21,12 +21,11 @@ import java.io.InputStream; import java.net.URL; import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; import java.util.Objects; -import org.apache.commons.io.function.IOSupplier; import org.apache.rat.ConfigurationException; +import org.apache.rat.ReportConfiguration; +import org.apache.rat.document.DocumentName; import static java.lang.String.format; @@ -49,7 +48,11 @@ public enum StyleSheets { /** * The plain style sheet. The current default. */ - XML("xml", "Produces output in pretty-printed XML."); + XML("xml", "Produces output in pretty-printed XML."), + /** + * Official HTML5 stylesheet. + */ + XHTML5("xhtml5", "Produces a HTML5 report"); /** * The name of the style sheet. Must map to bundled resource xslt file */ @@ -70,29 +73,31 @@ public enum StyleSheets { } /** - * Gets the IOSupplier for a style sheet. - * @return an IOSupplier for the sheet. + * Gets the IODescriptor for a style sheet. + * @return an IODescriptor for the sheet. */ - public IOSupplier getStyleSheet() { - return Objects.requireNonNull(StyleSheets.class.getClassLoader().getResource(format("org/apache/rat/%s.xsl", name)), - "missing stylesheet: " + name)::openStream; + public ReportConfiguration.IODescriptor getStyleSheet() { + URL url = StyleSheets.class.getClassLoader().getResource(format("org/apache/rat/%s.xsl", name)); + Objects.requireNonNull(url, "missing stylesheet: " + name); + return new ReportConfiguration.IODescriptor<>(name, url::openStream); } /** - * Gets the IOSupplier for a style sheet. + * Gets the IODescriptor for a style sheet. * @param name the short name for or the path to a style sheet. - * @return the IOSupplier for the style sheet. + * @param workingDirectory the working directory to resolve the name against. + * @return the IODescriptor for the style sheet. */ - public static IOSupplier getStyleSheet(final String name) { + public static ReportConfiguration.IODescriptor getStyleSheet(final String name, final DocumentName workingDirectory) { URL url = StyleSheets.class.getClassLoader().getResource(format("org/apache/rat/%s.xsl", name)); if (url != null) { - return url::openStream; + return new ReportConfiguration.IODescriptor<>(name, url::openStream); } - Path p = Paths.get(name); - if (p.toFile().exists()) { - return () -> Files.newInputStream(p); + DocumentName xslt = workingDirectory.resolve(name); + if (xslt.asFile().exists()) { + return new ReportConfiguration.IODescriptor<>(xslt.toString(), () -> Files.newInputStream(xslt.asFile().toPath())); } - throw new ConfigurationException(format("Stylesheet file '%s' not found", name)); + throw new ConfigurationException(format("Stylesheet file '%s' not found: %s", name, xslt.getName())); } /** diff --git a/apache-rat-core/src/main/java/org/apache/rat/config/exclusion/ExclusionProcessor.java b/apache-rat-core/src/main/java/org/apache/rat/config/exclusion/ExclusionProcessor.java index 7e7474b00..8a32b56fb 100644 --- a/apache-rat-core/src/main/java/org/apache/rat/config/exclusion/ExclusionProcessor.java +++ b/apache-rat-core/src/main/java/org/apache/rat/config/exclusion/ExclusionProcessor.java @@ -22,15 +22,22 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.TreeSet; +import java.util.function.Predicate; import java.util.stream.Collectors; +import org.apache.commons.lang3.NotImplementedException; +import org.apache.rat.configuration.XMLConfigurationReader; import org.apache.rat.document.DocumentName; import org.apache.rat.document.DocumentNameMatcher; +import org.apache.rat.report.xml.writer.IXmlWriter; import org.apache.rat.utils.DefaultLog; import org.apache.rat.utils.ExtendedIterator; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; import static java.lang.String.format; @@ -74,7 +81,13 @@ public ExclusionProcessor() { excludedCollections = new HashSet<>(); } - /** Reset the {@link #lastMatcher} and {@link #lastMatcherBaseDir} to start again */ + public Serde serde() { + return new Serde(); + } + + /** + * Reset the {@link #lastMatcher} and {@link #lastMatcherBaseDir} to start again + */ private void resetLastMatcher() { lastMatcher = null; lastMatcherBaseDir = null; @@ -337,4 +350,73 @@ private void extractPaths(final MatcherSet.Builder matcherBuilder) { } } + public class Serde { + public void serialize(final IXmlWriter writer) throws IOException { + writer.openElement("ExclusionProcessor"); + + for (String pattern : excludedPatterns) { + writer.openElement("excludedPattern").attribute("pattern", pattern).closeElement(); + } + for (StandardCollection obj : excludedCollections) { + writer.openElement("excludedCollection").attribute("name", obj.name()).closeElement(); + } + for (DocumentNameMatcher obj : excludedPaths) { + writer.openElement("excludedPath").attribute("name", obj.toString()).closeElement(); + } + + for (String pattern : includedPatterns) { + writer.openElement("includedPattern").attribute("pattern", pattern).closeElement(); + } + for (StandardCollection obj : includedCollections) { + writer.openElement("includedCollection").attribute("name", obj.name()).closeElement(); + } + for (DocumentNameMatcher obj : includedPaths) { + writer.openElement("includedPath").attribute("name", obj.toString()).closeElement(); + } + + for (StandardCollection obj : fileProcessors) { + writer.openElement("fileProcessor").attribute("name", obj.name()).closeElement(); + } + writer.closeElement(); + + } + + public void deserialize(final Node n) { + final NodeList children = n.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + Node child = children.item(i); + Map attributes = XMLConfigurationReader.attributes(child); + StandardCollection collection; + switch (child.getNodeName()) { + case "excludedPattern": + excludedPatterns.add(attributes.get("pattern")); + break; + case "excludedCollection": + collection = StandardCollection.valueOf(attributes.get("name")); + excludedCollections.add(collection); + break; + case "excludedPath": + excludedPaths.add(new DocumentNameMatcher(attributes.get("name"), + (Predicate) x -> { + throw new NotImplementedException("Deserialized ExclusionProcessor can not evaluate paths"); + })); + break; + case "includedPattern": + includedPatterns.add(attributes.get("pattern")); + break; + case "includedCollection": + collection = StandardCollection.valueOf(attributes.get("name")); + includedCollections.add(collection); + break; + case "includedPath": + excludedPaths.add(new DocumentNameMatcher(attributes.get("name"), + (Predicate) x -> { + throw new NotImplementedException("Deserialized ExclusionProcessor can not evaluate paths"); + })); + break; + } + + } + } + } } diff --git a/apache-rat-core/src/main/java/org/apache/rat/configuration/XMLConfigurationReader.java b/apache-rat-core/src/main/java/org/apache/rat/configuration/XMLConfigurationReader.java index 4d3e1a882..51ef1c1a8 100644 --- a/apache-rat-core/src/main/java/org/apache/rat/configuration/XMLConfigurationReader.java +++ b/apache-rat-core/src/main/java/org/apache/rat/configuration/XMLConfigurationReader.java @@ -191,7 +191,7 @@ public void read(final URI... uris) { * @param list the NodeList to process * @param consumer the consumer to apply to each node in the list. */ - private void nodeListConsumer(final NodeList list, final Consumer consumer) { + public static void nodeListConsumer(final NodeList list, final Consumer consumer) { for (int i = 0; i < list.getLength(); i++) { consumer.accept(list.item(i)); } @@ -217,7 +217,7 @@ public void add(final Document newDoc) { * @param node The node to process * @return the map of attributes on the node. */ - private Map attributes(final Node node) { + public static Map attributes(final Node node) { NamedNodeMap nnm = node.getAttributes(); Map result = new HashMap<>(); for (int i = 0; i < nnm.getLength(); i++) { diff --git a/apache-rat-core/src/main/java/org/apache/rat/license/LicenseSetFactory.java b/apache-rat-core/src/main/java/org/apache/rat/license/LicenseSetFactory.java index 6fe00db5a..07f278918 100644 --- a/apache-rat-core/src/main/java/org/apache/rat/license/LicenseSetFactory.java +++ b/apache-rat-core/src/main/java/org/apache/rat/license/LicenseSetFactory.java @@ -20,13 +20,18 @@ import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.Optional; +import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.function.Predicate; +import java.util.stream.Collectors; +import org.apache.rat.ConfigurationException; import org.apache.rat.analysis.IHeaderMatcher; import org.apache.rat.analysis.IHeaders; +import org.apache.rat.utils.DefaultLog; import org.apache.rat.utils.Log; import org.apache.rat.utils.ReportingSet; @@ -121,6 +126,31 @@ public LicenseSetFactory(final SortedSet licenses) { licenses.forEach(l -> families.addIfNotPresent(l.getLicenseFamily())); } + public void validate() { + Log log = DefaultLog.getInstance(); + + // verify license definitions exist + if (getLicenses(LicenseFilter.ALL).isEmpty()) { + String msg = "At least one license must be defined"; + log.error(msg); + throw new ConfigurationException(msg); + } + + // verify that all approved license families exist + Set exists = getLicenseFamilies(LicenseFilter.ALL) + .stream().map(ILicenseFamily::getFamilyCategory).collect(Collectors.toSet()); + Set approved = new HashSet<>(approvedLicenseCategories); + approved.removeIf(exists::contains); + approved.forEach(name -> log.warn(String.format("License category '%s' was approved but does not exist.", name))); + + // verify that all approved licenses exist + exists = getLicenses(LicenseFilter.ALL) + .stream().map(ILicense::getId).collect(Collectors.toSet()); + approved = new HashSet<>(approvedLicenseIds); + approved.removeIf(exists::contains); + approved.forEach(name -> log.warn(String.format("License '%s' was approved but does not exist.", name))); + } + public void add(final LicenseSetFactory other) { this.families.addAll(other.families); this.licenses.addAll(other.licenses); diff --git a/apache-rat-core/src/main/java/org/apache/rat/report/IReportable.java b/apache-rat-core/src/main/java/org/apache/rat/report/IReportable.java index 2c108af6c..a92ba0599 100644 --- a/apache-rat-core/src/main/java/org/apache/rat/report/IReportable.java +++ b/apache-rat-core/src/main/java/org/apache/rat/report/IReportable.java @@ -33,5 +33,5 @@ public interface IReportable { * Returns the DocumentName for the reportable. * @return the DocumentName for the reportable. */ - DocumentName getName(); + DocumentName name(); } diff --git a/apache-rat-core/src/main/java/org/apache/rat/report/claim/ClaimStatistic.java b/apache-rat-core/src/main/java/org/apache/rat/report/claim/ClaimStatistic.java index 62572916c..d47f2e5b7 100644 --- a/apache-rat-core/src/main/java/org/apache/rat/report/claim/ClaimStatistic.java +++ b/apache-rat-core/src/main/java/org/apache/rat/report/claim/ClaimStatistic.java @@ -19,14 +19,26 @@ package org.apache.rat.report.claim; +import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.apache.commons.io.function.IOSupplier; import org.apache.commons.lang3.StringUtils; +import org.apache.rat.ConfigurationException; import org.apache.rat.api.Document; +import org.apache.rat.configuration.XMLConfigurationReader; +import org.apache.rat.report.xml.writer.XmlWriter; +import org.xml.sax.SAXException; /** * This class provides a numerical overview about @@ -113,6 +125,9 @@ public String displayName() { /** Map of counter type to value */ private final ConcurrentHashMap counterMap = new ConcurrentHashMap<>(); + public Serde serde() { + return new Serde(); + } /** * Converts null counter to 0. * @@ -141,6 +156,15 @@ public void incCounter(final Counter counter, final int value) { counterMap.compute(counter, (k, v) -> v == null ? new IntCounter().increment(value) : v.increment(value)); } + /** + * Increments the counts for the counter. + * @param counter the counter to increment. + * @param value the value to increment the counter by. + */ + public void setCounter(final Counter counter, final int value) { + counterMap.put(counter, new IntCounter().increment(value)); + } + /** * Gets the counts for the Document.Type. * @param documentType the Document.Type to get the counter for. @@ -288,5 +312,96 @@ public IntCounter increment(final int count) { public int value() { return value; } + + @Override + public String toString() { + return String.valueOf(value); + } + } + + /** + * Serialze and deserialze the claim Statistic. + */ + public class Serde { + + public void serialize(final Appendable appendable) throws IOException { + try (XmlWriter writer = new XmlWriter(appendable)) { + writer.startDocument().openElement("ClaimStatistic") + .openElement("licenseNameMap"); + for (Map.Entry entry : licenseNameMap.entrySet()) { + if (entry.getValue().value > 0) { + writer.openElement("licenseName") + .attribute("count", entry.getValue().toString()) + .attribute("name", entry.getKey()).closeElement(); + } + } + writer.closeElement() + .openElement("licenseFamilyCategoryMap"); + for (Map.Entry entry : licenseFamilyCategoryMap.entrySet()) { + if (entry.getValue().value > 0) { + writer.openElement("familyCategory") + .attribute("count", entry.getValue().toString()) + .attribute("name", entry.getKey()).closeElement(); + } + } + writer.closeElement() + .openElement("documentTypeMap"); + for (Map.Entry entry : documentTypeMap.entrySet()) { + if (entry.getValue().value > 0) { + writer.openElement("documentType") + .attribute("count", entry.getValue().toString()) + .attribute("name", entry.getKey().name()).closeElement(); + } + } + writer.closeElement() + .openElement("counterMap"); + for (Map.Entry entry : counterMap.entrySet()) { + if (entry.getValue().value > 0) { + writer.openElement("counter") + .attribute("count", entry.getValue().toString()) + .attribute("name", entry.getKey().name()).closeElement(); + } + } + writer.closeElement(); + } + } + + public void deserialize(final IOSupplier inputStreamSupplier) throws IOException { + DocumentBuilder builder; + try { + builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + } catch (ParserConfigurationException e) { + throw new ConfigurationException("Unable to create DOM builder", e); + } + org.w3c.dom.Document document; + try (InputStream stream = inputStreamSupplier.get()) { + document = builder.parse(stream); + + } catch (SAXException e) { + throw new IOException("Unable to read input", e); + } + + XMLConfigurationReader.nodeListConsumer(document.getElementsByTagName("licenseName"), node -> { + Map attributes = XMLConfigurationReader.attributes(node); + incLicenseNameCount(attributes.get("name"), Integer.parseInt(attributes.get("count"))); + }); + + XMLConfigurationReader.nodeListConsumer(document.getElementsByTagName("familyCategory"), node -> { + Map attributes = XMLConfigurationReader.attributes(node); + incLicenseCategoryCount(attributes.get("name"), Integer.parseInt(attributes.get("count"))); + }); + + XMLConfigurationReader.nodeListConsumer(document.getElementsByTagName("documentType"), node -> { + Map attributes = XMLConfigurationReader.attributes(node); + Document.Type type = Document.Type.valueOf(attributes.get("name")); + incCounter(type, Integer.parseInt(attributes.get("count"))); + }); + + XMLConfigurationReader.nodeListConsumer(document.getElementsByTagName("counter"), node -> { + Map attributes = XMLConfigurationReader.attributes(node); + Counter type = Counter.valueOf(attributes.get("name")); + setCounter(type, Integer.parseInt(attributes.get("count"))); + }); + } } } diff --git a/apache-rat-core/src/test/java/org/apache/rat/testhelpers/FileUtils.java b/apache-rat-core/src/main/java/org/apache/rat/utils/FileUtils.java similarity index 81% rename from apache-rat-core/src/test/java/org/apache/rat/testhelpers/FileUtils.java rename to apache-rat-core/src/main/java/org/apache/rat/utils/FileUtils.java index 5681e7972..50da3eccd 100644 --- a/apache-rat-core/src/test/java/org/apache/rat/testhelpers/FileUtils.java +++ b/apache-rat-core/src/main/java/org/apache/rat/utils/FileUtils.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * * under the License. * */ -package org.apache.rat.testhelpers; +package org.apache.rat.utils; import java.io.File; import java.io.FileWriter; @@ -25,15 +25,17 @@ import java.util.Arrays; import java.util.Collections; -import static org.assertj.core.api.Fail.fail; -public class FileUtils { +public final class FileUtils { + private FileUtils() { + // do not instantiate + } /** * Creates a directory if it does not exist. * @param dir the directory to make. */ - public static void mkDir(File dir) { + public static void mkDir(final File dir) { boolean ignored = dir.mkdirs(); } @@ -41,7 +43,7 @@ public static void mkDir(File dir) { * Deletes a file if it exists. * @param file the file to delete. */ - public static void delete(File file) { + public static void delete(final File file) { if (file.exists()) { if (file.isDirectory()) { try { @@ -62,15 +64,16 @@ public static void delete(File file) { * @param lines the lines to write into the file. * @return the new File. */ - static public File writeFile(File dir, final String name, final Iterable lines) { + public static File writeFile(final File dir, final String name, final Iterable lines) { if (dir == null) { - fail("base directory not specified"); + throw new IllegalArgumentException("base directory not specified"); } + mkDir(dir); File file = new File(dir, name); try (PrintWriter writer = new PrintWriter(new FileWriter(file))) { lines.forEach(writer::println); } catch (IOException e) { - fail(e.getMessage()); + throw new RuntimeException(e.getMessage(), e); } return file; } @@ -82,7 +85,7 @@ static public File writeFile(File dir, final String name, final Iterable * @param lines the lines to write into the file. * @return the new File. */ - static public File writeFile(File dir, final String name, final String... lines) { + public static File writeFile(final File dir, final String name, final String... lines) { return writeFile(dir, name, Arrays.asList(lines)); } @@ -92,7 +95,7 @@ static public File writeFile(File dir, final String name, final String... lines) * @param name the name of the file. * @return the new file. */ - public static File writeFile(File dir, String name) { + public static File writeFile(final File dir, final String name) { return writeFile(dir, name, Collections.singletonList(name)); } } diff --git a/apache-rat-core/src/main/java/org/apache/rat/walker/FileListWalker.java b/apache-rat-core/src/main/java/org/apache/rat/walker/FileListWalker.java index 056090858..1898c4e84 100644 --- a/apache-rat-core/src/main/java/org/apache/rat/walker/FileListWalker.java +++ b/apache-rat-core/src/main/java/org/apache/rat/walker/FileListWalker.java @@ -72,7 +72,7 @@ private FileDocument createDocument(final String unixFileName) { @Override public void run(final RatReport report) throws RatException { DefaultLog.getInstance().debug(String.format("Reading file name: %s due to option %s", source, Arg.SOURCE.option())); - DocumentName sourceName = getName(); + DocumentName sourceName = name(); try (Reader reader = source.reader()) { for (String docName : IOUtils.readLines(reader)) { try { @@ -93,7 +93,7 @@ public void run(final RatReport report) throws RatException { } @Override - public DocumentName getName() { + public DocumentName name() { return source.getName(); } } diff --git a/apache-rat-core/src/main/java/org/apache/rat/walker/IReportableListWalker.java b/apache-rat-core/src/main/java/org/apache/rat/walker/IReportableListWalker.java index b028b9c68..8df62905e 100644 --- a/apache-rat-core/src/main/java/org/apache/rat/walker/IReportableListWalker.java +++ b/apache-rat-core/src/main/java/org/apache/rat/walker/IReportableListWalker.java @@ -61,13 +61,13 @@ public void run(final RatReport report) { try { reportable.run(report); } catch (RatException e) { - DefaultLog.getInstance().error("Error processing " + reportable.getName(), e); + DefaultLog.getInstance().error("Error processing " + reportable.name(), e); } } } @Override - public DocumentName getName() { + public DocumentName name() { return documentName; } diff --git a/apache-rat-core/src/main/java/org/apache/rat/walker/Walker.java b/apache-rat-core/src/main/java/org/apache/rat/walker/Walker.java index 5651f7ab9..f0e6d7bd4 100644 --- a/apache-rat-core/src/main/java/org/apache/rat/walker/Walker.java +++ b/apache-rat-core/src/main/java/org/apache/rat/walker/Walker.java @@ -48,7 +48,7 @@ protected Document getDocument() { } @Override - public DocumentName getName() { + public DocumentName name() { return document.getName(); } } diff --git a/apache-rat-core/src/main/resources/org/apache/rat/xhtml5.xsl b/apache-rat-core/src/main/resources/org/apache/rat/xhtml5.xsl new file mode 100644 index 000000000..b15224605 --- /dev/null +++ b/apache-rat-core/src/main/resources/org/apache/rat/xhtml5.xsl @@ -0,0 +1,354 @@ + + + + + + + + + + 🗜 + 🔠 + 🚫 + + + + 📂 + + + + + + + + + + + + + + + +

Rat Report

+ + + + + + + + + + + + +

Detail

+ +

+ Documents with unapproved licenses will start with a + The first character on the next line identifies the document type. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Symbols used in this RAT report +
SymbolType
Archive file
Binary file
Ignored file
Notice file
Standard file
Unknown file
Directory
+ + + + + + + + + +
+ Resources discovered in this RAT report +
+ + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+ + + + +
+ ID: + + UUID + + + + Family: + + Unknown + + + +
+
+
+
+ + +

Summary

+ +
+

Generated at by + +

+ + + + + + + + + + + + + + + +
+ Table 1: A summary of statistics from this RAT report. +
Name:Count:Description:
+ + + + + Unknown + + +
+ +

License Statistics

+
+

Categories

+
+ + + + + + + + + + + + + +
+ Table 2: License categories found in this RAT report. +
Name:Count:
+ + + + + Unknown + + +
+
+ +

Licenses

+
+ + + + + + + + + + + + + +
+ Table 3: Licenses found in this RAT report. +
Name:Count:
+ + + + + Unknown + + +
+
+
+ +

Document types

+
+ + + + + + + + + + + + + +
+ Table 4: Document types found in this RAT report. +
Name:Count:
+
+
+
+ + +

Files with unapproved licenses

+ +
    + +
  • +
    +
+
+ + +

Archives

+
    + +
  • +
    +
+
+ + + + + + + + + + + + + + + + + + +
+ +
+ + +
+
+ +
+ + +
+
diff --git a/apache-rat-core/src/test/java/org/apache/rat/OptionCollectionTest.java b/apache-rat-core/src/test/java/org/apache/rat/OptionCollectionTest.java index 64c11257a..985fb7c29 100644 --- a/apache-rat-core/src/test/java/org/apache/rat/OptionCollectionTest.java +++ b/apache-rat-core/src/test/java/org/apache/rat/OptionCollectionTest.java @@ -225,7 +225,7 @@ public void getReportableTest(String fName) throws IOException { ReportConfiguration config = OptionCollection.parseCommands(testPath.toFile(), new String[]{fName}, o -> fail("Help called"), false); IReportable reportable = OptionCollection.getReportable(base, config); assertThat(reportable).as(() -> format("'%s' returned null", fName)).isNotNull(); - assertThat(reportable.getName().getName()).isEqualTo(expected); + assertThat(reportable.name().getName()).isEqualTo(expected); } @Test diff --git a/apache-rat-core/src/test/java/org/apache/rat/ReportConfigurationTest.java b/apache-rat-core/src/test/java/org/apache/rat/ReportConfigurationTest.java index 69af88b2b..f29c622ea 100644 --- a/apache-rat-core/src/test/java/org/apache/rat/ReportConfigurationTest.java +++ b/apache-rat-core/src/test/java/org/apache/rat/ReportConfigurationTest.java @@ -39,6 +39,7 @@ import java.util.List; import java.util.SortedSet; import java.util.function.Function; +import java.util.stream.Collectors; import org.apache.commons.io.filefilter.DirectoryFileFilter; import org.apache.commons.io.output.CloseShieldOutputStream; @@ -56,6 +57,7 @@ import org.apache.rat.testhelpers.TestingLicense; import org.apache.rat.testhelpers.TestingMatcher; import org.apache.rat.utils.DefaultLog; +import org.apache.rat.utils.Log; import org.apache.rat.utils.Log.Level; import org.apache.rat.utils.ReportingSet.Options; import org.junit.jupiter.api.AfterEach; @@ -456,7 +458,7 @@ public void outputTest() throws IOException { assertThat(underTest.getWriter()).isNotNull(); ByteArrayOutputStream stream = new ByteArrayOutputStream(); - underTest.setOut(() -> stream); + underTest.setOut(new ReportConfiguration.IODescriptor<>("outputTest", () -> stream)); assertThat(underTest.getOutput().get()).isEqualTo(stream); PrintWriter writer = underTest.getWriter().get(); assertThat(writer).isNotNull(); @@ -480,9 +482,9 @@ public void stylesheetTest() throws IOException, URISyntaxException { URL url = this.getClass().getResource("ReportConfigurationTestFile"); assertThat(url).isNotNull(); - assertThat(underTest.getStyleSheet()).isNull(); + assertThat(underTest.getStyleSheetDescriptor()).isNull(); InputStream stream = mock(InputStream.class); - underTest.setStyleSheet(() -> stream); + underTest.setStyleSheet(new ReportConfiguration.IODescriptor<>("stylesheetTest", () -> stream)); assertThat(underTest.getStyleSheet().get()).isEqualTo(stream); File file = mock(File.class); @@ -515,32 +517,37 @@ public void testFlags() { @Test public void testValidate() { - final StringBuilder sb = new StringBuilder(); + TestingLog testLog = new TestingLog(); + Log oldLog = DefaultLog.getInstance(); + try { + DefaultLog.setInstance(testLog); + String msg = "At least one source must be specified"; - assertThatThrownBy(() -> underTest.validate(sb::append)).isExactlyInstanceOf(ConfigurationException.class) + assertThatThrownBy(underTest::validate).isExactlyInstanceOf(ConfigurationException.class) .hasMessageContaining(msg); - assertThat(sb.toString()).isEqualTo(msg); + testLog.assertContains(msg); + testLog.clear(); - sb.setLength(0); - msg = "You must specify at least one license"; + msg = "At least one license must be defined"; underTest.addSource(mock(IReportable.class)); - assertThatThrownBy(() -> underTest.validate(sb::append)).isExactlyInstanceOf(ConfigurationException.class) + assertThatThrownBy(underTest::validate).isExactlyInstanceOf(ConfigurationException.class) .hasMessageContaining(msg); - assertThat(sb.toString()).isEqualTo(msg); + testLog.assertContains(msg); - sb.setLength(0); underTest.addLicense(testingLicense("valid", "Validation testing license")); - underTest.validate(sb::append); - assertThat(sb.length()).isEqualTo(0); + underTest.validate(); + } finally { + DefaultLog.setInstance(oldLog); + } } @Test public void testSetOut() throws IOException { ReportConfiguration config = new ReportConfiguration(); try (OutputStreamInterceptor osi = new OutputStreamInterceptor()) { - config.setOut(() -> osi); + config.setOut(new ReportConfiguration.IODescriptor<>("testSetOut", () -> osi)); assertThat(osi.closeCount).isEqualTo(0); try (OutputStream os = config.getOutput().get()) { assertThat(os).isNotNull(); @@ -563,16 +570,18 @@ public void logFamilyCollisionTest() { // verify default collision logs WARNING underTest.addFamily(ILicenseFamily.builder().setLicenseFamilyCategory("CAT").setLicenseFamilyName("name2")); - assertThat(log.getCaptured().contains("WARN")).as("default value not WARN").isTrue(); - assertThat(log.getCaptured().contains("CAT")).as("'CAT' not found").isTrue(); + assertThat(log.getCaptured()).contains("WARN").contains("CAT"); // verify level setting works. for (Level l : Level.values()) { log.clear(); underTest.logFamilyCollisions(l); underTest.addFamily(ILicenseFamily.builder().setLicenseFamilyCategory("CAT").setLicenseFamilyName("name2")); - assertThat(log.getCaptured().contains("CAT")).as("'CAT' not found").isTrue(); - assertThat(log.getCaptured().contains(l.name())).as("logging not set to "+l).isTrue(); + if (DefaultLog.getInstance().isEnabled(l)) { + assertThat(log.getCaptured()).contains("CAT").contains(l.name()); + } else { + assertThat(log.getCaptured()).doesNotContain("CAT").doesNotContain(l.name()); + } } } @@ -662,51 +671,52 @@ public void licenseDuplicateOptionsTest() { .isExactlyInstanceOf(IllegalArgumentException.class); } - /** - * Validates that the configuration contains the default approved licenses. - * @param config The configuration to test. - */ - public static void validateDefaultApprovedLicenses(ReportConfiguration config) { - validateDefaultApprovedLicenses(config, 0); - } /** * Validates that the configuration contains the default approved licenses. * @param config The configuration to test. */ - public static void validateDefaultApprovedLicenses(ReportConfiguration config, int additionalIdCount) { - assertThat(config.getLicenseCategories(LicenseFilter.APPROVED)).hasSize(XMLConfigurationReaderTest.APPROVED_IDS.length + additionalIdCount); - for (String s : XMLConfigurationReaderTest.APPROVED_IDS) { - assertThat(config.getLicenseCategories(LicenseFilter.APPROVED)).contains(ILicenseFamily.makeCategory(s)); + public static void validateDefaultApprovedLicenses(ReportConfiguration config, String... additionalIds) { + validateLicenses(config, Arrays.asList(additionalIds), LicenseFilter.APPROVED, XMLConfigurationReaderTest.APPROVED_LICENSES); } + + /** + * Validates that the configuration contains all the default licenses along with any addiitonal licenses + * @param config the configuration to test. + * @param additionalLicenses Additional licence IDs that are expected. + */ + public static void validateDefaultLicenses(ReportConfiguration config, String...additionalLicenses) { + validateLicenses(config, Arrays.asList(additionalLicenses), LicenseFilter.ALL, XMLConfigurationReaderTest.EXPECTED_LICENSES); } + private static void validateLicenses(ReportConfiguration config, List additionalIds, LicenseFilter filter, String[] approvedIds) { + List expected = new ArrayList<>(Arrays.asList(approvedIds)); + expected.addAll(additionalIds); + assertThat(config.getLicenses(filter).stream().map(ILicense::getId).collect(Collectors.toSet())).containsExactlyInAnyOrderElementsOf(expected); + } + + /** * Validates that the configuration contains the default license families. * @param config the configuration to test. */ public static void validateDefaultLicenseFamilies(ReportConfiguration config, String...additionalIds) { - assertThat(config.getLicenseFamilies(LicenseFilter.ALL)).hasSize(XMLConfigurationReaderTest.EXPECTED_IDS.length + additionalIds.length); - List expected = new ArrayList<>(); - expected.addAll(Arrays.asList(XMLConfigurationReaderTest.EXPECTED_IDS)); - expected.addAll(Arrays.asList(additionalIds)); - for (ILicenseFamily family : config.getLicenseFamilies(LicenseFilter.ALL)) { - assertThat(expected).contains(family.getFamilyCategory().trim()); - } + validateLicenseFamilies(config, Arrays.asList(additionalIds), LicenseFilter.ALL, XMLConfigurationReaderTest.EXPECTED_IDS); } /** - * Validates that the configuration contains the default licenses. + * Validates that the configuration contains the default license families. * @param config the configuration to test. */ - public static void validateDefaultLicenses(ReportConfiguration config, String...additionalLicenses) { - assertThat(config.getLicenses(LicenseFilter.ALL)).hasSize(XMLConfigurationReaderTest.EXPECTED_LICENSES.length + additionalLicenses.length); - List expected = new ArrayList<>(); - expected.addAll(Arrays.asList(XMLConfigurationReaderTest.EXPECTED_LICENSES)); - expected.addAll(Arrays.asList(additionalLicenses)); - for (ILicense license : config.getLicenses(LicenseFilter.ALL)) { - assertThat(expected).contains(license.getId()); + public static void validateDefaultApprovedLicenseFamilies(ReportConfiguration config, String...additionalIds) { + validateLicenseFamilies(config, Arrays.asList(additionalIds), LicenseFilter.APPROVED, XMLConfigurationReaderTest.APPROVED_IDS); } + + private static void validateLicenseFamilies(ReportConfiguration config, List additionalIds, LicenseFilter filter, String[] approvedIds) { + List expected = new ArrayList<>(Arrays.asList(approvedIds)); + expected.addAll(additionalIds); + assertThat(config.getLicenseFamilies(filter).stream().map(lf -> lf.getFamilyCategory().trim()) + .collect(Collectors.toSet())).containsExactlyInAnyOrderElementsOf(expected); } /** @@ -719,9 +729,10 @@ public static void validateDefault(ReportConfiguration config) { assertThat(config.getCopyrightMessage()).isNull(); assertThat(config.getStyleSheet()).withFailMessage("Stylesheet should not be null").isNotNull(); - validateDefaultApprovedLicenses(config); validateDefaultLicenseFamilies(config); + validateDefaultApprovedLicenseFamilies(config); validateDefaultLicenses(config); + validateDefaultApprovedLicenses(config); } /** diff --git a/apache-rat-core/src/test/java/org/apache/rat/ReporterOptionsProvider.java b/apache-rat-core/src/test/java/org/apache/rat/ReporterOptionsProvider.java index 2baa5e6ff..2bf1c8dce 100644 --- a/apache-rat-core/src/test/java/org/apache/rat/ReporterOptionsProvider.java +++ b/apache-rat-core/src/test/java/org/apache/rat/ReporterOptionsProvider.java @@ -56,7 +56,7 @@ import org.apache.rat.test.AbstractOptionsProvider; import org.apache.rat.test.utils.OptionFormatter; import org.apache.rat.test.utils.Resources; -import org.apache.rat.testhelpers.FileUtils; +import org.apache.rat.utils.FileUtils; import org.apache.rat.testhelpers.TestingLog; import org.apache.rat.testhelpers.TextUtils; import org.apache.rat.testhelpers.XmlUtils; @@ -128,9 +128,9 @@ private void validateNoArgSetup() throws IOException, RatException { try { ReportConfiguration config = generateConfig(Collections.emptyList()); Reporter reporter = new Reporter(config); - ClaimStatistic claimStatistic = reporter.execute(); + Reporter.Output output = reporter.execute(); ClaimValidator validator = config.getClaimValidator(); - assertThat(validator.listIssues(claimStatistic)).isEmpty(); + assertThat(validator.listIssues(output.getStatistic())).isEmpty(); } finally { DefaultLog.setInstance(null); } @@ -156,8 +156,8 @@ private void editLicenseTest(final Option option) { FileUtils.delete(resultFile); Reporter reporter = new Reporter(config); - ClaimStatistic claimStatistic = reporter.execute(); - assertThat(claimStatistic).isNotNull(); + Reporter.Output output = reporter.execute(); + assertThat(output.getStatistic()).isNotNull(); String contents = String.join("\n", IOUtils.readLines(new FileReader(testFile))); assertThat(contents).isEqualTo("class NoLicense {}"); assertThat(resultFile).exists(); @@ -212,9 +212,9 @@ private void execLicensesDeniedTest(final Option option, final String[] args) { ReportConfiguration config = generateConfig(ImmutablePair.of(option, args)); Reporter reporter = new Reporter(config); - ClaimStatistic claimStatistic = reporter.execute(); + Reporter.Output output = reporter.execute(); ClaimValidator validator = config.getClaimValidator(); - assertThat(validator.listIssues(claimStatistic)).containsExactly("UNAPPROVED"); + assertThat(validator.listIssues(output.getStatistic())).containsExactly("UNAPPROVED"); } catch (IOException | RatException e) { fail(e.getMessage(), e); } @@ -246,9 +246,7 @@ private void noDefaultsTest(final Option option) { reporter.execute(); fail("Should have thrown exception"); } catch (RatException e) { - ClaimStatistic claimStatistic = reporter.getClaimsStatistic(); ClaimValidator validator = config.getClaimValidator(); - assertThat(validator.listIssues(claimStatistic)).containsExactlyInAnyOrder("DOCUMENT_TYPES", "LICENSE_CATEGORIES", "LICENSE_NAMES", "STANDARDS"); } } catch (IOException | RatException e) { fail(e.getMessage(), e); @@ -276,16 +274,16 @@ protected void counterMaxTest() { ReportConfiguration config = generateConfig(Collections.emptyList()); Reporter reporter = new Reporter(config); - ClaimStatistic claimStatistic = reporter.execute(); + Reporter.Output output = reporter.execute(); ClaimValidator validator = config.getClaimValidator(); - assertThat(validator.listIssues(claimStatistic)).containsExactly("UNAPPROVED"); + assertThat(validator.listIssues(output.getStatistic())).containsExactly("UNAPPROVED"); arg[0] = "Unapproved:1"; config = generateConfig(ImmutablePair.of(option, arg)); reporter = new Reporter(config); - claimStatistic = reporter.execute(); + output = reporter.execute(); validator = config.getClaimValidator(); - assertThat(validator.listIssues(claimStatistic)).isEmpty(); + assertThat(validator.listIssues(output.getStatistic())).isEmpty(); } catch (IOException | RatException e) { fail(e.getMessage(), e); } @@ -303,16 +301,16 @@ protected void counterMinTest() { ReportConfiguration config = generateConfig(Collections.emptyList()); Reporter reporter = new Reporter(config); - ClaimStatistic claimStatistic = reporter.execute(); + Reporter.Output output = reporter.execute(); ClaimValidator validator = config.getClaimValidator(); - assertThat(validator.listIssues(claimStatistic)).containsExactly("UNAPPROVED"); + assertThat(validator.listIssues(output.getStatistic())).containsExactly("UNAPPROVED"); arg[0] = "Unapproved:1"; config = generateConfig(ImmutablePair.of(option, arg)); reporter = new Reporter(config); - claimStatistic = reporter.execute(); + output = reporter.execute(); validator = config.getClaimValidator(); - assertThat(validator.listIssues(claimStatistic)).isEmpty(); + assertThat(validator.listIssues(output.getStatistic())).isEmpty(); } catch (IOException | RatException e) { fail(e.getMessage(), e); } @@ -332,16 +330,16 @@ private void execExcludeTest(final Option option, final String[] args) { ReportConfiguration config = generateConfig(Collections.emptyList()); Reporter reporter = new Reporter(config); - ClaimStatistic claimStatistic = reporter.execute(); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(5); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.IGNORED)).isEqualTo(0); + Reporter.Output output = reporter.execute(); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(5); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.IGNORED)).isEqualTo(0); // filter out source config = generateConfig(ImmutablePair.of(option, args)); reporter = new Reporter(config); - claimStatistic = reporter.execute(); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(2); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.IGNORED)).isEqualTo(3); + output = reporter.execute(); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(2); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.IGNORED)).isEqualTo(3); } catch (IOException | RatException e) { fail(e.getMessage(), e); } @@ -386,16 +384,16 @@ protected void inputExcludeSizeTest() { ReportConfiguration config = generateConfig(Collections.emptyList()); Reporter reporter = new Reporter(config); - ClaimStatistic claimStatistic = reporter.execute(); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(3); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.IGNORED)).isEqualTo(0); + Reporter.Output output = reporter.execute(); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(3); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.IGNORED)).isEqualTo(0); // filter out source config = generateConfig(ImmutablePair.of(option, args)); reporter = new Reporter(config); - claimStatistic = reporter.execute(); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(2); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.IGNORED)).isEqualTo(1); + output = reporter.execute(); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(2); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.IGNORED)).isEqualTo(1); } catch (IOException | RatException e) { fail(e.getMessage(), e); } @@ -422,15 +420,15 @@ protected void inputExcludeStdTest() { ReportConfiguration config = generateConfig(Collections.emptyList()); Reporter reporter = new Reporter(config); - ClaimStatistic claimStatistic = reporter.execute(); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(5); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.IGNORED)).isEqualTo(4); + Reporter.Output output = reporter.execute(); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(5); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.IGNORED)).isEqualTo(4); config = generateConfig(ImmutablePair.of(option, args)); reporter = new Reporter(config); - claimStatistic = reporter.execute(); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(4); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.IGNORED)).isEqualTo(5); + output = reporter.execute(); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(4); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.IGNORED)).isEqualTo(5); } catch (IOException | RatException e) { fail(e.getMessage(), e); } @@ -480,16 +478,16 @@ protected void inputExcludeParsedScmTest() { ReportConfiguration config = generateConfig(Collections.emptyList()); Reporter reporter = new Reporter(config); - ClaimStatistic claimStatistic = reporter.execute(); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(11); + Reporter.Output output = reporter.execute(); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(11); // .gitignore is ignored by default as it is hidden but not counted - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.IGNORED)).isEqualTo(0); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.IGNORED)).isEqualTo(0); config = generateConfig(ImmutablePair.of(option, args)); reporter = new Reporter(config); - claimStatistic = reporter.execute(); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(3); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.IGNORED)).isEqualTo(8); + output = reporter.execute(); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(3); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.IGNORED)).isEqualTo(8); } catch (IOException | RatException e) { fail(e.getMessage(), e); } @@ -509,25 +507,25 @@ private void execIncludeTest(final Option option, final String[] args) { ReportConfiguration config = generateConfig(Collections.emptyList()); Reporter reporter = new Reporter(config); - ClaimStatistic claimStatistic = reporter.execute(); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(4); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.IGNORED)).isEqualTo(0); + Reporter.Output output = reporter.execute(); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(4); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.IGNORED)).isEqualTo(0); // verify exclude removes most files. config = generateConfig(ImmutablePair.of(excludeOption, EXCLUDE_ARGS)); reporter = new Reporter(config); - claimStatistic = reporter.execute(); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(1); + output = reporter.execute(); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(1); // .gitignore is ignored by default as it is hidden but not counted - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.IGNORED)).isEqualTo(3); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.IGNORED)).isEqualTo(3); // verify include pust them back config = generateConfig(ImmutablePair.of(option, args), ImmutablePair.of(excludeOption, EXCLUDE_ARGS)); reporter = new Reporter(config); - claimStatistic = reporter.execute(); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(3); + output = reporter.execute(); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(3); // .gitignore is ignored by default as it is hidden but not counted - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.IGNORED)).isEqualTo(1); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.IGNORED)).isEqualTo(1); } catch (IOException | RatException e) { fail(e.getMessage(), e); } @@ -579,15 +577,15 @@ protected void inputIncludeStdTest() { ReportConfiguration config = generateConfig(Collections.singletonList(excludes)); Reporter reporter = new Reporter(config); - ClaimStatistic claimStatistic = reporter.execute(); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(3); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.IGNORED)).isEqualTo(5); + Reporter.Output output = reporter.execute(); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(3); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.IGNORED)).isEqualTo(5); config = generateConfig(excludes, ImmutablePair.of(option, args)); reporter = new Reporter(config); - claimStatistic = reporter.execute(); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(7); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.IGNORED)).isEqualTo(1); + output = reporter.execute(); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(7); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.IGNORED)).isEqualTo(1); } catch (IOException | RatException e) { fail(e.getMessage(), e); } @@ -605,15 +603,15 @@ protected void inputSourceTest() { ReportConfiguration config = generateConfig(); Reporter reporter = new Reporter(config); - ClaimStatistic claimStatistic = reporter.execute(); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(3); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.IGNORED)).isEqualTo(0); + Reporter.Output output = reporter.execute(); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(3); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.IGNORED)).isEqualTo(0); config = generateConfig(ImmutablePair.of(option, new String[]{inputFile.getAbsolutePath()})); reporter = new Reporter(config); - claimStatistic = reporter.execute(); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(1); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.IGNORED)).isEqualTo(0); + output = reporter.execute(); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(1); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.IGNORED)).isEqualTo(0); } catch (IOException | RatException e) { fail(e.getMessage(), e); } @@ -636,18 +634,18 @@ private void execLicenseFamiliesApprovedTest(final Option option, final String[] ReportConfiguration config = addCatzLicense(generateConfig()); Reporter reporter = new Reporter(config); - ClaimStatistic claimStatistic = reporter.execute(); + Reporter.Output output = reporter.execute(); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(1); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.APPROVED)).isEqualTo(0); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.UNAPPROVED)).isEqualTo(1); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(1); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.APPROVED)).isEqualTo(0); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.UNAPPROVED)).isEqualTo(1); config = addCatzLicense(generateConfig(arg1)); reporter = new Reporter(config); - claimStatistic = reporter.execute(); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(1); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.APPROVED)).isEqualTo(1); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.UNAPPROVED)).isEqualTo(0); + output = reporter.execute(); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(1); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.APPROVED)).isEqualTo(1); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.UNAPPROVED)).isEqualTo(0); } catch (IOException | RatException e) { fail(e.getMessage(), e); } @@ -677,17 +675,17 @@ private void execLicenseFamiliesDeniedTest(final Option option, final String[] a ReportConfiguration config = generateConfig(); Reporter reporter = new Reporter(config); - ClaimStatistic claimStatistic = reporter.execute(); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(1); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.APPROVED)).isEqualTo(1); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.UNAPPROVED)).isEqualTo(0); + Reporter.Output output = reporter.execute(); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(1); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.APPROVED)).isEqualTo(1); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.UNAPPROVED)).isEqualTo(0); config = generateConfig(arg1); reporter = new Reporter(config); - claimStatistic = reporter.execute(); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(1); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.APPROVED)).isEqualTo(0); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.UNAPPROVED)).isEqualTo(1); + output = reporter.execute(); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(1); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.APPROVED)).isEqualTo(0); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.UNAPPROVED)).isEqualTo(1); } catch (IOException | RatException e) { fail(e.getMessage(), e); } @@ -721,26 +719,26 @@ private void configTest(final Option option) { ReportConfiguration config = generateConfig(); Reporter reporter = new Reporter(config); - ClaimStatistic claimStatistic = reporter.execute(); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(2); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.APPROVED)).isEqualTo(1); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.UNAPPROVED)).isEqualTo(1); + Reporter.Output output = reporter.execute(); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(2); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.APPROVED)).isEqualTo(1); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.UNAPPROVED)).isEqualTo(1); config = generateConfig(arg1); reporter = new Reporter(config); - claimStatistic = reporter.execute(); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(2); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.APPROVED)).isEqualTo(2); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.UNAPPROVED)).isEqualTo(0); + output = reporter.execute(); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(2); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.APPROVED)).isEqualTo(2); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.UNAPPROVED)).isEqualTo(0); Pair arg2 = ImmutablePair.of(Arg.CONFIGURATION_NO_DEFAULTS.find("configuration-no-defaults"), null); config = generateConfig(arg1, arg2); reporter = new Reporter(config); - claimStatistic = reporter.execute(); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(2); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.APPROVED)).isEqualTo(1); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.UNAPPROVED)).isEqualTo(1); + output = reporter.execute(); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(2); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.APPROVED)).isEqualTo(1); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.UNAPPROVED)).isEqualTo(1); } catch (IOException | RatException e) { fail(e.getMessage(), e); @@ -769,17 +767,17 @@ protected void execLicensesApprovedTest(final Option option, String[] args) { ReportConfiguration config = generateConfig(); Reporter reporter = new Reporter(config); - ClaimStatistic claimStatistic = reporter.execute(); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(2); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.APPROVED)).isEqualTo(1); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.UNAPPROVED)).isEqualTo(1); + Reporter.Output output = reporter.execute(); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(2); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.APPROVED)).isEqualTo(1); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.UNAPPROVED)).isEqualTo(1); config = generateConfig(arg1); reporter = new Reporter(config); - claimStatistic = reporter.execute(); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(2); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.APPROVED)).isEqualTo(2); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.UNAPPROVED)).isEqualTo(0); + output = reporter.execute(); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(2); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.APPROVED)).isEqualTo(2); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.UNAPPROVED)).isEqualTo(0); } catch (IOException | RatException e) { @@ -816,17 +814,17 @@ protected void scanHiddenDirectoriesTest() { ReportConfiguration config = generateConfig(); Reporter reporter = new Reporter(config); - ClaimStatistic claimStatistic = reporter.execute(); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(1); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.APPROVED)).isEqualTo(1); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.UNAPPROVED)).isEqualTo(0); + Reporter.Output output = reporter.execute(); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(1); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.APPROVED)).isEqualTo(1); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.UNAPPROVED)).isEqualTo(0); config = generateConfig(arg1); reporter = new Reporter(config); - claimStatistic = reporter.execute(); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(2); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.APPROVED)).isEqualTo(1); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.UNAPPROVED)).isEqualTo(1); + output = reporter.execute(); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(2); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.APPROVED)).isEqualTo(1); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.UNAPPROVED)).isEqualTo(1); } catch (IOException | RatException e) { fail(e.getMessage(), e); } @@ -842,16 +840,17 @@ private void outTest(final Option option) { ReportConfiguration config = generateConfig(ImmutablePair.of(option, args)); Reporter reporter = new Reporter(config); - ClaimStatistic claimStatistic = reporter.output(); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(1); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.APPROVED)).isEqualTo(1); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.UNAPPROVED)).isEqualTo(0); + Reporter.Output output = reporter.execute(); + output.format(config); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(1); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.APPROVED)).isEqualTo(1); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.UNAPPROVED)).isEqualTo(0); String actualText = TextUtils.readFile(outFile); TextUtils.assertContainsExactly(1, "Apache License 2.0: 1 ", actualText); TextUtils.assertContainsExactly(1, "STANDARD: 1 ", actualText); - } catch (IOException | RatException e) { + } catch (IOException | RatException e ) { fail(e.getMessage(), e); } } @@ -885,10 +884,11 @@ private void styleSheetTest(final Option option) { args[0] = sheet.arg(); ReportConfiguration config = generateConfig(ImmutablePair.of(option, args)); Reporter reporter = new Reporter(config); - ClaimStatistic claimStatistic = reporter.output(); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(1); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.APPROVED)).isEqualTo(0); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.UNAPPROVED)).isEqualTo(1); + Reporter.Output output = reporter.execute(); + output.format(config); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(1); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.APPROVED)).isEqualTo(0); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.UNAPPROVED)).isEqualTo(1); String actualText = baos.toString(StandardCharsets.UTF_8); switch (sheet) { @@ -906,6 +906,9 @@ private void styleSheetTest(final Option option) { case UNAPPROVED_LICENSES: TextUtils.assertContainsExactly(1, "Files with unapproved licenses:" + System.lineSeparator() + " /stylesheet", actualText); break; + case XHTML5: + TextUtils.assertPatternInTarget("Approved<\\/td>\\s+\\d+<\\/td>\\s+A count of approved licenses.<\\/td>", actualText); + break; default: fail("No test for stylesheet " + sheet); break; @@ -915,10 +918,11 @@ private void styleSheetTest(final Option option) { args[0] = file.getAbsolutePath(); ReportConfiguration config = generateConfig(ImmutablePair.of(option, args)); Reporter reporter = new Reporter(config); - ClaimStatistic claimStatistic = reporter.output(); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(1); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.APPROVED)).isEqualTo(0); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.UNAPPROVED)).isEqualTo(1); + Reporter.Output output = reporter.execute(); + output.format(config); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(1); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.APPROVED)).isEqualTo(0); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.UNAPPROVED)).isEqualTo(1); String actualText = baos.toString(StandardCharsets.UTF_8); TextUtils.assertContainsExactly(1, "Hello world", actualText); @@ -957,15 +961,16 @@ protected void xmlTest() { ReportConfiguration config = generateConfig(ImmutablePair.of(option, null)); Reporter reporter = new Reporter(config); - ClaimStatistic claimStatistic = reporter.output(); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(1); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.APPROVED)).isEqualTo(0); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.UNAPPROVED)).isEqualTo(1); + Reporter.Output output = reporter.execute(); + output.format(config); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(1); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.APPROVED)).isEqualTo(0); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.UNAPPROVED)).isEqualTo(1); String actualText = baos.toString(StandardCharsets.UTF_8); TextUtils.assertContainsExactly(1, "", actualText); - try (InputStream expected = StyleSheets.getStyleSheet("xml").get(); + try (InputStream expected = StyleSheets.getStyleSheet("xml", null).ioSupplier().get(); InputStream actual = config.getStyleSheet().get()) { assertThat(IOUtils.contentEquals(expected, actual)).as("'xml' does not match").isTrue(); } @@ -988,13 +993,13 @@ protected void logLevelTest() { ReportConfiguration config = generateConfig(); Reporter reporter = new Reporter(config); - reporter.output(); - TextUtils.assertNotContains("DEBUG", baos.toString(StandardCharsets.UTF_8)); + reporter.execute(); + TextUtils.assertNotContains("DEBUG", baos.toString(StandardCharsets.UTF_8.name())); config = generateConfig(ImmutablePair.of(option, new String[]{"debug"})); reporter = new Reporter(config); - reporter.output(); - TextUtils.assertContains("DEBUG", baos.toString(StandardCharsets.UTF_8)); + reporter.execute(); + TextUtils.assertContains("DEBUG", baos.toString(StandardCharsets.UTF_8.name())); } catch (IOException | RatException e) { fail(e.getMessage(), e); } finally { @@ -1009,16 +1014,11 @@ private void listLicenses(final Option option) { try { configureSourceDir(option); - File outFile = new File(sourceDir, "out.xml"); - FileUtils.delete(outFile); - ImmutablePair outputFile = ImmutablePair.of(Arg.OUTPUT_FILE.option(), new String[]{outFile.getAbsolutePath()}); - ImmutablePair stylesheet = ImmutablePair.of(Arg.OUTPUT_STYLE.option(), new String[]{StyleSheets.XML.arg()}); for (LicenseSetFactory.LicenseFilter filter : LicenseSetFactory.LicenseFilter.values()) { args[0] = filter.name(); - ReportConfiguration config = generateConfig(outputFile, stylesheet, ImmutablePair.of(option, args)); + ReportConfiguration config = generateConfig(ImmutablePair.of(option, args)); Reporter reporter = new Reporter(config); - reporter.output(); - Document document = XmlUtils.toDom(new FileInputStream(outFile)); + Document document = reporter.execute().getDocument(); switch (filter) { case ALL: XmlUtils.assertIsPresent(filter.name(), document, xPath, "/rat-report/rat-config/licenses/license[@id='AL2.0']"); @@ -1036,8 +1036,7 @@ private void listLicenses(final Option option) { throw new IllegalArgumentException("Unexpected filter: " + filter); } } - } catch (IOException | RatException | SAXException | ParserConfigurationException | - XPathExpressionException e) { + } catch (IOException | RatException | XPathExpressionException e) { fail(e.getMessage(), e); } } @@ -1058,16 +1057,12 @@ private void listFamilies(final Option option) { try { configureSourceDir(option); - File outFile = new File(sourceDir, "out.xml"); - FileUtils.delete(outFile); - ImmutablePair outputFile = ImmutablePair.of(Arg.OUTPUT_FILE.option(), new String[]{outFile.getAbsolutePath()}); - ImmutablePair stylesheet = ImmutablePair.of(Arg.OUTPUT_STYLE.option(), new String[]{StyleSheets.XML.arg()}); for (LicenseSetFactory.LicenseFilter filter : LicenseSetFactory.LicenseFilter.values()) { args[0] = filter.name(); - ReportConfiguration config = generateConfig(outputFile, stylesheet, ImmutablePair.of(option, args)); + ReportConfiguration config = generateConfig(ImmutablePair.of(option, args)); Reporter reporter = new Reporter(config); - reporter.output(); - Document document = XmlUtils.toDom(Files.newInputStream(outFile.toPath())); + Reporter.Output output = reporter.execute(); + Document document = output.getDocument(); switch (filter) { case ALL: XmlUtils.assertIsPresent(filter.name(), document, xPath, "/rat-report/rat-config/families/family[@id='AL']"); @@ -1085,8 +1080,7 @@ private void listFamilies(final Option option) { throw new IllegalArgumentException("Unexpected filter: " + filter); } } - } catch (IOException | RatException | SAXException | ParserConfigurationException | - XPathExpressionException e) { + } catch (IOException | RatException | XPathExpressionException e) { fail(e.getMessage(), e); } } @@ -1122,9 +1116,8 @@ private void archiveTest(final Option option) { args[0] = proc.name(); ReportConfiguration config = generateConfig(outputFile, stylesheet, ImmutablePair.of(option, args)); Reporter reporter = new Reporter(config); - reporter.output(); - Document document = XmlUtils.toDom(Files.newInputStream(outFile.toPath())); + Document document = reporter.execute().getDocument(); XmlUtils.assertIsPresent(proc.name(), document, xPath, "/rat-report/resource[@name='/dummy.jar']"); switch (proc) { case ABSENCE: @@ -1143,8 +1136,7 @@ private void archiveTest(final Option option) { throw new IllegalArgumentException("Unexpected processing " + proc); } } - } catch (IOException | RatException | SAXException | ParserConfigurationException | - XPathExpressionException e) { + } catch (IOException | RatException | XPathExpressionException e) { fail(e.getMessage(), e); } } @@ -1175,9 +1167,9 @@ private void standardTest(final Option option) { args[0] = proc.name(); ReportConfiguration config = generateConfig(outputFile, stylesheet, ImmutablePair.of(option, args)); Reporter reporter = new Reporter(config); - reporter.output(); + Reporter.Output output = reporter.execute(); - Document document = XmlUtils.toDom(Files.newInputStream(outFile.toPath())); + Document document = output.getDocument(); XmlUtils.assertIsPresent(proc.name(), document, xPath, testDoc); XmlUtils.assertIsPresent(proc.name(), document, xPath, missingDoc); @@ -1198,8 +1190,7 @@ private void standardTest(final Option option) { throw new IllegalArgumentException("Unexpected processing " + proc); } } - } catch (IOException | RatException | SAXException | ParserConfigurationException | - XPathExpressionException e) { + } catch (IOException | RatException | XPathExpressionException e) { fail(e.getMessage(), e); } } diff --git a/apache-rat-core/src/test/java/org/apache/rat/ReporterOptionsTest.java b/apache-rat-core/src/test/java/org/apache/rat/ReporterOptionsTest.java index ccb82f3c6..4f52a31a5 100644 --- a/apache-rat-core/src/test/java/org/apache/rat/ReporterOptionsTest.java +++ b/apache-rat-core/src/test/java/org/apache/rat/ReporterOptionsTest.java @@ -27,7 +27,7 @@ import org.apache.rat.api.RatException; import org.apache.rat.report.claim.ClaimStatistic; import org.apache.rat.test.AbstractConfigurationOptionsProvider; -import org.apache.rat.testhelpers.FileUtils; +import org.apache.rat.utils.FileUtils; import org.apache.rat.testhelpers.XmlUtils; import org.apache.rat.utils.DefaultLog; import org.apache.rat.utils.Log; @@ -78,14 +78,14 @@ void testRat362() { FileUtils.writeFile(testDir, "foo.md"); ReportConfiguration config = OptionCollection.parseCommands(testDir, args, o -> fail("Help called"), true); Reporter reporter = new Reporter(config); - ClaimStatistic claimStatistic = reporter.execute(); - XmlUtils.printDocument(System.out, reporter.getDocument()); + Reporter.Output output = reporter.execute(); + XmlUtils.printDocument(System.out, output.getDocument()); XPath xpath = XPathFactory.newInstance().newXPath(); - XmlUtils.assertIsPresent(reporter.getDocument(), xpath, "/rat-report/resource[@name='/foo.md']"); - XmlUtils.assertAttributes(reporter.getDocument(), xpath, "/rat-report/resource[@name='/foo.md']", + XmlUtils.assertIsPresent(output.getDocument(), xpath, "/rat-report/resource[@name='/foo.md']"); + XmlUtils.assertAttributes(output.getDocument(), xpath, "/rat-report/resource[@name='/foo.md']", XmlUtils.mapOf("type", "IGNORED")); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(0); - assertThat(claimStatistic.getCounter(ClaimStatistic.Counter.IGNORED)).isEqualTo(2); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(0); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.IGNORED)).isEqualTo(2); } catch (IOException | RatException | XPathExpressionException e) { fail(e); } diff --git a/apache-rat-core/src/test/java/org/apache/rat/ReporterTest.java b/apache-rat-core/src/test/java/org/apache/rat/ReporterTest.java index 3c2a9a725..d8f85e0d5 100644 --- a/apache-rat-core/src/test/java/org/apache/rat/ReporterTest.java +++ b/apache-rat-core/src/test/java/org/apache/rat/ReporterTest.java @@ -19,21 +19,24 @@ package org.apache.rat; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Fail.fail; -import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.IOException; import java.io.PrintStream; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; +import java.nio.file.Path; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; +import java.util.stream.Stream; import javax.xml.XMLConstants; import javax.xml.transform.Source; import javax.xml.transform.dom.DOMSource; @@ -45,24 +48,29 @@ import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathFactory; -import org.apache.commons.cli.CommandLine; -import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.ParseException; import org.apache.commons.io.FileUtils; import org.apache.rat.api.Document.Type; import org.apache.rat.api.RatException; +import org.apache.rat.commandline.Arg; import org.apache.rat.commandline.ArgumentContext; -import org.apache.rat.commandline.StyleSheets; import org.apache.rat.document.FileDocument; import org.apache.rat.document.DocumentName; import org.apache.rat.license.ILicenseFamily; import org.apache.rat.report.claim.ClaimStatistic; import org.apache.rat.test.utils.Resources; +import org.apache.rat.testhelpers.BaseOptionCollection; import org.apache.rat.testhelpers.TextUtils; import org.apache.rat.testhelpers.XmlUtils; +import org.apache.rat.testhelpers.data.ReportTestDataProvider; +import org.apache.rat.testhelpers.data.TestData; +import org.apache.rat.testhelpers.data.ValidatorData; import org.apache.rat.walker.DirectoryWalker; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.w3c.dom.Document; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; @@ -74,19 +82,19 @@ public class ReporterTest { @TempDir File tempDirectory; final String basedir; + private final OptionCollectionParser collectionParser; ReporterTest() throws URISyntaxException { basedir = Resources.getExampleResource("exampleData").getPath(); + collectionParser = new OptionCollectionParser(BaseOptionCollection.builder().build()); } @Test - public void testExecute() throws RatException, ParseException { + public void testExecute() throws RatException, ParseException, IOException { File output = new File(tempDirectory, "testExecute"); - - CommandLine cl = new DefaultParser().parse(OptionCollection.buildOptions(), new String[]{"--output-style", "xml", "--output-file", output.getPath(), basedir}); - ArgumentContext ctxt = new ArgumentContext(new File("."), cl); - ReportConfiguration config = OptionCollection.createConfiguration(ctxt); - ClaimStatistic statistic = new Reporter(config).execute(); + BaseOptionCollection optionCollection = BaseOptionCollection.builder().build(); + ArgumentContext ctxt = collectionParser.parseCommands(new File("."), new String[]{"--output-style", "xml", "--output-file", output.getPath(), basedir}); + ClaimStatistic statistic = new Reporter(ctxt.getConfiguration()).execute().getStatistic(); assertThat(statistic.getCounter(Type.ARCHIVE)).isEqualTo(1); assertThat(statistic.getCounter(Type.BINARY)).isEqualTo(2); @@ -137,11 +145,8 @@ public void testExecute() throws RatException, ParseException { @Test public void testOutputOption() throws Exception { File output = new File(tempDirectory, "test"); - CommandLine commandLine = new DefaultParser().parse(OptionCollection.buildOptions(), new String[]{"-o", output.getCanonicalPath(), basedir}); - ArgumentContext ctxt = new ArgumentContext(new File("."), commandLine); - - ReportConfiguration config = OptionCollection.createConfiguration(ctxt); - new Reporter(config).output(); + ArgumentContext ctxt = collectionParser.parseCommands(new File("."), new String[]{"--output-file", output.getCanonicalPath(), basedir}); + new Reporter(ctxt.getConfiguration()).execute().format(ctxt.getConfiguration()); assertThat(output.exists()).isTrue(); String content = FileUtils.readFileToString(output, StandardCharsets.UTF_8); TextUtils.assertPatternInTarget("^! Unapproved:\\s*2 ", content); @@ -152,15 +157,13 @@ public void testOutputOption() throws Exception { @Test public void testDefaultOutput() throws Exception { File output = new File(tempDirectory, "testDefaultOutput"); + BaseOptionCollection optionCollection = BaseOptionCollection.builder().build(); PrintStream origin = System.out; try (PrintStream out = new PrintStream(output)) { System.setOut(out); - CommandLine commandLine = new DefaultParser().parse(OptionCollection.buildOptions(), new String[]{basedir}); - ArgumentContext ctxt = new ArgumentContext(new File("."), commandLine); - - ReportConfiguration config = OptionCollection.createConfiguration(ctxt); - new Reporter(config).output(); + ArgumentContext ctxt = collectionParser.parseCommands(new File("."), new String[]{basedir}); + new Reporter(ctxt.getConfiguration()).execute().format(ctxt.getConfiguration()); } finally { System.setOut(origin); } @@ -209,11 +212,8 @@ public void testXMLOutput() throws Exception { "type", "STANDARD")); File output = new File(tempDirectory, "testXMLOutput"); - CommandLine commandLine = new DefaultParser().parse(OptionCollection.buildOptions(), new String[]{"--output-style", "xml", "--output-file", output.getPath(), basedir}); - ArgumentContext ctxt = new ArgumentContext(new File("."), commandLine); - - ReportConfiguration config = OptionCollection.createConfiguration(ctxt); - new Reporter(config).output(); + ArgumentContext ctxt = collectionParser.parseCommands(new File("."), new String[]{"--output-style", "xml", "--output-file", output.getPath(), basedir}); + new Reporter(ctxt.getConfiguration()).execute().format(ctxt.getConfiguration()); assertThat(output).exists(); Document doc = XmlUtils.toDom(java.nio.file.Files.newInputStream(output.toPath())); @@ -403,13 +403,8 @@ private Validator initValidator() throws SAXException { @Test public void xmlReportTest() throws Exception { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - ReportConfiguration configuration = initializeConfiguration(); - configuration.setStyleSheet(StyleSheets.XML.getStyleSheet()); - configuration.setOut(() -> out); - new Reporter(configuration).output(); - Document doc = XmlUtils.toDom(new ByteArrayInputStream(out.toByteArray())); + Document doc = new Reporter(configuration).execute().getDocument(); XPath xPath = XPathFactory.newInstance().newXPath(); @@ -455,10 +450,9 @@ public void plainReportTest() throws Exception { "Generated at: "; ByteArrayOutputStream out = new ByteArrayOutputStream(); ReportConfiguration configuration = initializeConfiguration(); - configuration.setOut(() -> out); - new Reporter(configuration).output(); + configuration.setOut(new ReportConfiguration.IODescriptor<>("plainReportTest", () -> out)); + new Reporter(configuration).execute().format(configuration); - out.flush(); String document = out.toString(); TextUtils.assertNotContains("", document); @@ -471,11 +465,10 @@ public void plainReportTest() throws Exception { public void unapprovedLicensesReportTest() throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); ReportConfiguration configuration = initializeConfiguration(); - configuration.setOut(() -> out); + configuration.setOut(new ReportConfiguration.IODescriptor<>("unapprovedLicensesReportTest", () -> out)); configuration.setStyleSheet(this.getClass().getResource("/org/apache/rat/unapproved-licenses.xsl")); - new Reporter(configuration).output(); + new Reporter(configuration).execute().format(configuration); - out.flush(); String document = out.toString(); TextUtils.assertContains("Generated at: ", document ); @@ -486,21 +479,50 @@ public void unapprovedLicensesReportTest() throws Exception { @Test public void counterMaxTest() throws Exception { ReportConfiguration config = initializeConfiguration(); - Reporter reporter = new Reporter(config); - reporter.output(); + Reporter.Output output = new Reporter(config).execute(); assertThat(config.getClaimValidator().hasErrors()).isTrue(); - assertThat(config.getClaimValidator().isValid(ClaimStatistic.Counter.UNAPPROVED, reporter.getClaimsStatistic().getCounter(ClaimStatistic.Counter.UNAPPROVED))) + assertThat(config.getClaimValidator().isValid(ClaimStatistic.Counter.UNAPPROVED, output.getStatistic().getCounter(ClaimStatistic.Counter.UNAPPROVED))) .isFalse(); config = initializeConfiguration(); config.getClaimValidator().setMax(ClaimStatistic.Counter.UNAPPROVED, 2); - reporter = new Reporter(config); - reporter.output(); + output = new Reporter(config).execute(); assertThat(config.getClaimValidator().hasErrors()).isFalse(); - assertThat(config.getClaimValidator().isValid(ClaimStatistic.Counter.UNAPPROVED, reporter.getClaimsStatistic().getCounter(ClaimStatistic.Counter.UNAPPROVED))) + assertThat(config.getClaimValidator().isValid(ClaimStatistic.Counter.UNAPPROVED, output.getStatistic().getCounter(ClaimStatistic.Counter.UNAPPROVED))) .isTrue(); } + static Stream getTestData() { + BaseOptionCollection.Builder builder = BaseOptionCollection.builder() + .unsupported(Arg.OUTPUT_FILE); + return new ReportTestDataProvider().getOptionTests(builder.build()).stream().map(testData -> + Arguments.of(testData.getTestName(), testData)); + } + + @ParameterizedTest( name = "{index} {0}") + @MethodSource("getTestData") + void testReportData(String name, TestData test) throws Exception { + Path tempPath = tempDirectory.toPath(); + Path basePath = tempPath.resolve(test.getTestName()); + org.apache.rat.utils.FileUtils.mkDir(basePath.toFile()); + test.setupFiles(basePath); + ArgumentContext ctxt = collectionParser.parseCommands(basePath.toFile(), + test.getCommandLine(basePath.toString())); + if (test.expectingException()) { + assertThatThrownBy(() -> new Reporter(ctxt.getConfiguration()).execute()).as("Expected throws from " + name) + .hasMessageContaining(test.getExpectedException().getMessage()); + ValidatorData data = new ValidatorData(Reporter.Output.builder().configuration(ctxt.getConfiguration()).build(), + basePath.toString()); + test.getValidator().accept(data); + } else { + Reporter.Output output = ctxt.getConfiguration() != null ? new Reporter(ctxt.getConfiguration()).execute() : + Reporter.Output.builder().build(); + ValidatorData data = new ValidatorData(output, basePath.toString()); + data.getOutput().format(data.getConfiguration()); + test.getValidator().accept(data); + } + } + private record LicenseInfo(String id, String family, boolean approval, boolean hasNotes) { LicenseInfo(String id, boolean approval, boolean hasNotes) { this(id, id, approval, hasNotes); diff --git a/apache-rat-core/src/test/java/org/apache/rat/configuration/XMLConfigurationReaderTest.java b/apache-rat-core/src/test/java/org/apache/rat/configuration/XMLConfigurationReaderTest.java index a78632733..949dd1f18 100644 --- a/apache-rat-core/src/test/java/org/apache/rat/configuration/XMLConfigurationReaderTest.java +++ b/apache-rat-core/src/test/java/org/apache/rat/configuration/XMLConfigurationReaderTest.java @@ -29,6 +29,7 @@ import java.net.URL; import java.util.Collection; +import java.util.stream.Collectors; import org.apache.rat.analysis.IHeaderMatcher; import org.apache.rat.config.parameters.ComponentType; import org.apache.rat.config.parameters.Description; @@ -55,6 +56,10 @@ public class XMLConfigurationReaderTest { public static final String[] EXPECTED_LICENSES = { "AL1.0", "AL1.1", "AL2.0", "BSD-3", "DOJO", "TMF", "CDDL1", "ILLUMOS", "GPL1", "GPL2", "GPL3", "MIT", "OASIS", "W3C", "W3CD" }; + public static final String[] APPROVED_LICENSES = { "AL1.0", "AL1.1", "AL2.0", "BSD-3", "DOJO", "TMF", "CDDL1", "ILLUMOS", + "MIT", "OASIS", "W3C", "W3CD" }; + + @Test public void approvedLicenseIdTest() throws URISyntaxException { XMLConfigurationReader reader = new XMLConfigurationReader(); @@ -62,9 +67,8 @@ public void approvedLicenseIdTest() throws URISyntaxException { assertThat(url).isNotNull(); reader.read(url.toURI()); - Collection readCategories = reader.approvedLicenseId(); - - assertArrayEquals(APPROVED_IDS, readCategories.toArray(new String[readCategories.size()])); + Collection actual = reader.approvedLicenseId(); + assertThat(actual).containsExactlyInAnyOrder(APPROVED_IDS); } @Test @@ -80,9 +84,12 @@ public void LicensesTest() throws URISyntaxException { public void LicenseFamiliesTest() throws URISyntaxException { XMLConfigurationReader reader = new XMLConfigurationReader(); URL url = XMLConfigurationReaderTest.class.getResource("/org/apache/rat/default.xml"); + assertNotNull(url); reader.read(url.toURI()); - assertArrayEquals(EXPECTED_IDS, reader.readFamilies().stream().map(x -> x.getFamilyCategory().trim()).toArray(String[]::new)); + Collection actual = reader.readFamilies().stream().map(lf -> lf.getFamilyCategory().trim()) + .collect(Collectors.toList()); + assertThat(actual).containsExactlyInAnyOrder(EXPECTED_IDS); } private void checkMatcher(String name, Class clazz) { @@ -118,7 +125,7 @@ public void descriptionTest() throws SecurityException, URISyntaxException { IHeaderMatcher.Builder builder = MatcherBuilderTracker.getMatcherBuilder("copyright"); Description desc = DescriptionBuilder.buildMap(builder.getClass()); - assertNotNull(desc, () -> "did not build description for 'copyright'"); + assertNotNull(desc, "did not build description for 'copyright'"); assertEquals("copyright", desc.getCommonName()); assertEquals(ComponentType.MATCHER, desc.getType()); assertFalse(desc.isCollection()); diff --git a/apache-rat-core/src/test/java/org/apache/rat/test/AbstractConfigurationOptionsProvider.java b/apache-rat-core/src/test/java/org/apache/rat/test/AbstractConfigurationOptionsProvider.java index 6c4aa2ab5..ef3bdc463 100644 --- a/apache-rat-core/src/test/java/org/apache/rat/test/AbstractConfigurationOptionsProvider.java +++ b/apache-rat-core/src/test/java/org/apache/rat/test/AbstractConfigurationOptionsProvider.java @@ -787,16 +787,23 @@ private void styleSheetTest(final Option option) { // run the test String[] args = {null}; try { - for (String sheet : new String[]{"plain-rat", "missing-headers", "unapproved-licenses", file.getAbsolutePath()}) { + for (String sheet : new String[]{"plain-rat", "missing-headers", "unapproved-licenses", "stylesheet-" + option.getLongOpt()}) { args[0] = sheet; ReportConfiguration config = generateConfig(ImmutablePair.of(option, args)); - try (InputStream expected = StyleSheets.getStyleSheet(sheet).get(); + DocumentName base = DocumentName.builder(baseDir).build(); + DocumentName expectedName = base.resolve(sheet); + try (InputStream expected = StyleSheets.getStyleSheet(sheet, base).ioSupplier().get(); InputStream actual = config.getStyleSheet().get()) { - assertThat(IOUtils.contentEquals(expected, actual)).as(() -> String.format("'%s' does not match", sheet)).isTrue(); + String expectedStr = IOUtils.toString(expected); + String actualStr = IOUtils.toString(actual); + assertThat(actualStr).isEqualTo(expectedStr).as(() -> String.format("'%s' does not match '%s': %s != %s", + config.getStyleSheetDescriptor().name(), + expectedName.getName(), + actualStr, expectedStr)); } } } catch (IOException e) { - fail(e.getMessage()); + fail(e.getMessage(), e); } } @@ -821,7 +828,7 @@ protected void scanHiddenDirectoriesTest() { protected void xmlTest() { try { ReportConfiguration config = generateConfig(ImmutablePair.of(Arg.OUTPUT_STYLE.find("xml"), null)); - try (InputStream expected = StyleSheets.getStyleSheet("xml").get(); + try (InputStream expected = StyleSheets.getStyleSheet("xml", null).ioSupplier().get(); InputStream actual = config.getStyleSheet().get()) { assertThat(IOUtils.contentEquals(expected, actual)).as("'xml' does not match").isTrue(); } diff --git a/apache-rat-core/src/test/java/org/apache/rat/test/AbstractOptionsProvider.java b/apache-rat-core/src/test/java/org/apache/rat/test/AbstractOptionsProvider.java index f4a188099..1f55d2ba0 100644 --- a/apache-rat-core/src/test/java/org/apache/rat/test/AbstractOptionsProvider.java +++ b/apache-rat-core/src/test/java/org/apache/rat/test/AbstractOptionsProvider.java @@ -170,7 +170,7 @@ public static String[] extractArgs(List> args) { } protected File writeFile(final String name, final Iterable lines) { - return org.apache.rat.testhelpers.FileUtils.writeFile(baseDir, name, lines); + return org.apache.rat.utils.FileUtils.writeFile(baseDir, name, lines); } final protected DocumentName mkDocName(final String name) { diff --git a/apache-rat-core/src/test/java/org/apache/rat/testhelpers/BaseOption.java b/apache-rat-core/src/test/java/org/apache/rat/testhelpers/BaseOption.java new file mode 100644 index 000000000..4a22d9d7e --- /dev/null +++ b/apache-rat-core/src/test/java/org/apache/rat/testhelpers/BaseOption.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you under the Apache License, Version 2.0 (the * + * "License"); you may not use this file except in compliance * + * with the License. You may obtain a copy of the License at * + * * + * https://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, * + * software distributed under the License is distributed on an * + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * + * KIND, either express or implied. See the License for the * + * specific language governing permissions and limitations * + * under the License. * + */ +package org.apache.rat.testhelpers; + +import org.apache.commons.cli.Option; +import org.apache.rat.ui.ArgumentTracker; +import org.apache.rat.ui.UIOption; +import org.apache.rat.ui.UIOptionCollection; +import org.apache.rat.utils.CasedString; + +public final class BaseOption extends UIOption { + BaseOption(final UIOptionCollection collection, Option option) { + super(collection, option, new CasedString(CasedString.StringCase.KEBAB, ArgumentTracker.extractKey(option))); + } + protected String cleanupName(Option option) { + return ArgumentTracker.extractKey(option); + } + + public String getExample() { + return ""; + } + + public String getText() { + return ""; + } +} diff --git a/apache-rat-core/src/test/java/org/apache/rat/testhelpers/BaseOptionCollection.java b/apache-rat-core/src/test/java/org/apache/rat/testhelpers/BaseOptionCollection.java new file mode 100644 index 000000000..18b507103 --- /dev/null +++ b/apache-rat-core/src/test/java/org/apache/rat/testhelpers/BaseOptionCollection.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you under the Apache License, Version 2.0 (the * + * "License"); you may not use this file except in compliance * + * with the License. You may obtain a copy of the License at * + * * + * https://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, * + * software distributed under the License is distributed on an * + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * + * KIND, either express or implied. See the License for the * + * specific language governing permissions and limitations * + * under the License. * + */ +package org.apache.rat.testhelpers; + +import org.apache.commons.cli.Option; +import org.apache.rat.ui.UIOptionCollection; + +public final class BaseOptionCollection extends UIOptionCollection { + private BaseOptionCollection(Builder builder) { + super(builder); + } + + public static Builder builder() { + return new Builder(); + } + + public static final class Builder extends UIOptionCollection.Builder { + private Builder() { + super(BaseOption::new); + } + + public BaseOptionCollection build() { + return new BaseOptionCollection(this); + } + } +} diff --git a/apache-rat-core/src/test/java/org/apache/rat/testhelpers/TestingLog.java b/apache-rat-core/src/test/java/org/apache/rat/testhelpers/TestingLog.java index 171cacacd..0298eb52c 100644 --- a/apache-rat-core/src/test/java/org/apache/rat/testhelpers/TestingLog.java +++ b/apache-rat-core/src/test/java/org/apache/rat/testhelpers/TestingLog.java @@ -26,6 +26,7 @@ public class TestingLog implements Log { private StringBuilder captured = new StringBuilder(); + private Log.Level level = Log.Level.INFO; /** * Clears the captured buffer @@ -86,12 +87,18 @@ public void assertNotContainsPattern(String pattern) { @Override public Level getLevel() { - return Level.DEBUG; + return level; + } + + @Override + public void setLevel(Level level) { + this.level = level; } @Override public void log(Level level, String msg) { - captured.append(String.format("%s: %s%n", level, msg)); + if (isEnabled(level)) + captured.append(String.format("%s: %s%n", level, msg)); } /** diff --git a/apache-rat-core/src/test/java/org/apache/rat/testhelpers/data/AbstractTestDataProvider.java b/apache-rat-core/src/test/java/org/apache/rat/testhelpers/data/AbstractTestDataProvider.java new file mode 100644 index 000000000..6bc5682f8 --- /dev/null +++ b/apache-rat-core/src/test/java/org/apache/rat/testhelpers/data/AbstractTestDataProvider.java @@ -0,0 +1,198 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you under the Apache License, Version 2.0 (the * + * "License"); you may not use this file except in compliance * + * with the License. You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, * + * software distributed under the License is distributed on an * + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * + * KIND, either express or implied. See the License for the * + * specific language governing permissions and limitations * + * under the License. * + */ +package org.apache.rat.testhelpers.data; + + +import com.google.common.collect.ImmutableList; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.stream.Stream; +import org.apache.commons.cli.Option; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.rat.OptionCollectionParser; +import org.apache.rat.commandline.Arg; +import org.apache.rat.ui.UIOptionCollection; +import org.apache.rat.ui.ArgumentTracker; +import org.apache.rat.utils.DefaultLog; + +/** + * Generates a list of TestData for executing the Report. + * Use of this interface ensures consistent testing across the UIs. Each method + * tests an Option from {@link OptionCollectionParser} that must be implemented in the UI. + */ +public abstract class AbstractTestDataProvider { + + /** The list of exclude args */ + static final String[] EXCLUDE_ARGS = {"*.foo", "%regex[[A-Z]\\.bar]", "justbaz"}; + /** the list of include args */ + static final String[] INCLUDE_ARGS = {"B.bar", "justbaz"}; + public final ImmutableList> NO_OPTIONS = ImmutableList.of(ImmutablePair.nullPair()); + + /** + * Generates a map of TestData indexed by the testName + * @param optionCollection the collection of options for the UI under test. + * @return the map of testName to Test Data. + */ + public final Map getOptionTestMap(final UIOptionCollection optionCollection) { + Map map = new TreeMap<>(); + for (TestData test : getOptionTests(optionCollection)) { + map.put(test.getTestName(), test); + } + return map; + } + + /** + * Generates a list of test data for Option testing. + * This is different from UI testing as this is to test + * that the command line is properly parsed into a configuration. + * @param optionCollection the collection of options for the UI under test. + * @return a set of TestData for the tests. + */ + public final Set getOptionTests(final UIOptionCollection optionCollection) { + // the optionCollection establishes any changes to the Arg values. + List