1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.rat;
20
21 import static org.assertj.core.api.Assertions.assertThat;
22 import static org.assertj.core.api.Assertions.assertThatThrownBy;
23 import static org.assertj.core.api.Fail.fail;
24
25 import java.io.ByteArrayOutputStream;
26 import java.io.File;
27 import java.io.IOException;
28 import java.io.PrintStream;
29 import java.lang.reflect.Method;
30 import java.net.URISyntaxException;
31 import java.nio.charset.StandardCharsets;
32
33 import java.nio.file.Files;
34 import java.nio.file.Path;
35 import java.util.Arrays;
36 import java.util.Collections;
37 import java.util.HashMap;
38 import java.util.List;
39 import java.util.Map;
40 import java.util.Optional;
41 import java.util.TreeMap;
42 import java.util.UUID;
43 import java.util.stream.Stream;
44 import java.util.regex.Pattern;
45 import javax.xml.XMLConstants;
46 import javax.xml.transform.Source;
47 import javax.xml.transform.TransformerException;
48 import javax.xml.transform.dom.DOMSource;
49 import javax.xml.transform.stream.StreamSource;
50 import javax.xml.validation.Schema;
51 import javax.xml.validation.SchemaFactory;
52 import javax.xml.validation.Validator;
53 import javax.xml.xpath.XPath;
54 import javax.xml.xpath.XPathConstants;
55
56 import org.apache.commons.io.FileUtils;
57 import org.apache.commons.xml.secure.SecureSchemaFactory;
58 import org.apache.commons.xml.secure.SecureXPathFactory;
59 import org.apache.rat.api.Document.Type;
60 import org.apache.rat.api.RatException;
61 import org.apache.rat.commandline.Arg;
62 import org.apache.rat.commandline.ArgumentContext;
63 import org.apache.rat.document.FileDocument;
64 import org.apache.rat.document.DocumentName;
65 import org.apache.rat.license.ILicenseFamily;
66 import org.apache.rat.report.claim.ClaimStatistic;
67 import org.apache.rat.report.claim.ClaimStatisticTest;
68 import org.apache.rat.test.utils.Resources;
69 import org.apache.rat.testhelpers.BaseOption;
70 import org.apache.rat.testhelpers.BaseOptionCollection;
71 import org.apache.rat.testhelpers.XmlUtils;
72 import org.apache.rat.testhelpers.data.ReportTestDataProvider;
73 import org.apache.rat.testhelpers.data.TestData;
74 import org.apache.rat.testhelpers.data.ValidatorData;
75 import org.apache.rat.utils.StandardXmlFactory;
76 import org.apache.rat.walker.DirectoryWalker;
77 import org.junit.jupiter.api.AfterAll;
78 import org.junit.jupiter.api.BeforeAll;
79 import org.junit.jupiter.api.BeforeEach;
80 import org.junit.jupiter.api.Test;
81 import org.junit.jupiter.api.TestInfo;
82 import org.junit.jupiter.params.ParameterizedTest;
83 import org.junit.jupiter.params.provider.Arguments;
84 import org.junit.jupiter.params.provider.MethodSource;
85 import org.w3c.dom.Document;
86 import org.w3c.dom.NodeList;
87 import org.xml.sax.SAXException;
88
89
90
91
92 public class ReporterTest {
93
94
95
96 private static Path tempPath;
97
98
99
100
101 private Path testPath;
102
103
104
105
106 final String basedir;
107
108 private final OptionCollectionParser<BaseOption> collectionParser;
109
110 ReporterTest() throws URISyntaxException {
111 basedir = Resources.getExampleResource("exampleData").getPath();
112 collectionParser = new OptionCollectionParser<>(new BaseOptionCollection());
113 }
114
115 @BeforeAll
116 static void setUp() throws IOException {
117 tempPath = Files.createTempDirectory("ReporterTest");
118 Arg.reset();
119 }
120
121 @AfterAll
122 static void cleanup() throws IOException {
123 FileUtils.deleteDirectory(tempPath.toFile());
124 }
125
126 @BeforeEach
127 void setUpTest(TestInfo testInfo) {
128 Optional<Method> testMethod = testInfo.getTestMethod();
129 this.testPath = tempPath.resolve(testMethod.map(Method::getName).orElseGet(() -> UUID.randomUUID().toString()));
130 File file = testPath.toFile();
131 if (!file.exists()) {
132 if (!file.mkdirs()) {
133 throw new RuntimeException("Unable to create temp directory: " + file.getName());
134 }
135 } else {
136 if (!file.isDirectory()) {
137 throw new RuntimeException(file.getName() + " is NOT a directory.");
138 }
139 }
140 }
141
142 @Test
143 void testExecute() throws RatException {
144 File output = testPath.resolve("output.xml").toFile();
145 ArgumentContext ctxt = collectionParser.parseCommands(new File("."), new String[]{"--output-style", "xml", "--output-file", output.getPath(), basedir});
146 ClaimStatistic statistic = new Reporter(ctxt.getConfiguration()).execute().getStatistic();
147
148 assertThat(statistic.getCounter(Type.ARCHIVE)).isEqualTo(1);
149 assertThat(statistic.getCounter(Type.BINARY)).isEqualTo(2);
150 assertThat(statistic.getCounter(Type.IGNORED)).isEqualTo(2);
151 assertThat(statistic.getCounter(Type.NOTICE)).isEqualTo(2);
152 assertThat(statistic.getCounter(Type.STANDARD)).isEqualTo(8);
153 assertThat(statistic.getCounter(Type.UNKNOWN)).isEqualTo(0);
154 assertThat(statistic.getCounter(ClaimStatistic.Counter.APPROVED)).isEqualTo(8);
155 assertThat(statistic.getCounter(ClaimStatistic.Counter.ARCHIVES)).isEqualTo(1);
156 assertThat(statistic.getCounter(ClaimStatistic.Counter.BINARIES)).isEqualTo(2);
157 assertThat(statistic.getCounter(ClaimStatistic.Counter.DOCUMENT_TYPES)).isEqualTo(5);
158 assertThat(statistic.getCounter(ClaimStatistic.Counter.IGNORED)).isEqualTo(2);
159 assertThat(statistic.getCounter(ClaimStatistic.Counter.LICENSE_CATEGORIES)).isEqualTo(4);
160 assertThat(statistic.getCounter(ClaimStatistic.Counter.LICENSE_NAMES)).isEqualTo(5);
161 assertThat(statistic.getCounter(ClaimStatistic.Counter.NOTICES)).isEqualTo(2);
162 assertThat(statistic.getCounter(ClaimStatistic.Counter.STANDARDS)).isEqualTo(8);
163 assertThat(statistic.getCounter(ClaimStatistic.Counter.UNAPPROVED)).isEqualTo(2);
164 assertThat(statistic.getCounter(ClaimStatistic.Counter.UNKNOWN)).isEqualTo(2);
165
166 List<Type> typeList = statistic.getDocumentTypes();
167 assertThat(typeList).isEqualTo(Arrays.asList(Type.ARCHIVE, Type.BINARY, Type.IGNORED, Type.NOTICE, Type.STANDARD));
168
169 TreeMap<String, Integer> expected = new TreeMap<>();
170 expected.put("Unknown license", 2);
171 expected.put("Apache License 2.0", 5);
172 expected.put("The MIT License", 1);
173 expected.put("BSD 3 clause", 1);
174 expected.put("The Telemanagement Forum License", 1);
175 TreeMap<String, Integer> actual = new TreeMap<>();
176
177 for (String licenseName : statistic.getLicenseNames()) {
178 actual.put(licenseName, statistic.getLicenseNameCount(licenseName));
179 }
180 assertThat(actual).isEqualTo(expected);
181
182 expected.clear();
183 expected.put("?????", 2);
184 expected.put("AL ", 5);
185 expected.put("BSD-3", 2);
186 expected.put("MIT ", 1);
187 actual.clear();
188 for (String licenseCategory : statistic.getLicenseFamilyCategories()) {
189 actual.put(licenseCategory, statistic.getLicenseCategoryCount(licenseCategory));
190 }
191 assertThat(actual).isEqualTo(expected);
192 }
193
194 @Test
195 void testExecuteNoSource() throws RatException, TransformerException {
196 File output = testPath.resolve("testExecuteNoSource").toFile();
197 ArgumentContext context = collectionParser.parseCommands(new File("."), new String[]{"--output-style", "xml", "--output-file", output.getPath()});
198 ReportConfiguration config = OptionCollection.createConfiguration(context);
199 Reporter.Output result = new Reporter(config).execute();
200 assertThat(StandardXmlFactory.serializeDocument(result.getDocument())).isEmpty();
201 ClaimStatisticTest.assertSame(result.getStatistic(), new ClaimStatistic());
202 }
203
204 @Test
205 void testOutputOption() throws Exception {
206 File output = testPath.resolve("testOutputOption.txt").toFile();
207 ArgumentContext ctxt = collectionParser.parseCommands(new File("."), new String[]{"--output-file", output.getCanonicalPath(), basedir});
208 new Reporter(ctxt.getConfiguration()).execute().format(ctxt.getConfiguration());
209 assertThat(output.exists()).isTrue();
210 String content = FileUtils.readFileToString(output, StandardCharsets.UTF_8);
211 assertThat(content).containsPattern(Pattern.compile("^! Unapproved:\\s*2 ", Pattern.MULTILINE))
212 .contains("/Source.java")
213 .contains("/sub/Empty.txt");
214 }
215
216 @Test
217 void testGetOutputMethod() throws Exception {
218 File output = testPath.resolve("testGetOutputMethod.txt").toFile();
219 ArgumentContext context = collectionParser.parseCommands(new File("."), new String[]{"-o", output.getCanonicalPath(), basedir});
220 Reporter reporter = new Reporter(context.getConfiguration());
221 Reporter.Output expected = reporter.execute();
222 assertThat(reporter.getOutput()).isEqualTo(expected);
223 }
224
225 @Test
226 void testDefaultOutput() throws Exception {
227 File output = testPath.resolve("captured.txt").toFile();
228
229 PrintStream origin = System.out;
230 try (PrintStream out = new PrintStream(output)) {
231 System.setOut(out);
232 ArgumentContext ctxt = collectionParser.parseCommands(new File("."), new String[]{basedir});
233 new Reporter(ctxt.getConfiguration()).execute().format(ctxt.getConfiguration());
234 } finally {
235 System.setOut(origin);
236 }
237 assertThat(output).exists();
238 String content = FileUtils.readFileToString(output, StandardCharsets.UTF_8);
239 verifyStandardContent(content);
240 }
241
242 private static Map<String, String> mapOf(String... parts) {
243 Map<String, String> map = new HashMap<>();
244 for (int i = 0; i < parts.length; i+=2) {
245 map.put(parts[i], parts[i+1]);
246 }
247 return map;
248 }
249
250 @Test
251 void testXMLOutput() throws Exception {
252 Map<String, Map<String, String>> expected = new HashMap<>();
253 expected.put("/.hiddenDirectory", mapOf("isDirectory", "true", "mediaType", "application/octet-stream",
254 "type", "IGNORED"));
255 expected.put("/ILoggerFactory.java", mapOf("encoding", "windows-1252", "mediaType", "text/x-java-source",
256 "type", "STANDARD"));
257 expected.put("/Image.png", mapOf("mediaType", "image/png", "type", "BINARY"));
258 expected.put("/LICENSE", mapOf("encoding", "windows-1252", "mediaType", "text/plain", "type", "NOTICE"));
259 expected.put("/NOTICE", mapOf("encoding", "windows-1252", "mediaType", "text/plain", "type", "NOTICE"));
260 expected.put("/Source.java", mapOf("encoding", "windows-1252", "mediaType", "text/x-java-source",
261 "type", "STANDARD"));
262 expected.put("/Text.txt", mapOf("encoding", "windows-1252", "mediaType", "text/plain",
263 "type", "STANDARD"));
264 expected.put("/TextHttps.txt", mapOf("encoding", "windows-1252", "mediaType", "text/plain",
265 "type", "STANDARD"));
266 expected.put("/Xml.xml", mapOf("encoding", "windows-1252", "mediaType", "application/xml",
267 "type", "STANDARD"));
268 expected.put("/buildr.rb", mapOf("encoding", "windows-1252", "mediaType", "text/x-ruby",
269 "type", "STANDARD"));
270 expected.put("/dummy.jar", mapOf("mediaType", "application/java-archive",
271 "type", "ARCHIVE"));
272 expected.put("/generated.txt", mapOf("encoding", "windows-1252", "mediaType", "text/plain",
273 "type", "IGNORED"));
274 expected.put("/plain.json", mapOf("mediaType", "application/json",
275 "type", "BINARY"));
276 expected.put("/sub/Empty.txt", mapOf("encoding", "windows-1252", "mediaType", "text/plain",
277 "type", "STANDARD"));
278 expected.put("/tri.txt", mapOf("encoding", "windows-1252", "mediaType", "text/plain",
279 "type", "STANDARD"));
280
281 File output = testPath.resolve(".rat/testXMLOutput").toFile();
282 org.apache.rat.utils.FileUtils.mkDir(output.getParentFile());
283 ArgumentContext ctxt = collectionParser.parseCommands(testPath.toFile(), new String[]{"--output-style", "xml", "--output-file", output.getPath(), basedir});
284 new Reporter(ctxt.getConfiguration()).execute().format(ctxt.getConfiguration());
285
286 assertThat(output).exists();
287 Document doc = XmlUtils.toDom(java.nio.file.Files.newInputStream(output.toPath()));
288 XPath xPath = SecureXPathFactory.newInstance().newXPath();
289
290 for (Map.Entry<String, Map<String, String>> entry : expected.entrySet()) {
291 XmlUtils.assertAttributes(doc, xPath, String.format("/rat-report/resource[@name='%s']", entry.getKey()), entry.getValue());
292 }
293
294 NodeList nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource/license[@approval='false']");
295 assertThat(nodeList.getLength()).isEqualTo(2);
296
297 nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource/license[@id='AL2.0']");
298 assertThat(nodeList.getLength()).isEqualTo(5);
299
300 nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource/license[@id='MIT']");
301 assertThat(nodeList.getLength()).isEqualTo(1);
302
303 nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource/license[@id='BSD-3']");
304 assertThat(nodeList.getLength()).isEqualTo(1);
305
306 nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource/license[@id='TMF']");
307 assertThat(nodeList.getLength()).isEqualTo(1);
308
309 nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource/license[@id='?????']");
310 assertThat(nodeList.getLength()).isEqualTo(2);
311
312 nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource[@type='STANDARD']");
313 assertThat(nodeList.getLength()).isEqualTo(8);
314
315 nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource[@type='ARCHIVE']");
316 assertThat(nodeList.getLength()).isEqualTo(1);
317
318 nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource[@type='BINARY']");
319 assertThat(nodeList.getLength()).isEqualTo(2);
320
321 nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource[@type='IGNORED']");
322 assertThat(nodeList.getLength()).isEqualTo(2);
323
324 nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource[@type='UNKNOWN']");
325 assertThat(nodeList.getLength()).isEqualTo(0);
326
327 nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource[@type='NOTICE']");
328 assertThat(nodeList.getLength()).isEqualTo(2);
329
330 nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource[@type='IGNORED']/license");
331 assertThat(nodeList.getLength()).isEqualTo(0);
332
333 nodeList = XmlUtils.getNodeList(doc, xPath, "/rat-report/resource[@isDirectory='true']");
334 assertThat(nodeList.getLength()).isEqualTo(1);
335
336
337 Map<String, String> apacheLicense = mapOf("approval", "true", "family", "AL ", "id", "AL2.0", "name", "Apache License 2.0");
338 Map<String, String> unknownLicense = mapOf("approval", "false", "family", "?????",
339 "id", "?????", "name", "Unknown license");
340 expected = new HashMap<>();
341 expected.put("/.hiddenDirectory", Collections.emptyMap());
342 expected.put("/ILoggerFactory.java", mapOf("approval", "true", "family", "MIT ",
343 "id", "MIT", "name", "The MIT License"));
344 expected.put("/Image/png", Collections.emptyMap());
345 expected.put("/LICENSE", Collections.emptyMap());
346 expected.put("/NOTICE", Collections.emptyMap());
347 expected.put("/Source.java", unknownLicense);
348 expected.put("/Text.txt", apacheLicense);
349 expected.put("/TextHttps.txt", apacheLicense);
350 expected.put("/Xml.xml", apacheLicense);
351 expected.put("/buildr.rb", apacheLicense);
352 expected.put("/dummy.jar", Collections.emptyMap());
353 expected.put("/generated.txt", Collections.emptyMap());
354 expected.put("/plain.json", Collections.emptyMap());
355 expected.put("/sub/Empty.txt", unknownLicense);
356 expected.put("/tri.txt", mapOf("approval", "true", "family", "BSD-3", "id", "TMF",
357 "name", "The Telemanagement Forum License"));
358
359 for (Map.Entry<String, Map<String, String>> entry : expected.entrySet()) {
360 Map<String, String> attrs = entry.getValue();
361 if (attrs.isEmpty()) {
362 String xpath = String.format("/rat-report/resource[@name='%s']/license",entry.getKey());
363 nodeList = XmlUtils.getNodeList(doc, xPath, xpath);
364 assertThat(nodeList.getLength()).as(xpath).isEqualTo(0);
365 } else {
366 XmlUtils.assertAttributes(doc, xPath, String.format("/rat-report/resource[@name='%s']/license[@id='%s']",
367 entry.getKey(), attrs.get("id")), attrs);
368 }
369 }
370 }
371
372
373
374
375
376
377
378
379
380
381
382
383 private static void checkNode(final Document doc, final XPath xpath, final String resource, final LicenseInfo licenseInfo,
384 final String type) throws Exception {
385 XmlUtils.getNode(doc, xpath, String.format("/rat-report/resource[@name='%s'][@type='%s']", resource, type));
386 if (licenseInfo != null) {
387 XmlUtils.getNode(doc, xpath,
388 String.format("/rat-report/resource[@name='%s'][@type='%s']/license[@id='%s'][@family='%s']",
389 resource, type, licenseInfo.id, licenseInfo.family));
390 XmlUtils.getNode(doc, xpath,
391 String.format("/rat-report/resource[@name='%s'][@type='%s']/license[@id='%s'][@approval='%s']",
392 resource, type, licenseInfo.id, licenseInfo.approval));
393 if (licenseInfo.hasNotes) {
394 XmlUtils.getNode(doc, xpath,
395 String.format("/rat-report/resource[@name='%s'][@type='%s']/license[@id='%s']/notes", resource,
396 type, licenseInfo.id));
397 }
398 }
399 }
400
401 private ReportConfiguration initializeConfiguration() throws URISyntaxException {
402 Defaults defaults = Defaults.builder().build();
403 final File elementsFile = Resources.getExampleResource("exampleData");
404 final ReportConfiguration configuration = new ReportConfiguration();
405 configuration.setFrom(defaults);
406 DocumentName documentName = DocumentName.builder(elementsFile).build();
407 configuration.addSource(new DirectoryWalker(new FileDocument(documentName, elementsFile,
408 configuration.getDocumentExcluder(documentName))));
409 return configuration;
410 }
411
412 private void verifyStandardContent(final String document) {
413 assertThat(document)
414 .containsPattern(Pattern.compile("^ {2}Notices:\\s*2 ", Pattern.MULTILINE))
415 .containsPattern(Pattern.compile("^ {2}Binaries:\\s*2 ", Pattern.MULTILINE))
416 .containsPattern(Pattern.compile("^ {2}Archives:\\s*1 ", Pattern.MULTILINE))
417 .containsPattern(Pattern.compile("^ {2}Standards:\\s*8 ", Pattern.MULTILINE))
418 .containsPattern(Pattern.compile("^ {2}Ignored:\\s*2 ", Pattern.MULTILINE))
419 .containsPattern(Pattern.compile("^! Unapproved:\\s*2 ", Pattern.MULTILINE))
420 .containsPattern(Pattern.compile("^ {2}Unknown:\\s*2 ", Pattern.MULTILINE))
421 .containsPattern(Pattern.compile("^Apache License 2.0: 5 ", Pattern.MULTILINE))
422 .containsPattern(Pattern.compile("^BSD 3 clause: 1 ", Pattern.MULTILINE))
423 .containsPattern(Pattern.compile("^The MIT License: 1 ", Pattern.MULTILINE))
424 .containsPattern(Pattern.compile("^The Telemanagement Forum License: 1 ", Pattern.MULTILINE))
425 .containsPattern(Pattern.compile("^Unknown license: 2 ", Pattern.MULTILINE))
426 .containsPattern(Pattern.compile("^\\Q?????\\E: 2 ", Pattern.MULTILINE))
427 .containsPattern(Pattern.compile("^AL {3}: 5 ", Pattern.MULTILINE))
428 .containsPattern(Pattern.compile("^BSD-3: 2 ", Pattern.MULTILINE))
429 .containsPattern(Pattern.compile("^MIT {2}: 1 ", Pattern.MULTILINE))
430 .containsPattern(
431 Pattern.compile("^Files with unapproved licenses\\s+\\*+\\s+"
432 + "\\Q/Source.java\\E\\s+"
433 + "\\Q/sub/Empty.txt\\E\\s", Pattern.MULTILINE))
434 .containsPattern(Pattern.compile(ReporterTestUtils.documentOut(true, Type.ARCHIVE, "/dummy.jar"), Pattern.MULTILINE))
435 .containsPattern(
436 Pattern.compile(ReporterTestUtils.documentOut(true, Type.STANDARD, "/ILoggerFactory.java")
437 + ReporterTestUtils.licenseOut("MIT", "The MIT License"), Pattern.MULTILINE))
438 .containsPattern(Pattern.compile(ReporterTestUtils.documentOut(true, Type.BINARY, "/Image.png"), Pattern.MULTILINE))
439 .containsPattern(Pattern.compile(ReporterTestUtils.documentOut(true, Type.NOTICE, "/LICENSE"), Pattern.MULTILINE))
440 .containsPattern(Pattern.compile(ReporterTestUtils.documentOut(true, Type.NOTICE, "/NOTICE"), Pattern.MULTILINE))
441 .containsPattern(Pattern.compile(ReporterTestUtils.documentOut(false, Type.STANDARD, "/Source.java")
442 + ReporterTestUtils.UNKNOWN_LICENSE, Pattern.MULTILINE))
443 .containsPattern(Pattern.compile(ReporterTestUtils.documentOut(true, Type.STANDARD, "/Text.txt")
444 + ReporterTestUtils.APACHE_LICENSE, Pattern.MULTILINE))
445 .containsPattern(Pattern.compile(ReporterTestUtils.documentOut(true, Type.STANDARD, "/Xml.xml")
446 + ReporterTestUtils.APACHE_LICENSE, Pattern.MULTILINE))
447 .containsPattern(Pattern.compile(ReporterTestUtils.documentOut(true, Type.STANDARD, "/buildr.rb")
448 + ReporterTestUtils.APACHE_LICENSE, Pattern.MULTILINE))
449 .containsPattern(Pattern.compile(ReporterTestUtils.documentOut(true, Type.STANDARD, "/TextHttps.txt")
450 + ReporterTestUtils.APACHE_LICENSE, Pattern.MULTILINE))
451 .containsPattern(Pattern.compile(ReporterTestUtils.documentOut(true, Type.STANDARD, "/tri.txt")
452 + ReporterTestUtils.APACHE_LICENSE + ReporterTestUtils.licenseOut("BSD-3", "BSD 3 clause")
453 + ReporterTestUtils.licenseOut("BSD-3", "TMF", "The Telemanagement Forum License"), Pattern.MULTILINE))
454 .containsPattern(Pattern.compile(ReporterTestUtils.documentOut(false, Type.STANDARD, "/sub/Empty.txt")
455 + ReporterTestUtils.UNKNOWN_LICENSE, Pattern.MULTILINE));
456 }
457
458 private Validator initValidator() throws SAXException {
459 SchemaFactory factory = SecureSchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
460 Source schemaFile = new StreamSource(Reporter.class.getResourceAsStream("/org/apache/rat/rat-report.xsd"));
461 Schema schema = factory.newSchema(schemaFile);
462 return schema.newValidator();
463 }
464
465 @Test
466 void xmlReportTest() throws Exception {
467 ReportConfiguration configuration = initializeConfiguration();
468 Document doc = new Reporter(configuration).execute().getDocument();
469
470 XPath xPath = SecureXPathFactory.newInstance().newXPath();
471
472 XmlUtils.getNode(doc, xPath, "/rat-report[@timestamp]");
473
474 LicenseInfo apacheLic = new LicenseInfo("AL2.0", "AL", true, false);
475 checkNode(doc, xPath, "/ILoggerFactory.java", new LicenseInfo("MIT", true, false),
476 "STANDARD");
477 checkNode(doc, xPath, "/Image.png", null, "BINARY");
478 checkNode(doc, xPath, "/LICENSE", null, "NOTICE");
479 checkNode(doc, xPath, "/NOTICE", null, "NOTICE");
480 checkNode(doc, xPath, "/Source.java", new LicenseInfo("?????", false, false),
481 "STANDARD");
482 checkNode(doc, xPath, "/Text.txt", apacheLic, "STANDARD");
483 checkNode(doc, xPath, "/TextHttps.txt", apacheLic, "STANDARD");
484 checkNode(doc, xPath, "/Xml.xml", apacheLic, "STANDARD");
485 checkNode(doc, xPath, "/buildr.rb", apacheLic, "STANDARD");
486 checkNode(doc, xPath, "/dummy.jar", null, "ARCHIVE");
487 checkNode(doc, xPath, "/sub/Empty.txt", new LicenseInfo("?????", false, false),
488 "STANDARD");
489 checkNode(doc, xPath, "/tri.txt", apacheLic, "STANDARD");
490 checkNode(doc, xPath, "/tri.txt", new LicenseInfo("BSD-3", true, false), "STANDARD");
491 checkNode(doc, xPath, "/tri.txt", new LicenseInfo("TMF", "BSD-3", true, false),
492 "STANDARD");
493 checkNode(doc, xPath, "/generated.txt", null, "IGNORED");
494 NodeList nodeList = (NodeList) xPath.compile("/rat-report/resource").evaluate(doc, XPathConstants.NODESET);
495 assertThat(nodeList.getLength()).isEqualTo(15);
496 Validator validator = initValidator();
497 try {
498 validator.validate(new DOMSource(doc));
499 } catch (SAXException e) {
500 fail("Missing properties?", e);
501 }
502 }
503
504 @Test
505 void plainReportTest() throws Exception {
506 final String NL = System.lineSeparator();
507 final String SEPARATOR = "*****************************************************";
508 final String HEADER = SEPARATOR + NL +
509 "Summary" + NL +
510 SEPARATOR + NL +
511 "Generated at: ";
512 ByteArrayOutputStream out = new ByteArrayOutputStream();
513 ReportConfiguration configuration = initializeConfiguration();
514 configuration.setOutput(new ReportConfiguration.IODescriptor<>("plainReportTest", () -> out));
515 new Reporter(configuration).execute().format(configuration);
516
517 String document = out.toString();
518
519 assertThat(document).as(() -> "'Generated at' is not present in \n" + document)
520 .doesNotContainPattern("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
521 .startsWith(HEADER);
522
523 verifyStandardContent(document);
524 }
525
526 @Test
527 void unapprovedLicensesReportTest() throws Exception {
528 ByteArrayOutputStream out = new ByteArrayOutputStream();
529 ReportConfiguration configuration = initializeConfiguration();
530 configuration.setOutput(new ReportConfiguration.IODescriptor<>("unapprovedLicensesReportTest", () -> out));
531 configuration.setStyleSheet(this.getClass().getResource("/org/apache/rat/unapproved-licenses.xsl"));
532 new Reporter(configuration).execute().format(configuration);
533
534 String document = out.toString();
535
536 assertThat(document).containsOnlyOnce("Generated at: ")
537 .containsPattern(Pattern.compile("\\Q/Source.java\\E$", Pattern.MULTILINE))
538 .containsPattern(Pattern.compile("\\Q/sub/Empty.txt\\E", Pattern.MULTILINE));
539 }
540
541 @Test
542 void counterMaxTest() throws Exception {
543 ReportConfiguration config = initializeConfiguration();
544 Reporter.Output output = new Reporter(config).execute();
545 assertThat(config.getClaimValidator().hasErrors()).isTrue();
546 assertThat(config.getClaimValidator().isValid(ClaimStatistic.Counter.UNAPPROVED, output.getStatistic().getCounter(ClaimStatistic.Counter.UNAPPROVED)))
547 .isFalse();
548
549 config = initializeConfiguration();
550 config.getClaimValidator().setMax(ClaimStatistic.Counter.UNAPPROVED, 2);
551 output = new Reporter(config).execute();
552 assertThat(config.getClaimValidator().hasErrors()).isFalse();
553 assertThat(config.getClaimValidator().isValid(ClaimStatistic.Counter.UNAPPROVED, output.getStatistic().getCounter(ClaimStatistic.Counter.UNAPPROVED)))
554 .isTrue();
555 }
556
557 static Stream<Arguments> getTestData() {
558 BaseOptionCollection.Builder builder = BaseOptionCollection.builder()
559 .unsupported(Arg.OUTPUT_FILE);
560 return new ReportTestDataProvider().getOptionTests(builder.build()).stream().map(testData ->
561 Arguments.of(testData.getTestName(), testData));
562 }
563
564 @ParameterizedTest( name = "{index} {0}")
565 @MethodSource("getTestData")
566 void testReportData(String name, TestData test) throws Exception {
567 Path invokePath = testPath.resolve(test.getTestName());
568 org.apache.rat.utils.FileUtils.mkDir(invokePath.toFile());
569
570 test.setupFiles(invokePath);
571 ArgumentContext ctxt = collectionParser.parseCommands(invokePath.toFile(),
572 test.getCommandLine(invokePath.toString()));
573 if (test.expectingException()) {
574 assertThatThrownBy(() -> new Reporter(ctxt.getConfiguration()).execute()).as("Expected throws from " + name)
575 .hasMessageContaining(test.getExpectedException().getMessage());
576 ValidatorData data = new ValidatorData(Reporter.Output.builder().configuration(ctxt.getConfiguration()).build(),
577 invokePath.toString());
578 test.getValidator().accept(data);
579 } else {
580 Reporter.Output output = ctxt.getConfiguration() != null ? new Reporter(ctxt.getConfiguration()).execute() :
581 Reporter.Output.builder().build();
582 ValidatorData data = new ValidatorData(output, invokePath.toString());
583 data.getOutput().format(data.getConfiguration());
584 test.getValidator().accept(data);
585 }
586 }
587
588 private record LicenseInfo(String id, String family, boolean approval, boolean hasNotes) {
589 LicenseInfo(String id, boolean approval, boolean hasNotes) {
590 this(id, id, approval, hasNotes);
591 }
592
593 private LicenseInfo(String id, String family, boolean approval, boolean hasNotes) {
594 this.id = id;
595 this.family = ILicenseFamily.makeCategory(family);
596 this.approval = approval;
597 this.hasNotes = hasNotes;
598 }
599 }
600 }