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