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 java.io.FileOutputStream;
22 import java.lang.annotation.ElementType;
23 import java.lang.annotation.Retention;
24 import java.lang.annotation.RetentionPolicy;
25 import java.lang.annotation.Target;
26 import java.lang.reflect.InvocationTargetException;
27 import java.lang.reflect.Method;
28 import java.nio.charset.StandardCharsets;
29 import java.nio.file.Path;
30 import java.util.Locale;
31 import java.util.Map;
32 import java.util.TreeMap;
33 import org.apache.commons.cli.Option;
34 import org.apache.commons.cli.ParseException;
35 import org.apache.commons.lang3.tuple.Pair;
36 import org.apache.rat.commandline.ArgumentContext;
37 import org.apache.rat.document.DocumentName;
38 import org.apache.rat.license.LicenseSetFactory;
39 import org.apache.rat.report.Reportable;
40 import org.apache.rat.test.AbstractConfigurationOptionsProvider;
41 import org.apache.rat.test.utils.OptionFormatter;
42 import org.apache.rat.testhelpers.TestingLog;
43 import org.apache.rat.utils.CasedString;
44 import org.apache.rat.utils.DefaultLog;
45 import org.apache.rat.utils.FileUtils;
46 import org.apache.rat.utils.Log;
47 import org.apache.rat.walker.ArchiveWalker;
48 import org.apache.rat.walker.DirectoryWalker;
49 import org.junit.jupiter.api.AfterAll;
50 import org.junit.jupiter.api.Test;
51 import org.junit.jupiter.api.io.CleanupMode;
52 import org.junit.jupiter.api.io.TempDir;
53 import org.junit.jupiter.params.ParameterizedTest;
54 import org.junit.jupiter.params.provider.ArgumentsProvider;
55 import org.junit.jupiter.params.provider.ArgumentsSource;
56 import org.junit.jupiter.params.provider.ValueSource;
57
58 import java.io.File;
59 import java.io.IOException;
60 import java.util.ArrayList;
61 import java.util.Collections;
62 import java.util.List;
63 import java.util.concurrent.atomic.AtomicBoolean;
64
65 import static java.lang.String.format;
66
67 import static org.assertj.core.api.Assertions.assertThat;
68 import static org.assertj.core.api.Fail.fail;
69 import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
70
71 public class OptionCollectionTest {
72 @TempDir(cleanup = CleanupMode.NEVER)
73 static Path testPath;
74
75 @AfterAll
76 static void preserveData() {
77 AbstractConfigurationOptionsProvider.preserveData(testPath.toFile(), "optionTest");
78 }
79
80
81
82
83 @FunctionalInterface
84 public interface OptionTest {
85
86
87
88 void exec();
89
90
91
92
93 default void test() {
94 try {
95 exec();
96 } catch (AssertionError e) {
97 throw new AssertionError(formatMsg(e.getMessage()), e);
98 }
99 }
100
101
102
103
104
105
106 default String formatMsg(String msg) {
107 return String.format("%s: %s", this, msg);
108 }
109
110
111
112
113
114
115 default String name() {
116 return toString();
117 }
118
119
120
121
122
123
124
125 static OptionTest namedTest(String providerName, String name, OptionTest test) {
126 return new OptionTest() {
127 @Override
128 public void exec() {
129 test.exec();
130 }
131 @Override
132 public String toString() {
133 return String.join(":", providerName, name);
134 }
135 @Override
136 public String name() {
137 return name;
138 }
139 };
140 }
141 }
142
143
144
145
146 @Retention(RetentionPolicy.RUNTIME)
147 @Target(ElementType.METHOD)
148 public @interface TestFunction {
149 }
150
151
152
153
154
155 public static Path getTestPath() {
156 return testPath;
157 }
158
159
160
161
162
163
164
165 public static Map<String, OptionTest> processTestFunctionAnnotations(Object testProvider) {
166 final int testLength = 4;
167 final Class<?> clazz = testProvider.getClass();
168 final Map<String, OptionTest> result = new TreeMap<>();
169 for (Method method : clazz.getDeclaredMethods()) {
170 if (method.isAnnotationPresent(TestFunction.class) && method.getParameterCount() == 0 && method.getReturnType() == void.class) {
171 String name = method.getName();
172 if (name.endsWith("Test")) {
173 name = name.substring(0, name.length() - testLength);
174 }
175 if (name.startsWith("test")) {
176 name = name.substring(testLength);
177 }
178 name = new CasedString(CasedString.StringCase.CAMEL, name).toCase(CasedString.StringCase.KEBAB).toLowerCase(Locale.ROOT);
179 result.put(name, OptionTest.namedTest(clazz.getName(), name, () -> {
180 try {
181 method.invoke(testProvider);
182 } catch (IllegalAccessException | InvocationTargetException e) {
183 throw new AssertionError(e);
184 }
185 }
186 ));
187 }
188 }
189 return result;
190 }
191
192 @Test
193 public void testDeprecatedUseLogged() throws IOException {
194 TestingLog log = new TestingLog();
195 try {
196 FileUtils.mkDir(testPath.resolve("target").toFile());
197 DefaultLog.setInstance(log);
198 String[] args = {"--dir", "target", "-a"};
199 ReportConfiguration config = OptionCollection.parseCommands(testPath.toFile(), args, o -> fail("Help printed"), true);
200 assertThat(config).isNotNull();
201 } finally {
202 DefaultLog.setInstance(null);
203 }
204 assertThat(log.getCaptured()).containsOnlyOnce("WARN: Option [-d, --dir] used. Deprecated for removal since 0.17: Use the standard '--'")
205 .containsOnlyOnce("WARN: Option [-a] used. Deprecated for removal since 0.17: Use --edit-license");
206 }
207
208 @Test
209 public void testDirOptionCapturesDirectoryToScan() throws IOException {
210 TestingLog log = new TestingLog();
211 ReportConfiguration config;
212 try {
213 DefaultLog.setInstance(log);
214 String[] args = {"--dir", testPath.toFile().getAbsolutePath()};
215 config = OptionCollection.parseCommands(testPath.toFile(), args, (o) -> {
216 }, true);
217 } finally {
218 DefaultLog.setInstance(null);
219 }
220 assertThat(config).isNotNull();
221 assertThat(log.getCaptured()).containsOnlyOnce("WARN: Option [-d, --dir] used. Deprecated for removal since 0.17: Use the standard '--'");
222 }
223
224 @Test
225 public void testShortenedOptions() throws IOException {
226 String[] args = {"--output-lic", "ALL"};
227 ReportConfiguration config = OptionCollection.parseCommands(testPath.toFile(), args, (o) -> {
228 }, true);
229 assertThat(config).isNotNull();
230 assertThat(config.listLicenses()).isEqualTo(LicenseSetFactory.LicenseFilter.ALL);
231 }
232
233 @Test
234 public void testDefaultConfiguration() throws ParseException {
235 String[] empty = {};
236 ArgumentContext context = new ArgumentContext(new File("."), OptionCollection.buildOptions(), empty);
237 ReportConfiguration config = OptionCollection.createConfiguration(context);
238 ReportConfigurationTest.validateDefault(config);
239 }
240
241 @ParameterizedTest
242 @ValueSource(strings = { ".", "./", "target", "./target" })
243 public void getReportableTest(String fName) throws IOException {
244 File base = new File(fName);
245 DocumentName.FSInfo fsInfo = new DocumentName.FSInfo(testPath.getFileSystem());
246 String expected = fsInfo.normalize(base.getAbsolutePath());
247 ReportConfiguration config = OptionCollection.parseCommands(testPath.toFile(), new String[]{fName}, o -> fail("Help called"), false);
248 Reportable reportable = OptionCollection.getReportable(base, config);
249 assertThat(reportable).as(() -> format("'%s' returned null", fName)).isNotNull();
250 assertThat(reportable.name().getName()).isEqualTo(expected);
251 }
252
253 @Test
254 void getReportable() throws IOException {
255 File dir1 = testPath.resolve("dir1").toFile();
256 assertThat(OptionCollection.getReportable(dir1, new ReportConfiguration())).isNull();
257
258 assertThat(dir1.mkdir()).isTrue();
259 ReportConfiguration reportConfiguration = new ReportConfiguration();
260
261 File dir2 = testPath.resolve("dir2").toFile();
262 reportConfiguration.addExcludedPatterns(List.of(dir2.getName()));
263 assertThat(OptionCollection.getReportable(dir2, reportConfiguration)).isNull();
264
265 Reportable reportable = OptionCollection.getReportable(dir1, new ReportConfiguration());
266 assertThat(reportable).isInstanceOf(DirectoryWalker.class);
267
268 File file1 = new File(dir1, "file1");
269 try (FileOutputStream fos = new FileOutputStream(file1)) {
270 fos.write("Hello world".getBytes(StandardCharsets.UTF_8));
271 }
272 reportable = OptionCollection.getReportable(file1, new ReportConfiguration());
273 assertThat(reportable).isInstanceOf(ArchiveWalker.class);
274 }
275
276
277
278
279
280
281 @ParameterizedTest( name = "{index} {0}")
282 @ArgumentsSource(ArgOptionsProvider.class)
283 public void testOptionsUpdateConfig(String name, OptionTest test) {
284 DefaultLog.getInstance().log(Log.Level.INFO, "Running test for: " + name);
285 test.test();
286 }
287
288
289
290
291 static class ArgOptionsProvider extends AbstractConfigurationOptionsProvider implements ArgumentsProvider {
292
293
294 final AtomicBoolean helpCalled = new AtomicBoolean(false);
295
296
297
298
299 public void helpTest() {
300 String[] args = { OptionFormatter.longOpt(OptionCollection.HELP) };
301
302 assertDoesNotThrow(() -> {
303 ReportConfiguration config = OptionCollection.parseCommands(testPath.toFile(), args, o -> helpCalled.set(true), true);
304 assertThat(config).as("Should not have config").isNull();
305 assertThat(helpCalled.get()).as("Help was not called").isTrue();
306 });
307 }
308
309
310
311
312 public ArgOptionsProvider() {
313 super("ArgsOptionsProvider", Collections.emptyList(), testPath.toFile());
314 addTest(OptionCollectionTest.OptionTest.namedTest(providerName, "help", this::helpTest));
315 }
316
317
318
319
320
321
322
323
324 protected final ReportConfiguration generateConfig(List<Pair<Option, String[]>> args) throws IOException {
325 helpCalled.set(false);
326 List<String> sArgs = new ArrayList<>();
327 for (Pair<Option, String[]> pair : args) {
328 if (pair.getKey() != null) {
329 sArgs.add("--" + pair.getKey().getLongOpt());
330 String[] oArgs = pair.getValue();
331 if (oArgs != null) {
332 Collections.addAll(sArgs, oArgs);
333 }
334 }
335 }
336 ReportConfiguration config = OptionCollection.parseCommands(testPath.toFile(), sArgs.toArray(new String[0]), o -> helpCalled.set(true), true);
337 assertThat(helpCalled.get()).as("Help was called").isFalse();
338 return config;
339 }
340 }
341 }