diff --git a/apache-rat-core/pom.xml b/apache-rat-core/pom.xml index 21f0c583c..061670044 100644 --- a/apache-rat-core/pom.xml +++ b/apache-rat-core/pom.xml @@ -263,5 +263,10 @@ jimfs test + + org.xmlunit + xmlunit-assertj3 + test + 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..0a46a22f9 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 @@ -68,7 +68,8 @@ ReportConfiguration configuration = OptionCollection.parseCommands(src, myArgs, assertNotNull(configuration) configuration.validate(DefaultLog.getInstance().&error) Reporter reporter = new Reporter(configuration) -ClaimStatistic statistic = reporter.execute() +Reporter.Output output = reporter.execute() +ClaimStatistic statistic = output.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..4346951d2 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 @@ -51,22 +51,22 @@ public static void main(final String[] args) throws Exception { 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 output = new Reporter(configuration).execute(); + output.format(configuration); + output.writeSummary(DefaultLog.getInstance().asWriter()); if (configuration.getClaimValidator().hasErrors()) { - configuration.getClaimValidator().logIssues(reporter.getClaimsStatistic()); + configuration.getClaimValidator().logIssues(output.getStatistic()); throw new RatDocumentAnalysisException(format("Issues with %s", String.join(", ", - configuration.getClaimValidator().listIssues(reporter.getClaimsStatistic())))); + configuration.getClaimValidator().listIssues(output.getStatistic())))); } } } /** * Prints the usage message on {@code System.out}. - * @param opts The defined options. + * @param opts the defined options. */ private static void printUsage(final Options opts) { new Help(System.out).printUsage(opts); 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 579095824..a17077e8c 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 @@ -35,19 +35,24 @@ 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 java.util.stream.Stream; import org.apache.commons.collections4.set.UnmodifiableSortedSet; 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; @@ -56,12 +61,20 @@ import org.apache.rat.license.ILicenseFamily; import org.apache.rat.license.LicenseSetFactory; import org.apache.rat.license.LicenseSetFactory.LicenseFilter; +import org.apache.rat.report.RatReport; import org.apache.rat.report.Reportable; +import org.apache.rat.report.claim.ClaimStatistic; +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.utils.StandardXmlFactory; import org.apache.rat.walker.FileListWalker; import org.apache.rat.walker.ReportableListWalker; +import org.w3c.dom.Node; +import org.xml.sax.SAXException; + +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; /** * A configuration object is used by the front end to invoke the @@ -87,7 +100,7 @@ public enum Processing { ABSENCE("List licenses found and any unknown licences"); /** - * Description of the processing + * Description of the processing. */ private final String description; @@ -111,19 +124,23 @@ public String desc() { * {@code true} if we are adding license headers to the files. */ private boolean addingLicenses; + /** * {@code true} if we are adding license headers in place (no *.new files) */ private boolean addingLicensesForced; + /** * The copyright message to add if we are adding headers. Will be {@code null} * if we are not adding copyright messages. */ private String copyrightMessage; + /** * The IODescriptor that provides the output stream to write the report to. */ private IODescriptor out; + /** * The IODescriptor that provides the stylesheet to style the XML output. */ @@ -140,7 +157,7 @@ public String desc() { private final List reportables; /** - * A predicate to test if a path should be included in the processing. + * The exclusion processor that determines if a file is included or excluded. */ private final ExclusionProcessor exclusionProcessor; @@ -148,22 +165,27 @@ public String desc() { * The default filter for displaying families. */ private LicenseFilter listFamilies; + /** * The default filter for displaying licenses. */ private LicenseFilter listLicenses; + /** * {@code true} if this is a dry run and no processing is to take place. */ private boolean dryRun; + /** * How to process ARCHIVE document types. */ private Processing archiveProcessing; + /** * How to process STANDARD document types. */ private Processing standardProcessing; + /** * The ClaimValidator to validate min/max counts and similar claims. */ @@ -183,6 +205,10 @@ public ReportConfiguration() { reportables = new ArrayList<>(); } + public SerDes serDes() { + return new SerDes(); + } + /** * Report the excluded files to the appendable object. * @param appendable the appendable object to write to. @@ -242,6 +268,16 @@ public ReportableListWalker.Builder getSources() { return builder; } + // for testing access + Iterable sources() { + return sources; + } + + // for testing access + Stream reportables() { + return reportables.stream().map(Reportable::name); + } + /** * Gets the matcher that matches generated text. * @return the matcher that matches generated text. @@ -267,16 +303,16 @@ public void setArchiveProcessing(final Processing archiveProcessing) { } /** - * Retrieves the archive processing type. - * @return the archive processing type. + * Retrieves the standard processing type. + * @return the standard processing type. */ public Processing getStandardProcessing() { return standardProcessing == null ? Defaults.STANDARD_PROCESSING : standardProcessing; } /** - * Sets the archive processing type. If not set will default to NOTIFICATION. - * @param standardProcessing the type of processing archives should have. + * Sets the standard processing type. If not set will default to NOTIFICATION. + * @param standardProcessing the type of processing standard files should have. */ public void setStandardProcessing(final Processing standardProcessing) { this.standardProcessing = standardProcessing; @@ -309,7 +345,7 @@ public void logLicenseCollisions(final Level level) { /** * Sets the reporting option for duplicate licenses. - * @param state the ReportingSt.Option to use for reporting. + * @param state the ReportingSet.Option to use for reporting. */ public void licenseDuplicateOption(final ReportingSet.Options state) { licenseSetFactory.licenseDuplicateOption(state); @@ -424,6 +460,14 @@ public void addIncludedFilter(final FileFilter fileFilter) { exclusionProcessor.addIncludedMatcher(new DocumentNameMatcher(fileFilter)); } + /** + * Includes files that match a DocumentNameMatcher. + * @param matcher the DocumentNameMatcher to match. + */ + public void addIncludedMatcher(final DocumentNameMatcher matcher) { + exclusionProcessor.addIncludedMatcher(matcher); + } + /** * Add file patterns that are to be included. These patterns override any exclusion of * the same files. @@ -442,6 +486,11 @@ public DocumentNameMatcher getDocumentExcluder(final DocumentName baseDir) { return exclusionProcessor.getNameMatcher(baseDir); } + // visible for testing. + ExclusionProcessor getExclusionProcessor() { + return exclusionProcessor; + } + /** * Gets the IOSupplier with the style sheet. * @return the Supplier of the InputStream that is the XSLT style sheet to style @@ -523,7 +572,7 @@ public void setStyleSheet(final URL styleSheet) { * appended to and that can be closed. If an {@code OutputStream} should not be * closed consider wrapping it in a {@code CloseShieldOutputStream} * @param out the OutputStream supplier that provides the output stream to write - * the report to. A null value will use System.out. + * the report to. A {@code null} value will use {@code System.out}. * @see CloseShieldOutputStream */ public void setOut(final IODescriptor out) { @@ -546,6 +595,7 @@ public void setOut(final File file) { DefaultLog.getInstance().warn("Unable to delete file: " + file); } } + File parent = file.getParentFile(); if (!parent.mkdirs() && !parent.isDirectory()) { DefaultLog.getInstance().warn("Unable to create directory: " + file.getParentFile()); @@ -645,7 +695,7 @@ public void addApprovedLicenseCategory(final ILicenseFamily approvedILicenseFami } /** - * Adds a license family category (id) to the list of approved licenses + * Adds a license family category (id) to the list of approved licenses. * @param familyCategory the category to add. */ public void addApprovedLicenseCategory(final String familyCategory) { @@ -827,8 +877,8 @@ public void setAddLicenseHeaders(final AddLicenseHeaders addLicenseHeaders) { *
  • {@code approved} - Only approved license families will be returned.
  • *
  • {@code none} - No license families will be returned.
  • * - * @param filter The license filter. - * @return The set of defined licenses. + * @param filter the license filter. + * @return the set of defined licenses. */ public SortedSet getLicenseFamilies(final LicenseFilter filter) { return licenseSetFactory.getLicenseFamilies(filter); @@ -871,7 +921,7 @@ public void validate(final Consumer logger) { /** * An IODescriptor comprises a name and an IOSupplier. The name should identify the contents of the stream. * @param name the name of the supplier. - * @param ioSupplier the IOSupplier that provides either an InputStream or an OutputStream + * @param ioSupplier the IOSupplier that provides either an InputStream or an OutputStream. * @param either InputStream or OutputStream. */ public record IODescriptor(String name, IOSupplier ioSupplier) { @@ -907,4 +957,131 @@ static IODescriptor input(final File file) { return new IODescriptor<>(file.toString(), () -> new FileInputStream(file)); } } + + /** + * Serializes the ReportConfiguration into an XML document that can be deserialized by the Serde. + * Deserialized ReportConfigurations can not be executed as the reportable objects use named placeholders + * and do not have access to the original object. + */ + @SuppressFBWarnings("EI_EXPOSE_REP2") + public class SerDes { + /** + * 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 (XmlWriter writer = new XmlWriter(appendable)) { + writer.startElement("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.startElement("copyrightMessage").content(copyrightMessage).closeElement(); + } + writer.startElement("sources"); + for (File f : sources) { + writer.startElement("source").attribute("name", f.toString()).closeElement(); + } + writer.closeElement("sources").startElement("reportables"); + for (Reportable reportable : reportables) { + writer.startElement("reportable") + .attribute("baseName", reportable.name().getBaseName()) + .attribute("name", reportable.name().toString()) + .attribute("class", reportable.getClass().getName()).closeElement(); + } + writer.closeElement(); + + exclusionProcessor.serDes().serialize(writer); + + writer.startElement("claimValidator"); + for (ClaimStatistic.Counter counter : ClaimStatistic.Counter.values()) { + writer.startElement("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 { + org.w3c.dom.Document document; + try (InputStream stream = inputStreamSupplier.get()) { + document = StandardXmlFactory.documentBuilder().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); + addingLicenses = Boolean.parseBoolean(attributes.get("addingLicenses")); + 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); + } + 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("copyrightMessage"), + lNode -> setCopyrightMessage(lNode.getTextContent())); + + XMLConfigurationReader.nodeListConsumer(document.getElementsByTagName("source"), lNode -> { + Map nAttributes = XMLConfigurationReader.attributes(lNode); + addSource(new File(nAttributes.get("name"))); + }); + + // Deserialize the reportables. + 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.serDes().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"))); + }); + } + } + + /** + * A record that identifies a deserialized reportable. Deserialized reportables are not executable. + * @param name the name of the reportable. + */ + private record DeserializedReportable(DocumentName name) implements Reportable { + @Override + public void run(final RatReport report) throws RatException { + throw new RatException("Attempt to run a deserialized reportable"); + } + } } 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 5cf396d9c..c752db924 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,14 +18,14 @@ */ 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.transform.TransformerException; @@ -34,6 +34,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; @@ -41,6 +42,8 @@ import org.apache.rat.report.xml.writer.XmlWriter; import org.apache.rat.utils.StandardXmlFactory; 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 @@ -48,18 +51,21 @@ */ public class Reporter { - /** Format used for listing licenses. */ + /** + * 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 */ + /** + * The configuration for the report. + */ private final ReportConfiguration configuration; + /** + * The output from the execution. + */ + private Output output; + /** * Create the reporter. * @@ -71,112 +77,237 @@ 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 (XmlWriter 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 = StandardXmlFactory.documentBuilder().parse(inputStream); - } else { - document = StandardXmlFactory.documentBuilder().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 (XmlWriter 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(StandardXmlFactory.documentBuilder().parse(inputSource)); } - } catch (Exception e) { - throw RatException.makeRatException(e); + } else { + builder.document = StandardXmlFactory.documentBuilder().newDocument(); + builder.statistic(new ClaimStatistic()); } + this.output = builder.build(); + return output; + } catch (Exception e) { + throw RatException.makeRatException(e); } - return statistic; } /** - * Get the claim statistics from the run. + * Gets the output from the last {@link #execute} call or {@code null} if {@link #execute} has not been called. * - * @return the claim statistics. + * @return the output */ - public ClaimStatistic getClaimsStatistic() { - return statistic; + public Output getOutput() { + return output; } /** - * Outputs the report using the stylesheet and output specified in the configuration. - * @return the Claim statistic from the run. - * @throws RatException on error. + * The output from a report run. */ - public ClaimStatistic output() throws RatException { - return output(configuration.getStyleSheet(), configuration.getOutput()); - } + 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; - /** - * 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(); - try (OutputStream out = output.get(); - InputStream styleIn = stylesheet.get()) { - StandardXmlFactory.createTransformer(styleIn).transform(new DOMSource(document), - new StreamResult(new OutputStreamWriter(out, StandardCharsets.UTF_8))); - return result; - } catch (TransformerException | IOException e) { - throw new RatException(e); + /** + * 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; } - } - /** - * Lists the licenses on the configured output stream. - * @param configuration The configuration for the system - * @param filter the license filter that specifies which licenses to output. - * @throws IOException if PrintWriter can not be retrieved from configuration. - */ - public static void listLicenses(final ReportConfiguration configuration, final LicenseFilter filter) throws IOException { - try (PrintWriter pw = configuration.getWriter().get()) { - pw.format("Licenses (%s):%n", filter); + 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; + } + + /** + * Get the claim statistics from the run. + * + * @return the claim statistics. + */ + 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 the 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 { + try (OutputStream out = output.get(); + InputStream styleIn = stylesheet.get()) { + StandardXmlFactory.createTransformer(styleIn).transform(new DOMSource(document), + new StreamResult(new OutputStreamWriter(out, StandardCharsets.UTF_8))); + } catch (TransformerException | IOException e) { + throw new RatException(e); + } + } + + /** + * Lists the licenses on the print writer. + * + * @param printWriter the print writer to write to. + * @param filter the license filter that specifies which licenses to output. + */ + public void listLicenses(final PrintWriter printWriter, final LicenseFilter filter) { + printWriter.format("Licenses (%s):%n", filter); configuration.getLicenses(filter) - .forEach(lic -> pw.format(LICENSE_FORMAT, lic.getLicenseFamily().getFamilyCategory(), + .forEach(lic -> printWriter.format(LICENSE_FORMAT, lic.getLicenseFamily().getFamilyCategory(), lic.getLicenseFamily().getFamilyName(), lic.getNote())); - pw.println(); + printWriter.println(); } - } - /** - * 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(getClaimsStatistic().getCounter(counter))) - .append(System.lineSeparator()); + /** + * Lists the licenses on the output specified in the configuration. + * + * @param filter the license filter that specifies which licenses to output. + * @throws IOException if PrintWriter can not be retrieved from configuration. + */ + public void listLicenses(final LicenseFilter filter) throws IOException { + try (PrintWriter pw = configuration.getWriter().get()) { + listLicenses(pw, filter); + } } - } - /** - * Gets the document that was generated during execution. - * @return the document that was generated during execution. - */ - public Document getDocument() { - return document; + /** + * 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) { + File inputFile = workingDirectory.resolve(fileName).asFile(); + try (InputStream inputStream = new FileInputStream(inputFile)) { + this.document = StandardXmlFactory.documentBuilder().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 newStatistic = new ClaimStatistic(); + newStatistic.serDes().deserialize(() -> new FileInputStream(sourceFile)); + this.statistic = newStatistic; + 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.serDes().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/StyleSheets.java b/apache-rat-core/src/main/java/org/apache/rat/commandline/StyleSheets.java index b56e0dde7..ea0c11133 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 @@ -71,8 +71,8 @@ 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 ReportConfiguration.IODescriptor getStyleSheet() { URL url = StyleSheets.class.getClassLoader().getResource(format("org/apache/rat/%s.xsl", name)); @@ -81,9 +81,9 @@ public ReportConfiguration.IODescriptor getStyleSheet() { } /** - * 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. + * @return the IODescriptor for the style sheet. */ public static ReportConfiguration.IODescriptor getStyleSheet(final String name) { URL url = StyleSheets.class.getClassLoader().getResource(format("org/apache/rat/%s.xsl", name)); 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 d7b5862be..d57e21181 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 @@ -20,17 +20,25 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.Collection; 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.XmlWriter; 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,21 +82,66 @@ public ExclusionProcessor() { excludedCollections = new HashSet<>(); } - /** Reset the {@link #lastMatcher} and {@link #lastMatcherBaseDir} to start again */ + public SerDes serDes() { + return new SerDes(); + } + + /* the following set of methods are here for testing purposes */ + Set getExcludedPatterns() { + return new HashSet<>(excludedPatterns); + } + + Collection getExcludedPaths() { + return new ArrayList<>(excludedPaths); + } + + Set getIncludedPatterns() { + return new HashSet<>(includedPatterns); + } + + Collection getIncludedPaths() { + return new ArrayList<>(includedPaths); + } + + Collection getFileProcessors() { + return new HashSet<>(fileProcessors); + } + + Set getIncludedCollections() { + return new HashSet<>(includedCollections); + } + + Set getExcludedCollections() { + return new HashSet<>(excludedCollections); + } + + DocumentNameMatcher getLastMatcher() { + return lastMatcher; + } + + DocumentName getLastMatcherBaseDir() { + return lastMatcherBaseDir; + } + + /** + * Reset the {@link #lastMatcher} and {@link #lastMatcherBaseDir} to start again. + */ private void resetLastMatcher() { lastMatcher = null; lastMatcherBaseDir = null; } /** - * Add the Iterable of strings to the collection of file/directory patters to ignore. - * @param patterns the patterns to add + * Add the iterable of strings to the collection of file/directory patters to ignore. + * @param patterns the patterns to add. * @return this */ public ExclusionProcessor addIncludedPatterns(final Iterable patterns) { + if (patterns != null) { DefaultLog.getInstance().debug(format("Including patterns: %s", String.join(", ", patterns))); patterns.forEach(includedPatterns::add); resetLastMatcher(); + } return this; } @@ -139,9 +192,11 @@ public ExclusionProcessor addIncludedCollection(final StandardCollection collect * @return this */ public ExclusionProcessor addExcludedPatterns(final Iterable patterns) { + if (patterns != null) { DefaultLog.getInstance().debug(format("Excluding patterns: %s", String.join(", ", patterns))); patterns.forEach(excludedPatterns::add); resetLastMatcher(); + } return this; } @@ -177,9 +232,11 @@ public void reportExclusions(final Appendable appendable) throws IOException { for (DocumentNameMatcher nameMatcher : excludedPaths) { appendable.append(format("Excluding %s.%n", nameMatcher.toString())); } + for (DocumentNameMatcher nameMatcher : includedPaths) { + appendable.append(format("Including %s.%n", nameMatcher.toString())); + } } - /** * Excludes the files/directories specified by a StandardCollection. * @param collection the StandardCollection that identifies the files to exclude. @@ -203,7 +260,7 @@ public ExclusionProcessor addExcludedCollection(final StandardCollection collect public DocumentNameMatcher getNameMatcher(final DocumentName basedir) { // if lastMatcher is not set or the basedir is not the same as the last one then // we have to regenerate the matching document. - // Otherwise we can just return the lastMatcher since there is no change. + // Otherwise, we can just return the lastMatcher since there is no change. if (lastMatcher == null || !basedir.equals(lastMatcherBaseDir)) { lastMatcherBaseDir = basedir; @@ -337,4 +394,90 @@ private void extractPaths(final MatcherSet.Builder matcherBuilder) { } } + /** + * Serializes and deserializes the ExclusionProcessor to an XML document. + */ + public class SerDes { + /** The pattern attribute name */ + private static final String PATTERN = "pattern"; + /** THe name attribute name */ + private static final String NAME = "name"; + + /** + * Serialize the ExclusionProcessor to XML writer. + * @param writer the writer to serialize to. + * @throws IOException on Error + */ + public void serialize(final XmlWriter writer) throws IOException { + writer.startElement("ExclusionProcessor"); + + for (String pattern : excludedPatterns) { + writer.startElement("excludedPattern").attribute(PATTERN, pattern).closeElement(); + } + for (StandardCollection obj : excludedCollections) { + writer.startElement("excludedCollection").attribute(NAME, obj.name()).closeElement(); + } + for (DocumentNameMatcher obj : excludedPaths) { + writer.startElement("excludedPath").attribute(NAME, obj.toString()).closeElement(); + } + + for (String pattern : includedPatterns) { + writer.startElement("includedPattern").attribute(PATTERN, pattern).closeElement(); + } + for (StandardCollection obj : includedCollections) { + writer.startElement("includedCollection").attribute(NAME, obj.name()).closeElement(); + } + for (DocumentNameMatcher obj : includedPaths) { + writer.startElement("includedPath").attribute(NAME, obj.toString()).closeElement(); + } + + for (StandardCollection obj : fileProcessors) { + writer.startElement("fileProcessor").attribute(NAME, obj.name()).closeElement(); + } + writer.closeElement(); + } + + /** + * Deserialize from XML Document node to ExclusionProcessor. + * @param xmlNode the node to deserialize from. + */ + public void deserialize(final Node xmlNode) { + final NodeList children = xmlNode.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + Node child = children.item(i); + Map attributes = XMLConfigurationReader.attributes(child); + switch (child.getNodeName()) { + case "excludedPattern" -> + excludedPatterns.add(attributes.get(PATTERN)); + + case "excludedCollection" -> + excludedCollections.add(StandardCollection.valueOf(attributes.get(NAME))); + + case "excludedPath" -> + excludedPaths.add(new DocumentNameMatcher(attributes.get(NAME), + (Predicate) x -> { + throw new NotImplementedException("Deserialized ExclusionProcessor can not evaluate excluded paths"); + })); + + case "includedPattern" -> + includedPatterns.add(attributes.get(PATTERN)); + + case "includedCollection" -> + includedCollections.add(StandardCollection.valueOf(attributes.get(NAME))); + + case "includedPath" -> + includedPaths.add(new DocumentNameMatcher(attributes.get(NAME), + (Predicate) x -> { + throw new NotImplementedException("Deserialized ExclusionProcessor can not evaluate included paths"); + })); + + case "fileProcessor" -> + fileProcessors.add(StandardCollection.valueOf(attributes.get(NAME))); + + default -> + DefaultLog.getInstance().error(String.format("Unknown child node '%s'", child.getNodeName())); + } + } + } + } } 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 ac11bdd5a..ba20fd5fd 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 @@ -123,7 +123,7 @@ public SortedSet licenseFamilies() { /** * Creates textual representation of a node for display. * @param node the node to create the textual representation of. - * @return The textual representation of the node for display. + * @return the textual representation of the node for display. */ private String nodeText(final Node node) { StringBuilder stringBuilder = new StringBuilder().append("<").append(node.getNodeName()); @@ -154,7 +154,7 @@ public void read(final Reader reader) { /** * Read the uris and extract the DOM information to create new objects. - * @param uris The URIs to read. + * @param uris the URIs to read. */ public void read(final URI... uris) { DocumentBuilder builder = StandardXmlFactory.documentBuilder(); @@ -170,10 +170,10 @@ public void read(final URI... uris) { /** * Applies the {@code consumer} to each node in the {@code list}. Generally used for extracting info from a * {@code NodeList}. - * @param list the NodeList to process + * @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)); } @@ -196,10 +196,10 @@ public void add(final Document newDoc) { /** * Get a map of Node attribute names to values. - * @param node The node to process + * @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++) { @@ -211,9 +211,9 @@ private Map attributes(final Node node) { /** * Finds the setter description property in the builder and set it with the value. - * @param desc The description for the setter. - * @param builder The builder to set the value in. - * @param value The value to set. + * @param desc the description for the setter. + * @param builder the builder to set the value in. + * @param value the value to set. */ private void callSetter(final Description desc, final IHeaderMatcher.Builder builder, final Object value) { try { @@ -226,7 +226,7 @@ private void callSetter(final Description desc, final IHeaderMatcher.Builder bui /** * For any children of description that are BUILD_PARAMETERS set the builder property. - * @param description The description for the builder. + * @param description the description for the builder. * @param builder the builder to set the properties in. */ private void processBuilderParams(final Description description, final IHeaderMatcher.Builder builder) { @@ -245,7 +245,7 @@ private void processBuilderParams(final Description description, final IHeaderMa * of the child (if any) to the BiPredicate. If there is not a child description * for the node it is ignored. If the node is processed it is removed from list * of children. - * @param description the Description of the node being processed + * @param description the description of the node being processed. * @param children the child nodes of that node. * @param childProcessor the function that handles the processing of the child * node. @@ -266,7 +266,7 @@ private void processChildren(final Description description, final List chi /** * Creates a child node processor for the builder described by the description. - * @param builder The builder to set properties in. + * @param builder the builder to set properties in. * @param description the description of the builder. * @return child node. */ @@ -315,11 +315,11 @@ private void setValue(final Description description, final Description childDesc * Process the ELEMENT_NODEs children of the parent whose names match child * descriptions. All children of children are processed with the childProcessor. If * the childProcessor handles the node it is not included in the resulting list. - * @param description the Description of the parent node. - * @param parent the node being processed + * @param description the description of the parent node. + * @param parent the node being processed. * @param childProcessor the BiProcessor to handle process each child. If the * processor handles the child it must return {@code true}. - * @return A Pair comprising a boolean flag indicating children were found, and + * @return a Pair comprising a boolean flag indicating children were found, and * a list of all child nodes that were not processed by the childProcessor. */ private Pair> processChildNodes(final Description description, final Node parent, @@ -350,7 +350,7 @@ private Pair> processChildNodes(final Description descriptio /** * Creates a Builder from a Matcher node. * @param matcherNode the Matcher node to parse. - * @return The Builder for the matcher described by the node. + * @return the Builder for the matcher described by the node. */ private AbstractBuilder parseMatcher(final Node matcherNode) { final AbstractBuilder builder = MatcherBuilderTracker.getMatcherBuilder(matcherNode.getNodeName()); @@ -391,7 +391,6 @@ private AbstractBuilder parseMatcher(final Node matcherNode) { iter.remove(); } } - } else { processChildren(description, children, (child, childD) -> { if (childD.getChildType().equals(description.getChildType())) { @@ -401,7 +400,6 @@ private AbstractBuilder parseMatcher(final Node matcherNode) { return false; }); } - } if (!children.isEmpty()) { @@ -421,7 +419,7 @@ private BiPredicate licenseChildNodeProcessor(final ILicense. switch (childDescription.getType()) { case LICENSE: throw new ConfigurationException(String.format( - "%s may not be enclosed in another license. %s '%s' found in '%s'", childDescription.getType(), + "%s may not be enclosed in another license. %s '%s' found in '%s'", childDescription.getType(), childDescription.getType(), childDescription.getCommonName(), description.getCommonName())); case BUILD_PARAMETER: break; 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..7279e3e81 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,24 @@ 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 org.apache.commons.io.function.IOSupplier; import org.apache.commons.lang3.StringUtils; import org.apache.rat.api.Document; +import org.apache.rat.configuration.XMLConfigurationReader; +import org.apache.rat.report.xml.writer.XmlWriter; +import org.apache.rat.utils.StandardXmlFactory; +import org.xml.sax.SAXException; /** * This class provides a numerical overview about @@ -100,7 +110,7 @@ public int getDefaultMinValue() { * @return displayName of the counter, capitalized and without underscores. */ public String displayName() { - return StringUtils.capitalize(name().replaceAll("_", " ").toLowerCase(Locale.ROOT)); + return StringUtils.capitalize(name().replace("_", " ").toLowerCase(Locale.ROOT)); } } @@ -113,8 +123,11 @@ public String displayName() { /** Map of counter type to value */ private final ConcurrentHashMap counterMap = new ConcurrentHashMap<>(); + public SerDes serDes() { + return new SerDes(); + } /** - * Converts null counter to 0. + * Converts {@code null} counter to 0. * * @param counter the Counter to retrieve the value from. * @return 0 if counter is {@code null} or counter value otherwise. @@ -141,6 +154,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. @@ -168,24 +190,12 @@ public List getDocumentTypes() { public void incCounter(final Document.Type documentType, final int value) { documentTypeMap.compute(documentType, (k, v) -> updateCounter(Counter.DOCUMENT_TYPES, v, value)); switch (documentType) { - case STANDARD: - incCounter(Counter.STANDARDS, value); - break; - case ARCHIVE: - incCounter(Counter.ARCHIVES, value); - break; - case BINARY: - incCounter(Counter.BINARIES, value); - break; - case NOTICE: - incCounter(Counter.NOTICES, value); - break; - case UNKNOWN: - incCounter(Counter.UNKNOWN, value); - break; - case IGNORED: - incCounter(Counter.IGNORED, value); - break; + case STANDARD -> incCounter(Counter.STANDARDS, value); + case ARCHIVE -> incCounter(Counter.ARCHIVES, value); + case BINARY -> incCounter(Counter.BINARIES, value); + case NOTICE -> incCounter(Counter.NOTICES, value); + case UNKNOWN -> incCounter(Counter.UNKNOWN, value); + case IGNORED -> incCounter(Counter.IGNORED, value); } } @@ -208,10 +218,10 @@ public int getLicenseNameCount(final String licenseName) { } /** - * Updates the intCounter with the value and if the intCounter was null creates a new one and registers the + * Updates the intCounter with the value and if the intCounter was {@code null} creates a new one and registers the * creation as a counter type. * @param counter the Type of the counter. - * @param intCounter the IntCounter to update. May be null. + * @param intCounter the IntCounter to update. May be {@code null}. * @param value the value to add to the int counter. * @return the intCounter if it was not {@code null}, a new IntCounter otherwise. */ @@ -288,5 +298,105 @@ public IntCounter increment(final int count) { public int value() { return value; } + + @Override + public String toString() { + return String.valueOf(value); + } + } + + /** + * Serialize and deserialize the claim Statistic. + */ + public class SerDes { + /** The count attribute string. */ + private static final String COUNT = "count"; + /** The name attribute string. */ + private static final String NAME = "name"; + + /** + * Serializes the claim statistic into an appendable. + * @param appendable the appendable to write to. + * @throws IOException on error. + */ + public void serialize(final Appendable appendable) throws IOException { + try (XmlWriter writer = new XmlWriter(appendable)) { + writer.startDocument().startElement("ClaimStatistic") + .startElement("licenseNameMap"); + for (Map.Entry entry : licenseNameMap.entrySet()) { + if (entry.getValue().value > 0) { + writer.startElement("licenseName") + .attribute(COUNT, entry.getValue().toString()) + .attribute(NAME, entry.getKey()).closeElement(); + } + } + writer.closeElement() + .startElement("licenseFamilyCategoryMap"); + for (Map.Entry entry : licenseFamilyCategoryMap.entrySet()) { + if (entry.getValue().value > 0) { + writer.startElement("familyCategory") + .attribute(COUNT, entry.getValue().toString()) + .attribute(NAME, entry.getKey()).closeElement(); + } + } + writer.closeElement() + .startElement("documentTypeMap"); + for (Map.Entry entry : documentTypeMap.entrySet()) { + if (entry.getValue().value > 0) { + writer.startElement("documentType") + .attribute(COUNT, entry.getValue().toString()) + .attribute(NAME, entry.getKey().name()).closeElement(); + } + } + writer.closeElement() + .startElement("counterMap"); + for (Map.Entry entry : counterMap.entrySet()) { + if (entry.getValue().value > 0) { + writer.startElement("counter") + .attribute(COUNT, entry.getValue().toString()) + .attribute(NAME, entry.getKey().name()).closeElement(); + } + } + writer.closeElement(); + } + } + + /** + * Deserializes a ClaimStatistic from an input stream. + * @param inputStreamSupplier the supplier of the input stream to deserialize from. + * @throws IOException on error. + */ + public void deserialize(final IOSupplier inputStreamSupplier) throws IOException { + DocumentBuilder builder = StandardXmlFactory.documentBuilder(); + 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/main/java/org/apache/rat/utils/StandardXmlFactory.java b/apache-rat-core/src/main/java/org/apache/rat/utils/StandardXmlFactory.java index 3ec9849a7..b6a0339e8 100644 --- a/apache-rat-core/src/main/java/org/apache/rat/utils/StandardXmlFactory.java +++ b/apache-rat-core/src/main/java/org/apache/rat/utils/StandardXmlFactory.java @@ -18,7 +18,12 @@ */ package org.apache.rat.utils; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; import java.io.InputStream; +import java.io.StringWriter; +import java.nio.charset.StandardCharsets; import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilder; @@ -27,9 +32,17 @@ import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; +import org.apache.commons.lang3.StringUtils; +import org.w3c.dom.Document; + +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + /** * Factory to create standard XML objects. The intention of this class is to resolve in a consistent manner the * XXE errors and similar XML IO errors. @@ -62,6 +75,7 @@ public static Transformer createTransformer() throws TransformerConfigurationExc * @return the transformer. * @throws TransformerConfigurationException on error. */ + @SuppressFBWarnings("MALICIOUS_XSLT") public static Transformer createTransformer(final InputStream styleIn) throws TransformerConfigurationException { TransformerFactory factory = TransformerFactory.newInstance(); // NOSONAR factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); @@ -94,4 +108,29 @@ public static DocumentBuilder documentBuilder() { throw new IllegalStateException("No XML parser defined", e); } } + + /** + * Write an XML document to a file. + * @param document the document to write. + * @param file the file to write to. + */ + public static void writeDocument(final Document document, final File file) throws IOException, TransformerException { + DOMSource source = new DOMSource(document); + FileWriter writer = new FileWriter(file, StandardCharsets.UTF_8); + StreamResult result = new StreamResult(writer); + createTransformer().transform(source, result); + } + + /** + * Write an XML document to a file. + * @param document the document to write. + */ + public static String serializeDocument(final Document document) throws TransformerException { + DOMSource source = new DOMSource(document); + StringWriter writer = new StringWriter(); + StreamResult sink = new StreamResult(writer); + createTransformer().transform(source, sink); + String result = writer.toString(); + return StringUtils.defaultIfBlank(result, ""); + } } diff --git a/apache-rat-core/src/test/java/org/apache/rat/OutputTest.java b/apache-rat-core/src/test/java/org/apache/rat/OutputTest.java new file mode 100644 index 000000000..f523f55e3 --- /dev/null +++ b/apache-rat-core/src/test/java/org/apache/rat/OutputTest.java @@ -0,0 +1,282 @@ +/* + * 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; + +import org.apache.commons.io.function.IOSupplier; +import org.apache.rat.api.RatException; +import org.apache.rat.commandline.StyleSheets; +import org.apache.rat.config.AddLicenseHeaders; +import org.apache.rat.config.exclusion.StandardCollection; +import org.apache.rat.config.results.ClaimValidator; +import org.apache.rat.document.DocumentName; +import org.apache.rat.document.DocumentNameMatcher; +import org.apache.rat.document.FileDocument; +import org.apache.rat.license.LicenseSetFactory; +import org.apache.rat.report.claim.ClaimStatistic; +import org.apache.rat.report.claim.ClaimStatisticTest; +import org.apache.rat.test.utils.Resources; +import org.apache.rat.testhelpers.FileUtils; +import org.apache.rat.utils.StandardXmlFactory; +import org.apache.rat.utils.StandardXmlFactoryTest; +import org.apache.rat.walker.DirectoryWalker; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.w3c.dom.Document; +import org.xml.sax.SAXException; +import org.xmlunit.assertj3.XmlAssert; + +import javax.xml.transform.TransformerException; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class OutputTest { + + static Path tempPath; + + @BeforeAll + static void setup() throws IOException { + tempPath = Files.createTempDirectory("outputTest").toAbsolutePath(); + } + + @AfterAll + static void teardown() { + FileUtils.delete(tempPath.toFile()); + } + + @Test + void documentReadingTest() throws IOException, SAXException, TransformerException { + Path testPath = tempPath.resolve("documentReading"); + File testFile = testPath.toFile(); + FileUtils.mkDir(testFile); + DocumentName workingDirectory = DocumentName.builder(testPath.toFile()).setBaseName(testFile).build(); + Document document; + DocumentName documentFile = workingDirectory.resolve("document.xml"); + try (InputStream inputStream = OutputTest.class.getClassLoader().getResourceAsStream("XmlOutputExamples/elements.xml")) { + document = StandardXmlFactory.documentBuilder().parse(inputStream); + StandardXmlFactory.writeDocument(document, documentFile.asFile()); + } + + Reporter.Output.Builder builder = Reporter.Output.builder().document(documentFile.getName(), workingDirectory); + + Reporter.Output output = builder.build(); + XmlAssert.assertThat(output.getDocument()).and(document) + .ignoreWhitespace() + .areIdentical(); + } + + @Test + void statisticReadingTest() throws IOException { + Path testPath = tempPath.resolve("statisticReading"); + File testFile = testPath.toFile(); + FileUtils.mkDir(testFile); + DocumentName workingDirectory = DocumentName.builder(testPath.toFile()).setBaseName(testFile).build(); + DocumentName documentFile = workingDirectory.resolve("statistic.xml"); + + ClaimStatistic underTest = new ClaimStatistic(); + underTest.incLicenseCategoryCount("familyCategory", 1); + underTest.incCounter(ClaimStatistic.Counter.APPROVED, 2); + underTest.incCounter(org.apache.rat.api.Document.Type.IGNORED, 3); + underTest.incLicenseNameCount("licenseName", 4); + + ClaimStatistic.SerDes serDes = underTest.serDes(); + StringWriter stringWriter = new StringWriter(); + serDes.serialize(stringWriter); + try (FileOutputStream fos = new FileOutputStream(documentFile.asFile())) { + fos.write(stringWriter.toString().getBytes(StandardCharsets.UTF_8)); + } + + Reporter.Output.Builder builder = Reporter.Output.builder().statistic(documentFile.getName(), workingDirectory); + + Reporter.Output output = builder.build(); + ClaimStatisticTest.assertSame(output.getStatistic(), underTest); + } + + @Test + void readingBadFileTest() { + Path testPath = tempPath.resolve("readingBadFileTest"); + File testFile = testPath.toFile(); + FileUtils.mkDir(testFile); + DocumentName workingDirectory = DocumentName.builder(testPath.toFile()).setBaseName(testFile).build(); + DocumentName documentFile = workingDirectory.resolve("missing.file"); + String name = documentFile.getName(); + Reporter.Output.Builder builder = Reporter.Output.builder(); + assertThatThrownBy(() -> builder.statistic(name, workingDirectory)) + .as("statistic read") + .isInstanceOf(ConfigurationException.class) + .hasMessageContaining("Unable to read file: " + testPath.resolve("missing.file")); + + assertThatThrownBy(() -> builder.configuration(name, workingDirectory)) + .as("configuration read") + .isInstanceOf(ConfigurationException.class) + .hasMessageContaining("Unable to read file: " + testPath.resolve("missing.file")); + + assertThatThrownBy(() -> builder.document(name, workingDirectory)) + .as("document read") + .isInstanceOf(ConfigurationException.class) + .hasMessageContaining("Unable to read file: " + testPath.resolve("missing.file")); + } + + @Test + void configurationReadingTest() throws IOException { + Path testPath = tempPath.resolve("configurationReading"); + File testFile = testPath.toFile(); + FileUtils.mkDir(testFile); + DocumentName workingDirectory = DocumentName.builder(testPath.toFile()).setBaseName(testFile).build(); + DocumentName documentFile = workingDirectory.resolve("configuration.xml"); + + ReportConfiguration underTest = new ReportConfiguration(); + underTest.setAddLicenseHeaders(AddLicenseHeaders.FORCED); + underTest.listFamilies(LicenseSetFactory.LicenseFilter.APPROVED); + underTest.listLicenses(LicenseSetFactory.LicenseFilter.ALL); + underTest.setDryRun(true); + underTest.setArchiveProcessing(ReportConfiguration.Processing.NOTIFICATION); + underTest.setStandardProcessing(ReportConfiguration.Processing.ABSENCE); + underTest.setStyleSheet(StyleSheets.MISSING_HEADERS.getStyleSheet()); + underTest.setOut(new File("/some/file/somewhere")); + underTest.setCopyrightMessage("the copyright message"); + underTest.addSource(new File("/my/file")); + underTest.addSource(new ReportConfigurationTest.TestingReportable()); + underTest.addExcludedPatterns(List.of("pattern/**", "pattern2/**")); + underTest.addExcludedCollection(StandardCollection.BAZAAR); + underTest.addExcludedCollection(StandardCollection.MISC); + underTest.addExcludedFileProcessor(StandardCollection.HIDDEN_FILE); + underTest.addExcludedMatcher(DocumentNameMatcher.MATCHES_ALL); + underTest.addIncludedPatterns(List.of("**/pattern3", "**/pattern4")); + underTest.addIncludedCollection(StandardCollection.ARCH); + underTest.addIncludedCollection(StandardCollection.BITKEEPER); + underTest.addIncludedMatcher(DocumentNameMatcher.MATCHES_NONE); + + ClaimValidator claimValidator = underTest.getClaimValidator(); + claimValidator.setMax(ClaimStatistic.Counter.APPROVED, 5); + claimValidator.setMin(ClaimStatistic.Counter.APPROVED, 3); + claimValidator.setMax(ClaimStatistic.Counter.ARCHIVES, 10); + claimValidator.setMin(ClaimStatistic.Counter.BINARIES, 4); + + StringWriter stringWriter = new StringWriter(); + underTest.serDes().serialize(stringWriter); + + try (FileOutputStream fos = new FileOutputStream(documentFile.asFile())) { + fos.write(stringWriter.toString().getBytes(StandardCharsets.UTF_8)); + } + + Reporter.Output.Builder builder = Reporter.Output.builder().configuration(documentFile.getName(), workingDirectory); + + Reporter.Output output = builder.build(); + ReportConfigurationTest.assertSame(output.getConfiguration(), underTest); + } + + private ReportConfiguration initializeConfiguration() throws URISyntaxException { + Defaults defaults = Defaults.builder().build(); + final File elementsFile = Resources.getExampleResource("exampleData"); + final ReportConfiguration configuration = new ReportConfiguration(); + configuration.setFrom(defaults); + DocumentName documentName = DocumentName.builder(elementsFile).build(); + configuration.addSource(new DirectoryWalker(new FileDocument(documentName, elementsFile, + configuration.getDocumentExcluder(documentName)))); + return configuration; + } + + @Test + void listLicensesReportTest() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ReportConfiguration configuration = initializeConfiguration(); + configuration.setOut(new ReportConfiguration.IODescriptor<>("listLicensesReportTest", () -> out)); + configuration.setStyleSheet(StyleSheets.UNAPPROVED_LICENSES.getStyleSheet()); + Reporter.Output output = Reporter.Output.builder() + .statistic(new ClaimStatistic()) + .document(null) + .configuration(configuration) + .build(); + output.listLicenses(LicenseSetFactory.LicenseFilter.NONE); + + out.flush(); + String document = out.toString(); + assertThat(document).contains("Licenses (NONE):"); + StringWriter writer = new StringWriter(); + try (PrintWriter printWriter = new PrintWriter(writer)) { + output.listLicenses(printWriter, LicenseSetFactory.LicenseFilter.ALL); + } + assertThat(writer.toString()).contains("Licenses (ALL):"); + } + + @Test + void formatTest() throws IOException, SAXException { + Reporter.Output output = Reporter.Output.builder() + .statistic(new ClaimStatistic()) + .document(StandardXmlFactoryTest.simpleDocument()) + .configuration(new ReportConfiguration()) + .build(); + + IOSupplier badStyleSheet = () -> new InputStream() { + @Override + public int read() throws IOException { + throw new IOException("Expected exception."); + } + }; + assertThatThrownBy(() -> output.format(badStyleSheet, ReportConfiguration.SYSTEM_OUT.ioSupplier())) + .as("badStyleSheet") + .isInstanceOf(RatException.class) + .hasMessageContaining("Expected exception") + .hasCauseInstanceOf(TransformerException.class); + + IOSupplier missingStyleSheet = () -> {throw new IOException("Expected exception.");}; + + + assertThatThrownBy(() -> output.format(missingStyleSheet, ReportConfiguration.SYSTEM_OUT.ioSupplier())) + .as("missingStyleSheet") + .isInstanceOf(RatException.class) + .hasMessageContaining("Expected exception") + .hasCauseInstanceOf(IOException.class); + + IOSupplier badOutput = () -> new OutputStream() { + @Override + public void write(int b) throws IOException { + throw new IOException("Expected exception."); + } + }; + assertThatThrownBy(() -> output.format(StyleSheets.XML.getStyleSheet().ioSupplier(), badOutput)) + .as("badOutput") + .isInstanceOf(RatException.class) + .hasMessageContaining("Expected exception") + .hasCauseInstanceOf(TransformerException.class); + + IOSupplier missingOutput = () -> {throw new IOException("Expected exception.");}; + assertThatThrownBy(() -> output.format(StyleSheets.XML.getStyleSheet().ioSupplier(), missingOutput)) + .as("missingOutput") + .isInstanceOf(RatException.class) + .hasMessageContaining("Expected exception") + .hasCauseInstanceOf(IOException.class); + + } +} 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 7d6040433..098669e82 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 @@ -24,6 +24,7 @@ import static org.mockito.Mockito.when; import java.io.BufferedReader; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileFilter; @@ -32,9 +33,11 @@ import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; +import java.io.StringWriter; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -44,15 +47,21 @@ import org.apache.commons.io.filefilter.DirectoryFileFilter; import org.apache.commons.io.output.CloseShieldOutputStream; 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.ExclusionProcessorTest; import org.apache.rat.config.exclusion.StandardCollection; +import org.apache.rat.config.results.ClaimValidator; import org.apache.rat.configuration.XMLConfigurationReaderTest; import org.apache.rat.document.DocumentName; import org.apache.rat.document.DocumentNameMatcher; import org.apache.rat.license.ILicense; import org.apache.rat.license.ILicenseFamily; import org.apache.rat.license.LicenseSetFactory.LicenseFilter; +import org.apache.rat.report.RatReport; import org.apache.rat.report.Reportable; +import org.apache.rat.report.claim.ClaimStatistic; import org.apache.rat.testhelpers.TestingLog; import org.apache.rat.testhelpers.TestingLicense; import org.apache.rat.testhelpers.TestingMatcher; @@ -463,7 +472,7 @@ void outputTest() throws IOException { assertThat(underTest.getWriter()).isNotNull(); ByteArrayOutputStream stream = new ByteArrayOutputStream(); - underTest.setOut(new ReportConfiguration.IODescriptor("outputTest", () -> stream)); + underTest.setOut(new ReportConfiguration.IODescriptor<>("outputTest", () -> stream)); assertThat(underTest.getOutput().get()).isEqualTo(stream); PrintWriter writer = underTest.getWriter().get(); assertThat(writer).isNotNull(); @@ -490,7 +499,7 @@ void stylesheetTest() throws IOException, URISyntaxException { assertThat(underTest.getStyleSheetDescriptor()).isNull(); assertThat(underTest.getStyleSheet()).isNull(); InputStream stream = mock(InputStream.class); - underTest.setStyleSheet(new ReportConfiguration.IODescriptor("stylesheetTest", () -> stream)); + underTest.setStyleSheet(new ReportConfiguration.IODescriptor<>("stylesheetTest", () -> stream)); assertThat(underTest.getStyleSheetDescriptor().ioSupplier().get()).isEqualTo(stream); assertThat(underTest.getStyleSheet().get()).isEqualTo(stream); @@ -551,7 +560,7 @@ void testValidate() { void testSetOut() throws IOException { ReportConfiguration config = new ReportConfiguration(); try (OutputStreamInterceptor osi = new OutputStreamInterceptor()) { - config.setOut(new ReportConfiguration.IODescriptor("testSetOut",() -> osi)); + config.setOut(new ReportConfiguration.IODescriptor<>("testSetOut",() -> osi)); assertThat(osi.closeCount).isEqualTo(0); try (OutputStream os = config.getOutput().get()) { assertThat(os).isNotNull(); @@ -760,6 +769,64 @@ public static void validateDefault(ReportConfiguration config) { validateDefaultLicenses(config); } + public static void assertSame(ReportConfiguration actual, ReportConfiguration expected) { + assertThat(actual.isAddingLicenses()).isEqualTo(expected.isAddingLicenses()); + assertThat(actual.isAddingLicensesForced()).isEqualTo(expected.isAddingLicensesForced()); + assertThat(actual.listFamilies()).isEqualTo(expected.listFamilies()); + assertThat(actual.listLicenses()).isEqualTo(expected.listLicenses()); + assertThat(actual.isDryRun()).isEqualTo(expected.isDryRun()); + assertThat(actual.getArchiveProcessing()).isEqualTo(expected.getArchiveProcessing()); + assertThat(actual.getStandardProcessing()).isEqualTo(expected.getStandardProcessing()); + assertThat(actual.getStyleSheetDescriptor().name()).isEqualTo(expected.getStyleSheetDescriptor().name()); + assertThat(actual.getOutputDescriptor().name()).isEqualTo(expected.getOutputDescriptor().name()); + + assertThat(actual.getCopyrightMessage()).isEqualTo(expected.getCopyrightMessage()); + + assertThat(actual.sources()).containsExactlyElementsOf(expected.sources()); + assertThat(actual.reportables()).containsExactlyElementsOf(expected.reportables().toList()); + ExclusionProcessorTest.assertSame(actual.getExclusionProcessor(), expected.getExclusionProcessor()); + + } + @Test + void serDesTest() throws IOException { + underTest.setAddLicenseHeaders(AddLicenseHeaders.FORCED); + underTest.listFamilies(LicenseFilter.APPROVED); + underTest.listLicenses(LicenseFilter.ALL); + underTest.setDryRun(true); + underTest.setArchiveProcessing(ReportConfiguration.Processing.NOTIFICATION); + underTest.setStandardProcessing(ReportConfiguration.Processing.ABSENCE); + underTest.setStyleSheet(StyleSheets.MISSING_HEADERS.getStyleSheet()); + underTest.setOut(new File("/some/file/somewhere")); + underTest.setCopyrightMessage("the copyright message"); + underTest.addSource(new File("/my/file")); + underTest.addSource(new TestingReportable()); + + underTest.addExcludedPatterns(List.of("pattern/**", "pattern2/**")); + underTest.addExcludedCollection(StandardCollection.BAZAAR); + underTest.addExcludedCollection(StandardCollection.MISC); + underTest.addExcludedFileProcessor(StandardCollection.HIDDEN_FILE); + underTest.addExcludedMatcher(DocumentNameMatcher.MATCHES_ALL); + underTest.addIncludedPatterns(List.of("**/pattern3", "**/pattern4")); + underTest.addIncludedCollection(StandardCollection.ARCH); + underTest.addIncludedCollection(StandardCollection.BITKEEPER); + underTest.addIncludedMatcher(DocumentNameMatcher.MATCHES_NONE); + + ClaimValidator claimValidator = underTest.getClaimValidator(); + + claimValidator.setMax(ClaimStatistic.Counter.APPROVED, 5); + claimValidator.setMin(ClaimStatistic.Counter.APPROVED, 3); + claimValidator.setMax(ClaimStatistic.Counter.ARCHIVES, 10); + claimValidator.setMin(ClaimStatistic.Counter.BINARIES, 4); + + StringWriter stringWriter = new StringWriter(); + underTest.serDes().serialize(stringWriter); + + ReportConfiguration actual = new ReportConfiguration(); + actual.serDes().deserialize(() -> new ByteArrayInputStream(stringWriter.toString().getBytes(StandardCharsets.UTF_8)), + DocumentName.builder(new File("/rootDir")).build()); + assertSame(actual, underTest); + } + /** * A class to act as an output stream and count the number of close operations. */ @@ -776,4 +843,20 @@ public void close() { ++closeCount; } } + + /** + * A reportable that only reports its name. Does no actual work. + */ + static class TestingReportable implements Reportable { + + @Override + public void run(RatReport report) throws RatException { + // does nothing + } + + @Override + public DocumentName name() { + return DocumentName.builder().setBaseName("").setName("TestingReportable").build(); + } + } } 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 bd9e4fd63..12fe7aa45 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 @@ -65,6 +65,7 @@ import static org.apache.rat.commandline.Arg.HELP_LICENSES; 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 static org.junit.jupiter.api.Assertions.assertDoesNotThrow; @@ -133,9 +134,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); } @@ -160,8 +161,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(); @@ -214,9 +215,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"); }); } @@ -243,14 +244,8 @@ private void noDefaultsTest(final Option option) { ReportConfiguration config = generateConfig(ImmutablePair.of(option, null)); Reporter reporter = new Reporter(config); - try { - 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"); - } + assertThatThrownBy(reporter::execute) + .isInstanceOf(RatException.class); }); } @@ -276,16 +271,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(); }); } @@ -301,16 +296,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(); }); } @@ -329,16 +324,16 @@ private void execExcludeTest(final Option option, final String[] args, final boo 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(addIgnored ? 1 : 0); + Reporter.Output output = reporter.execute(); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(5); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.IGNORED)).isEqualTo(addIgnored ? 1 : 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(addIgnored ? 4 : 3); + output = reporter.execute(); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(2); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.IGNORED)).isEqualTo(addIgnored ? 4 : 3); }); } @@ -381,16 +376,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)).isZero(); // 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); }); } @@ -415,15 +410,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); }); } @@ -471,16 +466,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)).isZero(); 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); }); } @@ -499,25 +494,25 @@ private void execIncludeTest(final Option option, final String[] args, boolean a 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(addIgnored ? 1 : 0); + Reporter.Output output = reporter.execute(); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(4); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.IGNORED)).isEqualTo(addIgnored ? 1 : 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(addIgnored ? 4 : 3); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.IGNORED)).isEqualTo(addIgnored ? 4 : 3); - // verify include pust them back + // verify include put 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(addIgnored ? 2 : 1); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.IGNORED)).isEqualTo(addIgnored ? 2 : 1); }); } @@ -569,15 +564,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); }); } @@ -594,15 +589,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)).isZero(); 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)).isZero(); }); } @@ -624,18 +619,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)).isZero(); + 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)).isZero(); }); } @@ -658,23 +653,21 @@ private void execLicenseFamiliesDeniedTest(final Option option, final String[] a assertDoesNotThrow(() -> { configureSourceDir(option); - - // write the catz licensed text file writeFile("bsd.txt", "SPDX-License-Identifier: BSD-3-Clause"); 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)).isZero(); 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)).isZero(); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.UNAPPROVED)).isEqualTo(1); }); } @@ -704,26 +697,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)).isZero(); 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); }); } @@ -749,17 +742,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)).isZero(); }); } @@ -793,17 +786,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)).isZero(); 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); }); } @@ -817,11 +810,11 @@ 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(); + 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)).isZero(); + output.format(config); String actualText = TextUtils.readFile(outFile); TextUtils.assertContainsExactly(1, "Apache License 2.0: 1 ", actualText); TextUtils.assertContainsExactly(1, "STANDARD: 1 ", actualText); @@ -857,11 +850,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(); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(1); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.APPROVED)).isZero(); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.UNAPPROVED)).isEqualTo(1); + output.format(config); String actualText = baos.toString(StandardCharsets.UTF_8); switch (sheet) { case MISSING_HEADERS: @@ -887,11 +880,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(); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(1); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.APPROVED)).isZero(); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.UNAPPROVED)).isEqualTo(1); + output.format(config); String actualText = baos.toString(StandardCharsets.UTF_8); TextUtils.assertContainsExactly(1, "Hello world", actualText); } catch (IOException | RatException e) { @@ -929,11 +922,11 @@ 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(); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(1); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.APPROVED)).isZero(); + assertThat(output.getStatistic().getCounter(ClaimStatistic.Counter.UNAPPROVED)).isEqualTo(1); + output.format(config); String actualText = baos.toString(StandardCharsets.UTF_8); TextUtils.assertContainsExactly(1, "", actualText); @@ -960,12 +953,12 @@ protected void logLevelTest() { ReportConfiguration config = generateConfig(); Reporter reporter = new Reporter(config); - reporter.output(); + reporter.execute(); TextUtils.assertNotContains("DEBUG", baos.toString(StandardCharsets.UTF_8)); config = generateConfig(ImmutablePair.of(option, new String[]{"debug"})); reporter = new Reporter(config); - reporter.output(); + reporter.execute(); TextUtils.assertContains("DEBUG", baos.toString(StandardCharsets.UTF_8)); } catch (IOException | RatException e) { fail(e.getMessage(), e); @@ -989,7 +982,7 @@ private void listLicenses(final Option option) { args[0] = filter.name(); ReportConfiguration config = generateConfig(outputFile, stylesheet, ImmutablePair.of(option, args)); Reporter reporter = new Reporter(config); - reporter.output(); + reporter.execute().format(config); Document document = XmlUtils.toDom(new FileInputStream(outFile)); switch (filter) { case ALL: @@ -1035,7 +1028,7 @@ private void listFamilies(final Option option) { args[0] = filter.name(); ReportConfiguration config = generateConfig(outputFile, stylesheet, ImmutablePair.of(option, args)); Reporter reporter = new Reporter(config); - reporter.output(); + reporter.execute().format(config); Document document = XmlUtils.toDom(Files.newInputStream(outFile.toPath())); switch (filter) { case ALL: @@ -1088,7 +1081,7 @@ 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(); + reporter.execute().format(config); Document document = XmlUtils.toDom(Files.newInputStream(outFile.toPath())); XmlUtils.assertIsPresent(proc.name(), document, xPath, "/rat-report/resource[@name='/dummy.jar']"); @@ -1138,7 +1131,7 @@ 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.execute().format(config); Document document = XmlUtils.toDom(Files.newInputStream(outFile.toPath())); XmlUtils.assertIsPresent(proc.name(), document, xPath, testDoc); 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..d98a46468 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 @@ -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 d9f11b7ad..c0f2da002 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 @@ -7,7 +7,7 @@ * "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 * + * 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 * @@ -21,7 +21,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Fail.fail; -import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.PrintStream; @@ -36,6 +35,7 @@ import java.util.TreeMap; import javax.xml.XMLConstants; import javax.xml.transform.Source; +import javax.xml.transform.TransformerException; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamSource; import javax.xml.validation.Schema; @@ -52,15 +52,15 @@ import org.apache.rat.api.Document.Type; import org.apache.rat.api.RatException; 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.license.LicenseSetFactory; import org.apache.rat.report.claim.ClaimStatistic; +import org.apache.rat.report.claim.ClaimStatisticTest; import org.apache.rat.test.utils.Resources; import org.apache.rat.testhelpers.TextUtils; import org.apache.rat.testhelpers.XmlUtils; +import org.apache.rat.utils.StandardXmlFactory; import org.apache.rat.walker.DirectoryWalker; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; @@ -85,9 +85,9 @@ void testExecute() throws RatException, ParseException { 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(); + ArgumentContext context = new ArgumentContext(new File("."), cl); + ReportConfiguration config = OptionCollection.createConfiguration(context); + ClaimStatistic statistic = new Reporter(config).execute().getStatistic(); assertThat(statistic.getCounter(Type.ARCHIVE)).isEqualTo(1); assertThat(statistic.getCounter(Type.BINARY)).isEqualTo(2); @@ -135,14 +135,26 @@ void testExecute() throws RatException, ParseException { assertThat(actual).isEqualTo(expected); } + @Test + void testExecuteNoSource() throws ParseException, RatException, TransformerException { + File output = new File(tempDirectory, "testExecuteNoSource"); + + CommandLine cl = new DefaultParser().parse(OptionCollection.buildOptions(), new String[]{"--output-style", "xml", "--output-file", output.getPath()}); + ArgumentContext context = new ArgumentContext(new File("."), cl); + ReportConfiguration config = OptionCollection.createConfiguration(context); + Reporter.Output result = new Reporter(config).execute(); + assertThat(StandardXmlFactory.serializeDocument(result.getDocument())).isEmpty(); + ClaimStatisticTest.assertSame(result.getStatistic(), new ClaimStatistic()); + } + @Test 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); + ArgumentContext context = new ArgumentContext(new File("."), commandLine); - ReportConfiguration config = OptionCollection.createConfiguration(ctxt); - new Reporter(config).output(); + ReportConfiguration config = OptionCollection.createConfiguration(context); + new Reporter(config).execute().format(config); assertThat(output.exists()).isTrue(); String content = FileUtils.readFileToString(output, StandardCharsets.UTF_8); TextUtils.assertPatternInTarget("^! Unapproved:\\s*2 ", content); @@ -150,6 +162,18 @@ void testOutputOption() throws Exception { assertThat(content).contains("/sub/Empty.txt"); } + @Test + void testGetOutputMethod() throws Exception { + File output = new File(tempDirectory, "test"); + CommandLine commandLine = new DefaultParser().parse(OptionCollection.buildOptions(), new String[]{"-o", output.getCanonicalPath(), basedir}); + ArgumentContext context = new ArgumentContext(new File("."), commandLine); + + ReportConfiguration config = OptionCollection.createConfiguration(context); + Reporter reporter = new Reporter(config); + Reporter.Output expected = reporter.execute(); + assertThat(reporter.getOutput()).isEqualTo(expected); + } + @Test void testDefaultOutput() throws Exception { File output = new File(tempDirectory, "testDefaultOutput"); @@ -158,10 +182,10 @@ void testDefaultOutput() throws Exception { 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); + ArgumentContext context = new ArgumentContext(new File("."), commandLine); - ReportConfiguration config = OptionCollection.createConfiguration(ctxt); - new Reporter(config).output(); + ReportConfiguration config = OptionCollection.createConfiguration(context); + new Reporter(config).execute().format(config); } finally { System.setOut(origin); } @@ -212,10 +236,10 @@ void testXMLOutput() throws Exception { File output = new File(tempDirectory, ".rat/testXMLOutput"); output.getParentFile().mkdir(); CommandLine commandLine = new DefaultParser().parse(OptionCollection.buildOptions(), new String[]{"--output-style", "xml", "--output-file", output.getPath(), basedir}); - ArgumentContext ctxt = new ArgumentContext(tempDirectory, commandLine); + ArgumentContext context = new ArgumentContext(tempDirectory, commandLine); - ReportConfiguration config = OptionCollection.createConfiguration(ctxt); - new Reporter(config).output(); + ReportConfiguration config = OptionCollection.createConfiguration(context); + new Reporter(config).execute().format(config); assertThat(output).exists(); Document doc = XmlUtils.toDom(java.nio.file.Files.newInputStream(output.toPath())); @@ -307,7 +331,7 @@ void testXMLOutput() throws Exception { * Finds a node via xpath on the document. And then checks family, approval and * type of elements of the node. * - * @param doc The document to check + * @param doc the document to check * @param xpath the XPath instance to use. * @param resource the xpath statement to locate the node. * @param licenseInfo the license info for the node. (can be null) @@ -405,13 +429,8 @@ private Validator initValidator() throws SAXException { @Test void xmlReportTest() throws Exception { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - ReportConfiguration configuration = initializeConfiguration(); - configuration.setStyleSheet(StyleSheets.XML.getStyleSheet()); - configuration.setOut(new ReportConfiguration.IODescriptor("xmlReportTest", () -> 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(); @@ -457,10 +476,9 @@ void plainReportTest() throws Exception { "Generated at: "; ByteArrayOutputStream out = new ByteArrayOutputStream(); ReportConfiguration configuration = initializeConfiguration(); - configuration.setOut(new ReportConfiguration.IODescriptor("plainReportTest", () -> 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); @@ -473,11 +491,10 @@ void plainReportTest() throws Exception { void unapprovedLicensesReportTest() throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); ReportConfiguration configuration = initializeConfiguration(); - configuration.setOut(new ReportConfiguration.IODescriptor("unapprovedLicensesReportTest", () -> 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 ); @@ -485,35 +502,19 @@ void unapprovedLicensesReportTest() throws Exception { TextUtils.assertPatternInTarget("\\Q/sub/Empty.txt\\E", document); } - @Test - void listLicensesReportTest() throws Exception { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - ReportConfiguration configuration = initializeConfiguration(); - configuration.setOut(new ReportConfiguration.IODescriptor("listLicensesReportTest", () -> out)); - configuration.setStyleSheet(StyleSheets.UNAPPROVED_LICENSES.getStyleSheet()); - Reporter.listLicenses(configuration, LicenseSetFactory.LicenseFilter.NONE); - - out.flush(); - String document = out.toString(); - - assertThat(document).contains("Licenses (NONE):"); - } - @Test 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(); } diff --git a/apache-rat-core/src/test/java/org/apache/rat/config/exclusion/ExclusionProcessorTest.java b/apache-rat-core/src/test/java/org/apache/rat/config/exclusion/ExclusionProcessorTest.java index 19e2e1542..3e5ad642e 100644 --- a/apache-rat-core/src/test/java/org/apache/rat/config/exclusion/ExclusionProcessorTest.java +++ b/apache-rat-core/src/test/java/org/apache/rat/config/exclusion/ExclusionProcessorTest.java @@ -18,6 +18,9 @@ */ package org.apache.rat.config.exclusion; +import java.io.ByteArrayInputStream; +import java.io.StringWriter; +import java.nio.charset.StandardCharsets; import java.nio.file.FileSystems; import java.nio.file.Path; import java.util.ArrayList; @@ -26,6 +29,9 @@ import org.apache.commons.io.FileUtils; import org.apache.rat.document.DocumentNameMatcher; import org.apache.rat.document.DocumentName; +import org.apache.rat.report.xml.writer.XmlWriter; +import org.apache.rat.utils.StandardXmlFactory; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import java.io.File; @@ -36,7 +42,8 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import org.mockito.Mockito; +import org.w3c.dom.Document; +import org.xml.sax.SAXException; import static java.lang.String.format; import static org.assertj.core.api.Assertions.assertThat; @@ -44,6 +51,8 @@ import static org.apache.rat.document.FSInfoTest.UNIX; import static org.apache.rat.document.FSInfoTest.WINDOWS; import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; public class ExclusionProcessorTest { @@ -62,7 +71,7 @@ private String dump(DocumentNameMatcher nameMatcher, DocumentName name) { private DocumentName mkName(DocumentName baseDir, String pth) throws IOException { DocumentName result = baseDir.resolve(ExclusionUtils.convertSeparator(pth, "/", baseDir.getDirectorySeparator())); - DocumentName mocked = Mockito.spy(result); + DocumentName mocked = spy(result); String fn = result.localized(FileSystems.getDefault().getSeparator()); File file = tempDir.resolve(fn.substring(1)).toFile(); @@ -83,7 +92,7 @@ private DocumentName mkName(DocumentName baseDir, String pth) throws IOException if (!file.createNewFile()) { fail(() -> "Unable to create file: " + file); } - Mockito.when(mocked.asFile()).thenReturn(file); + when(mocked.asFile()).thenReturn(file); return mocked; } @@ -138,7 +147,6 @@ private void assertExclusions(DocumentName basedir, String pattern, Map expectedMap = new HashMap<>(); - expectedMap.put("a/b/foo", true); expectedMap.put("b/foo", true); expectedMap.put("foo", false); @@ -264,4 +272,217 @@ private static Stream getDocumentNames() { return lst.stream(); } + + @Test + void addNullIncludedMatcherTest() { + ExclusionProcessor underTest = new ExclusionProcessor(); + assertThat(underTest.getIncludedPaths()).isEmpty(); + underTest.addIncludedMatcher(null); + assertThat(underTest.getIncludedPaths()).isEmpty(); + underTest.addIncludedMatcher(DocumentNameMatcher.MATCHES_ALL) + .addIncludedMatcher(null); + assertThat(underTest.getIncludedPaths()).hasSize(1); + List actualPaths = underTest.getIncludedPaths().stream().map(DocumentNameMatcher::toString).toList(); + assertThat(actualPaths).containsExactly(DocumentNameMatcher.MATCHES_ALL.toString()); + } + + @Test + void addNullExcludedMatcherTest() { + ExclusionProcessor underTest = new ExclusionProcessor(); + assertThat(underTest.getExcludedPaths()).isEmpty(); + underTest.addExcludedMatcher(null); + assertThat(underTest.getExcludedPaths()).isEmpty(); + underTest.addExcludedMatcher(DocumentNameMatcher.MATCHES_ALL) + .addExcludedMatcher(null); + assertThat(underTest.getExcludedPaths()).hasSize(1); + List actualPaths = underTest.getExcludedPaths().stream().map(DocumentNameMatcher::toString).toList(); + assertThat(actualPaths).containsExactly(DocumentNameMatcher.MATCHES_ALL.toString()); + } + + @Test + void addFileProcessor() { + ExclusionProcessor underTest = new ExclusionProcessor(); + assertThat(underTest.getFileProcessors()).isEmpty(); + underTest.addFileProcessor(null); + assertThat(underTest.getFileProcessors()).isEmpty(); + underTest.addFileProcessor(StandardCollection.HIDDEN_FILE) + .addFileProcessor(null); + assertThat(underTest.getFileProcessors()).containsExactly(StandardCollection.HIDDEN_FILE); + } + + @Test + void addNullIncludedPatternTest() { + ExclusionProcessor underTest = new ExclusionProcessor(); + assertThat(underTest.getIncludedPatterns()).isEmpty(); + underTest.addIncludedPatterns(null); + assertThat(underTest.getIncludedPatterns()).isEmpty(); + underTest.addIncludedPatterns(Collections.emptyList()); + assertThat(underTest.getIncludedPatterns()).isEmpty(); + underTest.addIncludedPatterns(List.of("hello/world")) + .addIncludedPatterns(null) + .addIncludedPatterns(Collections.emptyList()); + assertThat(underTest.getIncludedPatterns()).containsExactly("hello/world"); + } + + @Test + void addNullIncludedCollectionTest() { + ExclusionProcessor underTest = new ExclusionProcessor(); + assertThat(underTest.getIncludedCollections()).isEmpty(); + underTest.addIncludedCollection(null); + assertThat(underTest.getIncludedCollections()).isEmpty(); + underTest.addIncludedCollection(StandardCollection.HIDDEN_FILE) + .addIncludedCollection(null); + assertThat(underTest.getIncludedCollections()).containsExactly(StandardCollection.HIDDEN_FILE); + } + + @Test + void addNullExcludedCollectionTest() { + ExclusionProcessor underTest = new ExclusionProcessor(); + assertThat(underTest.getExcludedCollections()).isEmpty(); + underTest.addExcludedCollection(null); + assertThat(underTest.getExcludedCollections()).isEmpty(); + underTest.addExcludedCollection(StandardCollection.HIDDEN_FILE) + .addExcludedCollection(null); + assertThat(underTest.getExcludedCollections()).containsExactly(StandardCollection.HIDDEN_FILE); + } + + @Test + void addNullExcludedPatternTest() { + ExclusionProcessor underTest = new ExclusionProcessor(); + assertThat(underTest.getExcludedPatterns()).isEmpty(); + underTest.addExcludedPatterns(null); + assertThat(underTest.getExcludedPatterns()).isEmpty(); + underTest.addExcludedPatterns(Collections.emptyList()); + assertThat(underTest.getExcludedPatterns()).isEmpty(); + underTest.addExcludedPatterns(List.of("hello/world")) + .addExcludedPatterns(null) + .addExcludedPatterns(Collections.emptyList()); + assertThat(underTest.getExcludedPatterns()).containsExactly("hello/world"); + } + + public static void assertSame(ExclusionProcessor actual, ExclusionProcessor expected) { + assertThat(actual.getExcludedCollections()).containsExactlyElementsOf(expected.getExcludedCollections()); + assertThat(actual.getFileProcessors()).containsExactlyElementsOf(expected.getFileProcessors()); + assertThat(actual.getIncludedCollections()).containsExactlyElementsOf(expected.getIncludedCollections()); + assertThat(actual.getExcludedPatterns()).containsExactlyElementsOf(expected.getExcludedPatterns()); + assertThat(actual.getIncludedPatterns()).containsExactlyElementsOf(expected.getIncludedPatterns()); + + // Paths only match on name. Deserialized paths are not functional. + List actualPaths = actual.getExcludedPaths().stream().map(DocumentNameMatcher::toString).toList(); + List expectedPaths = expected.getExcludedPaths().stream().map(DocumentNameMatcher::toString).toList(); + assertThat(actualPaths).as("excluded paths").containsExactlyElementsOf(expectedPaths); + + actualPaths = actual.getIncludedPaths().stream().map(DocumentNameMatcher::toString).toList(); + expectedPaths = expected.getIncludedPaths().stream().map(DocumentNameMatcher::toString).toList(); + assertThat(actualPaths).as("included paths").containsExactlyElementsOf(expectedPaths); + } + + @ParameterizedTest + @MethodSource("serDesTestData") + void serDesTest(ExclusionProcessor underTest) throws IOException, SAXException { + StringWriter stringWriter = new StringWriter(); + try (XmlWriter writer = new XmlWriter(stringWriter)) { + underTest.serDes().serialize(writer); + } + Document document = StandardXmlFactory.documentBuilder().parse(new ByteArrayInputStream(stringWriter.toString().getBytes(StandardCharsets.UTF_8))); + ExclusionProcessor actual = new ExclusionProcessor(); + actual.serDes().deserialize(document.getElementsByTagName("ExclusionProcessor").item(0)); + assertSame(actual, underTest); + } + + static List serDesTestData() { + List tests = new ArrayList<>(); + tests.add(new ExclusionProcessor()); + + // single exclusions + tests.add(new ExclusionProcessor() + .addExcludedPatterns(List.of("pattern/**"))); + + tests.add(new ExclusionProcessor() + .addExcludedCollection(StandardCollection.BAZAAR)); + + tests.add(new ExclusionProcessor() + .addExcludedMatcher(DocumentNameMatcher.MATCHES_ALL)); + + // single inclusions + tests.add(new ExclusionProcessor() + .addIncludedPatterns(List.of("pattern/**"))); + + tests.add(new ExclusionProcessor() + .addIncludedCollection(StandardCollection.BAZAAR)); + + tests.add(new ExclusionProcessor() + .addIncludedMatcher(DocumentNameMatcher.MATCHES_NONE)); + + // full population + tests.add(new ExclusionProcessor() + .addExcludedPatterns(List.of("pattern/**", "pattern2/**")) + .addExcludedCollection(StandardCollection.BAZAAR) + .addExcludedCollection(StandardCollection.MISC) + .addExcludedMatcher(DocumentNameMatcher.MATCHES_ALL) + .addIncludedPatterns(List.of("**/pattern3", "**/pattern4")) + .addIncludedCollection(StandardCollection.ARCH) + .addIncludedCollection(StandardCollection.BITKEEPER) + .addIncludedMatcher(DocumentNameMatcher.MATCHES_NONE)); + + return tests; + } + + @Test + void reportExclusionsTest() throws IOException { + ExclusionProcessor underTest = new ExclusionProcessor() + .addExcludedPatterns(List.of("pattern/**", "pattern2/**")) + .addExcludedCollection(StandardCollection.BAZAAR) + .addExcludedCollection(StandardCollection.MISC) + .addFileProcessor(StandardCollection.HIDDEN_FILE) + .addExcludedMatcher(DocumentNameMatcher.MATCHES_ALL) + .addIncludedPatterns(List.of("**/pattern3", "**/pattern4")) + .addIncludedCollection(StandardCollection.ARCH) + .addIncludedCollection(StandardCollection.BITKEEPER) + .addIncludedMatcher(DocumentNameMatcher.MATCHES_NONE); + + StringWriter writer = new StringWriter(); + underTest.reportExclusions(writer); + String actual = writer.toString(); + + assertThat(actual).containsPattern("Excluding patterns:[^$]+\\Qpattern/**\\E") + .containsPattern("Excluding patterns:[^$]+\\Qpattern2/**\\E") + .containsPattern("Including patterns:[^$]+\\Q**/pattern3\\E") + .containsPattern("Including patterns:[^$]+\\Q**/pattern4\\E") + .contains("Excluding " + StandardCollection.BAZAAR + " collection.") + .contains("Excluding " + StandardCollection.MISC + " collection.") + .contains("Including " + StandardCollection.ARCH + " collection.") + .contains("Including " + StandardCollection.BITKEEPER + " collection.") + .contains("Processing exclude file from " + StandardCollection.HIDDEN_FILE) + .contains("Excluding " + DocumentNameMatcher.MATCHES_ALL + ".") + .contains("Including " + DocumentNameMatcher.MATCHES_NONE + "."); + } + + @Test + void getNameMatcherTest() { + ExclusionProcessor underTest = new ExclusionProcessor() + .addExcludedPatterns(List.of("pattern/**", "pattern2/**")) + .addExcludedCollection(StandardCollection.BAZAAR) + .addExcludedCollection(StandardCollection.MISC) + .addFileProcessor(StandardCollection.HIDDEN_FILE) + .addExcludedMatcher(DocumentNameMatcher.MATCHES_ALL) + .addIncludedPatterns(List.of("**/pattern3", "**/pattern4")) + .addIncludedCollection(StandardCollection.ARCH) + .addIncludedCollection(StandardCollection.BITKEEPER) + .addIncludedMatcher(DocumentNameMatcher.MATCHES_NONE); + + assertThat(underTest.getLastMatcherBaseDir()).isNull(); + assertThat(underTest.getLastMatcher()).isNull(); + DocumentName base = DocumentName.builder(new File(".")).build(); + + DocumentNameMatcher matcher = underTest.getNameMatcher(base); + assertThat(underTest.getLastMatcherBaseDir()).isEqualTo(base); + assertThat(underTest.getLastMatcher()).isEqualTo(matcher); + + DocumentNameMatcher matcher2 = underTest.getNameMatcher(base); + assertThat(underTest.getLastMatcherBaseDir()).isEqualTo(base); + assertThat(underTest.getLastMatcher()).isEqualTo(matcher); + assertThat(underTest.getLastMatcherBaseDir()).isEqualTo(base); + assertThat(matcher2).isEqualTo(matcher); + } } diff --git a/apache-rat-core/src/test/java/org/apache/rat/report/claim/ClaimStatisticTest.java b/apache-rat-core/src/test/java/org/apache/rat/report/claim/ClaimStatisticTest.java new file mode 100644 index 000000000..a0da56311 --- /dev/null +++ b/apache-rat-core/src/test/java/org/apache/rat/report/claim/ClaimStatisticTest.java @@ -0,0 +1,151 @@ +/* + * 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.report.claim; + +import org.apache.rat.api.Document; +import org.junit.jupiter.api.Test; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.StringWriter; +import java.nio.charset.StandardCharsets; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ClaimStatisticTest { + + @Test + void counterTests() { + ClaimStatistic underTest = new ClaimStatistic(); + for (ClaimStatistic.Counter counter : ClaimStatistic.Counter.values()) { + assertThat(underTest.getCounter(counter)).isZero(); + underTest.incCounter(counter, 1); + assertThat(underTest.getCounter(counter)).isEqualTo(1); + underTest.incCounter(counter, -2); + assertThat(underTest.getCounter(counter)).isEqualTo(-1); + underTest.setCounter(counter, Integer.MAX_VALUE); + assertThat(underTest.getCounter(counter)).isEqualTo(Integer.MAX_VALUE); + } + } + + @Test + void typeTests() { + ClaimStatistic underTest = new ClaimStatistic(); + for (Document.Type docType : Document.Type.values()) { + assertThat(underTest.getCounter(docType)).isZero(); + underTest.incCounter(docType, 1); + assertThat(underTest.getCounter(docType)).isEqualTo(1); + underTest.incCounter(docType, -2); + assertThat(underTest.getCounter(docType)).isEqualTo(-1); + } + } + + @Test + void documentTypeTest() { + ClaimStatistic underTest = new ClaimStatistic(); + assertThat(underTest.getDocumentTypes()).isEmpty(); + underTest.incCounter(Document.Type.NOTICE, 1); + underTest.incCounter(Document.Type.BINARY, -1); + assertThat(underTest.getDocumentTypes()).containsExactly(Document.Type.BINARY, Document.Type.NOTICE); + } + + @Test + void licenseCategoryCountTest() { + ClaimStatistic underTest = new ClaimStatistic(); + assertThat(underTest.getLicenseCategoryCount("fam")).isZero(); + underTest.incLicenseCategoryCount("fam", 1); + assertThat(underTest.getLicenseCategoryCount("fam")).isEqualTo(1); + underTest.incLicenseCategoryCount("fam", -2); + assertThat(underTest.getLicenseCategoryCount("fam")).isEqualTo(-1); + underTest.incLicenseCategoryCount("wayToLongAName", 5); + assertThat(underTest.getLicenseCategoryCount("wayToLongAName")).isEqualTo(5); + } + + @Test + void licenseNameCountTest() { + ClaimStatistic underTest = new ClaimStatistic(); + assertThat(underTest.getLicenseNameCount("fam")).isZero(); + underTest.incLicenseNameCount("fam", 1); + assertThat(underTest.getLicenseNameCount("fam")).isEqualTo(1); + underTest.incLicenseNameCount("fam", -2); + assertThat(underTest.getLicenseNameCount("fam")).isEqualTo(-1); + } + + @Test + void licenseFamilyCategoriesTest() { + ClaimStatistic underTest = new ClaimStatistic(); + assertThat(underTest.getLicenseFamilyCategories()).isEmpty(); + underTest.incLicenseCategoryCount("one", 1); + underTest.incLicenseCategoryCount("neg", -1); + assertThat(underTest.getLicenseFamilyCategories()).containsExactly("neg", "one"); + underTest.incLicenseCategoryCount("wayToLongAName", 5); + assertThat(underTest.getLicenseFamilyCategories()).containsExactly("neg", "one", "wayToLongAName"); + } + + @Test + void licenseFamilyNameTest() { + ClaimStatistic underTest = new ClaimStatistic(); + assertThat(underTest.getLicenseNames()).isEmpty(); + underTest.incLicenseNameCount("one", 1); + underTest.incLicenseNameCount("neg", -1); + assertThat(underTest.getLicenseNames()).containsExactly("neg", "one"); + underTest.incLicenseNameCount("wayToLongAName", 5); + assertThat(underTest.getLicenseNames()).containsExactly("neg", "one", "wayToLongAName"); + } + + /** + * Compares two claim statistics for similarity after serialization/deserialization. + * @param actual the deserialized version. + * @param expected the original version. + */ + public static void assertSame(final ClaimStatistic actual, final ClaimStatistic expected) { + assertThat(actual.getLicenseFamilyCategories()).containsExactlyElementsOf(expected.getLicenseFamilyCategories()); + assertThat(actual.getLicenseNames()).containsExactlyElementsOf(expected.getLicenseNames()); + assertThat(actual.getDocumentTypes()).containsExactlyElementsOf(expected.getDocumentTypes()); + for (String cat : expected.getLicenseFamilyCategories()) { + assertThat(actual.getLicenseCategoryCount(cat)).isEqualTo(expected.getLicenseCategoryCount(cat)); + } + for (String name : expected.getLicenseNames()) { + assertThat(actual.getLicenseNameCount(name)).isEqualTo(expected.getLicenseNameCount(name)); + } + for (Document.Type type : expected.getDocumentTypes()) { + assertThat(actual.getCounter(type)).isEqualTo(expected.getCounter(type)); + } + for (ClaimStatistic.Counter counter : ClaimStatistic.Counter.values()) { + assertThat(actual.getCounter(counter)).isEqualTo(expected.getCounter(counter)); + } + } + + @Test + void serDesRoundTrip() throws IOException { + ClaimStatistic underTest = new ClaimStatistic(); + underTest.incLicenseCategoryCount("familyCategory", 1); + underTest.incCounter(ClaimStatistic.Counter.APPROVED, 2); + underTest.incCounter(Document.Type.IGNORED, 3); + underTest.incLicenseNameCount("licenseName", 4); + + ClaimStatistic.SerDes serDes = underTest.serDes(); + StringWriter stringWriter = new StringWriter(); + serDes.serialize(stringWriter); + ClaimStatistic actual = new ClaimStatistic(); + ClaimStatistic.SerDes serDes2 = actual.serDes(); + serDes2.deserialize(() -> new ByteArrayInputStream(stringWriter.toString().getBytes(StandardCharsets.UTF_8))); + + assertSame(actual, underTest); + } +} diff --git a/apache-rat-core/src/test/java/org/apache/rat/utils/CasedStringTests.java b/apache-rat-core/src/test/java/org/apache/rat/utils/CasedStringTest.java similarity index 99% rename from apache-rat-core/src/test/java/org/apache/rat/utils/CasedStringTests.java rename to apache-rat-core/src/test/java/org/apache/rat/utils/CasedStringTest.java index f69c11600..93738568a 100644 --- a/apache-rat-core/src/test/java/org/apache/rat/utils/CasedStringTests.java +++ b/apache-rat-core/src/test/java/org/apache/rat/utils/CasedStringTest.java @@ -29,7 +29,7 @@ import static org.assertj.core.api.Assertions.assertThat; -class CasedStringTests { +class CasedStringTest { @MethodSource("testSegmentationData") @ParameterizedTest diff --git a/apache-rat-core/src/test/java/org/apache/rat/utils/StandardXmlFactoryTests.java b/apache-rat-core/src/test/java/org/apache/rat/utils/StandardXmlFactoryTest.java similarity index 67% rename from apache-rat-core/src/test/java/org/apache/rat/utils/StandardXmlFactoryTests.java rename to apache-rat-core/src/test/java/org/apache/rat/utils/StandardXmlFactoryTest.java index bfffac4fa..0414ebe7c 100644 --- a/apache-rat-core/src/test/java/org/apache/rat/utils/StandardXmlFactoryTests.java +++ b/apache-rat-core/src/test/java/org/apache/rat/utils/StandardXmlFactoryTest.java @@ -18,14 +18,22 @@ */ package org.apache.rat.utils; +import org.apache.rat.report.xml.writer.XmlWriter; import org.junit.jupiter.api.Test; +import org.w3c.dom.Document; +import org.xml.sax.SAXException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; +import java.io.ByteArrayInputStream; +import java.io.IOException; import java.io.InputStream; +import java.io.StringWriter; +import java.nio.charset.StandardCharsets; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -33,7 +41,25 @@ /** * Tests for StandardXmlFactory. */ -class StandardXmlFactoryTests { +public class StandardXmlFactoryTest { + + public static final String SIMPLE_DOCUMENT_TEXT = """ + + + + """; + + public static Document simpleDocument() throws IOException, SAXException { + StringWriter writer = new StringWriter(); + try (XmlWriter xmlWriter = new XmlWriter(writer)) { + xmlWriter.startDocument().startElement("first") + .startElement("second") + .attribute("attr1", "one") + .closeDocument(); + } + return StandardXmlFactory.documentBuilder() + .parse(new ByteArrayInputStream(writer.toString().getBytes(StandardCharsets.UTF_8))); + } @Test void noArg() throws TransformerConfigurationException { @@ -64,6 +90,18 @@ void goodDocumentBuilderTest() { .isNotNull(); } + @Test + void serializeDocumentTest() throws IOException, SAXException, TransformerException { + String result = StandardXmlFactory.serializeDocument(simpleDocument()); + assertThat(result).isEqualToIgnoringNewLines(SIMPLE_DOCUMENT_TEXT); + } + + @Test + void serializeEmptyDocumentYieldsEmptyString() throws TransformerException { + Document document = StandardXmlFactory.documentBuilder().newDocument(); + assertThat(StandardXmlFactory.serializeDocument(document)).isEmpty(); + } + /** * Class to test failing document builder. */ diff --git a/apache-rat-plugin/src/main/java/org/apache/rat/mp/RatCheckMojo.java b/apache-rat-plugin/src/main/java/org/apache/rat/mp/RatCheckMojo.java index 5a55a0612..50fed5ad4 100644 --- a/apache-rat-plugin/src/main/java/org/apache/rat/mp/RatCheckMojo.java +++ b/apache-rat-plugin/src/main/java/org/apache/rat/mp/RatCheckMojo.java @@ -36,6 +36,7 @@ import org.apache.rat.commandline.Arg; import org.apache.rat.commandline.StyleSheets; import org.apache.rat.config.exclusion.StandardCollection; +import org.apache.rat.config.results.ClaimValidator; import org.apache.rat.license.LicenseSetFactory.LicenseFilter; import org.apache.rat.report.claim.ClaimStatistic; import org.apache.rat.utils.DefaultLog; @@ -52,7 +53,8 @@ @Mojo(name = "check", defaultPhase = LifecyclePhase.VALIDATE, threadSafe = true) public class RatCheckMojo extends AbstractRatMojo { - /** The default output file if no other is specified. + /** + * The default output file if no other is specified. * @deprecated Use <outputFile> instead. */ @Deprecated @@ -60,7 +62,7 @@ public class RatCheckMojo extends AbstractRatMojo { private File defaultReportFile; /** - * The defined build directory + * The defined build directory. */ @Parameter(defaultValue = "${project.build.directory}", readonly = true) private String buildDirectory; @@ -83,7 +85,7 @@ public void setReportFile(final File reportFile) { * or "xml" for the raw XML report. Alternatively you can give the path of an * XSL transformation that will be applied on the raw XML to produce the report * written to the output file. - * @deprecated Use setStyleSheet or xml instead. + * @deprecated Use setStyleSheet or "xml" instead. */ @Deprecated @Parameter(property = "rat.outputStyle") @@ -158,9 +160,6 @@ public void setCopyrightMessage(final String copyrightMessage) { @Parameter(property = "rat.consoleOutput", defaultValue = "true") private boolean consoleOutput; - /** The reporter that this mojo uses */ - private Reporter reporter; - @Override protected ReportConfiguration getConfiguration() throws MojoExecutionException { ReportConfiguration result = super.getConfiguration(); @@ -208,12 +207,12 @@ public void execute() throws MojoExecutionException, MojoFailureException { config.reportExclusions(logWriter); } try { - this.reporter = new Reporter(config); - reporter.output(); + Reporter.Output output = new Reporter(config).execute(); if (verbose) { - reporter.writeSummary(logWriter); + output.writeSummary(logWriter); } - check(config); + output.format(config); + check(output); } catch (MojoFailureException e) { throw e; } catch (Exception e) { @@ -224,17 +223,18 @@ public void execute() throws MojoExecutionException, MojoFailureException { } } - protected void check(final ReportConfiguration config) throws MojoFailureException { - ClaimStatistic statistics = reporter.getClaimsStatistic(); + protected void check(final Reporter.Output output) throws MojoFailureException { + ClaimStatistic statistics = output.getStatistic(); + ClaimValidator validator = output.getConfiguration().getClaimValidator(); try { - reporter.writeSummary(DefaultLog.getInstance().asWriter(Log.Level.DEBUG)); - if (config.getClaimValidator().hasErrors()) { - config.getClaimValidator().logIssues(statistics); + output.writeSummary(DefaultLog.getInstance().asWriter(Log.Level.DEBUG)); + if (validator.hasErrors()) { + validator.logIssues(statistics); if (consoleOutput && - !config.getClaimValidator().isValid(ClaimStatistic.Counter.UNAPPROVED, statistics.getCounter(ClaimStatistic.Counter.UNAPPROVED))) { + !validator.isValid(ClaimStatistic.Counter.UNAPPROVED, statistics.getCounter(ClaimStatistic.Counter.UNAPPROVED))) { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); - reporter.output(StyleSheets.UNAPPROVED_LICENSES.getStyleSheet().ioSupplier(), () -> baos); + output.format(StyleSheets.UNAPPROVED_LICENSES.getStyleSheet().ioSupplier(), () -> baos); getLog().warn(baos.toString(StandardCharsets.UTF_8)); } catch (RuntimeException rte) { throw rte; @@ -244,7 +244,7 @@ protected void check(final ReportConfiguration config) throws MojoFailureExcepti } String msg = format("Counter(s) %s exceeded minimum or maximum values. See RAT report in: '%s'.", - String.join(", ", config.getClaimValidator().listIssues(statistics)), + String.join(", ", validator.listIssues(statistics)), getRatTxtFile()); if (!ignoreErrors) { diff --git a/apache-rat-plugin/src/main/java/org/apache/rat/mp/RatReportMojo.java b/apache-rat-plugin/src/main/java/org/apache/rat/mp/RatReportMojo.java index 99ac97f58..c82d6d440 100644 --- a/apache-rat-plugin/src/main/java/org/apache/rat/mp/RatReportMojo.java +++ b/apache-rat-plugin/src/main/java/org/apache/rat/mp/RatReportMojo.java @@ -31,21 +31,22 @@ import java.util.Map; import java.util.ResourceBundle; +import javax.inject.Inject; + import org.apache.maven.artifact.Artifact; import org.apache.maven.doxia.sink.Sink; import org.apache.maven.doxia.sink.SinkFactory; import org.apache.maven.doxia.sink.impl.SinkEventAttributeSet; import org.apache.maven.doxia.site.SiteModel; import org.apache.maven.doxia.siterenderer.DocumentRenderingContext; -import org.apache.maven.doxia.siterenderer.Renderer; import org.apache.maven.doxia.siterenderer.RendererException; +import org.apache.maven.doxia.siterenderer.SiteRenderer; import org.apache.maven.doxia.siterenderer.SiteRenderingContext; import org.apache.maven.doxia.siterenderer.sink.SiteRendererSink; import org.apache.maven.doxia.tools.SiteTool; import org.apache.maven.doxia.tools.SiteToolException; import org.apache.maven.execution.MavenSession; import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugins.annotations.Component; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.ResolutionScope; @@ -114,27 +115,27 @@ public class RatReportMojo extends AbstractRatMojo implements MavenMultiPageRepo /** * SiteTool. */ - @Component + @Inject protected SiteTool siteTool; /** * Doxia Site Renderer component. */ - @Component - protected Renderer siteRenderer; + @Inject + protected SiteRenderer siteRenderer; /** - * The current sink to use + * The current sink to use. */ private Sink sink; /** - * The sink factory to use + * The sink factory to use. */ private SinkFactory sinkFactory; /** - * The current report output directory to use + * The current report output directory to use. */ private File reportOutputDirectory; @@ -142,7 +143,7 @@ public class RatReportMojo extends AbstractRatMojo implements MavenMultiPageRepo * This method is called when the report generation is invoked directly as a * standalone Mojo. * - * @throws MojoExecutionException if an error occurs when generating the report + * @throws MojoExecutionException if an error occurs when generating the report. * @see org.apache.maven.plugin.Mojo#execute() */ @Override @@ -218,8 +219,7 @@ private SiteRenderingContext createSiteRenderingContext(final Locale locale) Artifact skinArtifact = siteTool.getSkinArtifactFromRepository( session.getRepositorySession(), remoteRepositories, - siteSkin - ); + siteSkin); getLog().debug(buffer().a("Rendering content with ").strong(skinArtifact.getId() + " skin").a('.').build()); @@ -228,11 +228,10 @@ private SiteRenderingContext createSiteRenderingContext(final Locale locale) templateProperties, siteModel, project.getName(), - locale - ); + locale); context.setRootDirectory(project.getBasedir()); - return context; + return context; } catch (SiteToolException e) { throw new MavenReportException("Failed to retrieve skin artifact", e); } catch (RendererException e) { @@ -246,7 +245,7 @@ private SiteRenderingContext createSiteRenderingContext(final Locale locale) * * @param sink the sink to use for the generation. * @param sinkFactory the sink factory to use for the generation. - * @param locale the wanted locale to generate the report, could be null. + * @param locale the wanted locale to generate the report, could be {@code null}. * @throws MavenReportException if any */ @Override @@ -305,7 +304,7 @@ private void generateReportManually(final Locale locale) throws MavenReportExcep * Generate a report. * * @param sink the sink to use for the generation. - * @param locale the wanted locale to generate the report, could be null. + * @param locale the wanted locale to generate the report, could be {@code null}. * @throws MavenReportException if any * @deprecated use {@link #generate(Sink, SinkFactory, Locale)} instead. */ @@ -341,7 +340,7 @@ protected String getOutputDirectory() { return outputDirectory.getAbsolutePath(); } - protected Renderer getSiteRenderer() { + protected SiteRenderer getSiteRenderer() { return siteRenderer; } @@ -378,13 +377,6 @@ public Sink getSink() { return sink; } - /** - * @return the sink factory used - */ - public SinkFactory getSinkFactory() { - return sinkFactory; - } - /** * @return {@code false} by default. * @see org.apache.maven.reporting.MavenReport#isExternalReport() @@ -441,13 +433,14 @@ protected void executeReport(final Locale locale) throws MavenReportException { config.reportExclusions(logWriter); } ByteArrayOutputStream baos = new ByteArrayOutputStream(); - config.setOut(new ReportConfiguration.IODescriptor("RAT output", () -> baos)); + config.setOut(new ReportConfiguration.IODescriptor<>("RAT output", () -> baos)); Reporter reporter = new Reporter(config); - reporter.output(); + Reporter.Output output = reporter.execute(); if (verbose) { - reporter.writeSummary(logWriter); + output.writeSummary(logWriter); } - sink.text(baos.toString(StandardCharsets.UTF_8.name())); + output.format(config); + sink.text(baos.toString(StandardCharsets.UTF_8)); } catch (IOException | MojoExecutionException | RatException e) { throw new MavenReportException(e.getMessage(), e); } @@ -461,9 +454,9 @@ protected void executeReport(final Locale locale) throws MavenReportException { } /** - * Returns the reports bundle + * Returns the reports bundle. * - * @param locale Requested locale of the bundle + * @param locale Requested locale of the bundle. * @return The bundle, which is used to read localized strings. */ private ResourceBundle getBundle(final Locale locale) { @@ -485,7 +478,7 @@ public String getDescription(final Locale locale) { /** * Returns the reports name. * - * @param locale Requested locale of the bundle + * @param locale Requested locale of the bundle. * @return Report name, as given by the key "report.rat.name" in the bundle. */ @Override diff --git a/apache-rat-tasks/src/main/java/org/apache/rat/anttasks/Report.java b/apache-rat-tasks/src/main/java/org/apache/rat/anttasks/Report.java index cd91a8ecd..e43990340 100644 --- a/apache-rat-tasks/src/main/java/org/apache/rat/anttasks/Report.java +++ b/apache-rat-tasks/src/main/java/org/apache/rat/anttasks/Report.java @@ -29,7 +29,6 @@ import org.apache.commons.cli.Option; import org.apache.commons.io.filefilter.IOFileFilter; -import org.apache.commons.io.output.CloseShieldOutputStream; import org.apache.rat.ConfigurationException; import org.apache.rat.DeprecationReporter; import org.apache.rat.ImplementationException; @@ -443,9 +442,11 @@ public ReportConfiguration getConfiguration() { @Override public void execute() { try { - Reporter r = new Reporter(validate(getConfiguration())); - r.output(StyleSheets.PLAIN.getStyleSheet().ioSupplier(), () -> CloseShieldOutputStream.wrap(System.out)); - r.output(); + ReportConfiguration config = validate(getConfiguration()); + Reporter r = new Reporter(config); + Reporter.Output output = r.execute(); + output.format(StyleSheets.PLAIN.getStyleSheet().ioSupplier(), ReportConfiguration.SYSTEM_OUT.ioSupplier()); + output.format(config); } catch (BuildException e) { throw e; } catch (Exception ioex) { diff --git a/pom.xml b/pom.xml index cfdbc1106..f9eb6754b 100644 --- a/pom.xml +++ b/pom.xml @@ -61,6 +61,7 @@ agnostic home for software distribution comprehension and audit tools. RAT 2.4.1 3.1 + 2.12.0 3.5.1 3.0.0-M3 @@ -267,6 +268,18 @@ agnostic home for software distribution comprehension and audit tools. 1.3.1 test + + org.xmlunit + xmlunit-assertj3 + ${xmlunit.version} + test + + + org.xmlunit + xmlunit-core + ${xmlunit.version} + test + diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 8a9c0913c..e536bd97f 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -68,6 +68,9 @@ in order to be properly linked in site reports. --> + + Internal change: Introduced a Reporter.Output class that encapsulates the execution configuration, the generated XML document, and the ClaimStatistic containing counts for file types, licenses, license categories, and other all of RAT run statistics. + After dropping JDK8 support in r0.18 we rely on ASF parent pom to enforce minimal Maven version 3.9.16 and minimal JDK version 17. Cleanup build and dependency management to rely more on ASF parent pom.