View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one   *
3    * or more contributor license agreements.  See the NOTICE file *
4    * distributed with this work for additional information        *
5    * regarding copyright ownership.  The ASF licenses this file   *
6    * to you under the Apache License, Version 2.0 (the            *
7    * "License"); you may not use this file except in compliance   *
8    * with the License.  You may obtain a copy of the License at   *
9    *                                                              *
10   *   http://www.apache.org/licenses/LICENSE-2.0                 *
11   *                                                              *
12   * Unless required by applicable law or agreed to in writing,   *
13   * software distributed under the License is distributed on an  *
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
15   * KIND, either express or implied.  See the License for the    *
16   * specific language governing permissions and limitations      *
17   * under the License.                                           *
18   */
19  package org.apache.rat.test;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.util.ArrayList;
24  import java.util.Arrays;
25  import java.util.Collection;
26  import java.util.Collections;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.TreeMap;
30  import java.util.stream.Stream;
31  import org.apache.commons.cli.Option;
32  import org.apache.commons.lang3.tuple.Pair;
33  import org.apache.rat.OptionCollectionTest;
34  import org.apache.rat.ReportConfiguration;
35  import org.apache.rat.commandline.Arg;
36  import org.apache.rat.document.DocumentName;
37  import org.apache.rat.document.DocumentNameMatcher;
38  import org.apache.rat.document.DocumentNameMatcherTest;
39  import org.junit.jupiter.api.extension.ExtensionContext;
40  import org.junit.jupiter.params.provider.Arguments;
41  import org.junit.jupiter.params.provider.ArgumentsProvider;
42  import org.junit.jupiter.params.support.ParameterDeclarations;
43  
44  import static org.assertj.core.api.Fail.fail;
45  
46  /**
47   * A list of methods that an OptionsProvider in a test case must support.
48   * Use of this interface ensures consistent testing across the UIs. Each method
49   * tests an option from OptionCollection that must be implemented in the UI.
50   * Each method in this interface tests an Option in {@link org.apache.rat.OptionCollection}.
51   */
52  public abstract class AbstractOptionsProvider implements ArgumentsProvider {
53      /**
54       * A map of test options to tests.
55       */
56      protected final Map<String, OptionCollectionTest.OptionTest> testMap = new TreeMap<>();
57      /** The list of exclude args. */
58      protected static final String[] EXCLUDE_ARGS = {"*.foo", "%regex[[A-Z]\\.bar]", "justbaz"};
59      /** The list of include args. */
60      protected static final String[] INCLUDE_ARGS = {"B.bar", "justbaz"};
61      /**
62       * The directory to place test data in.
63       */
64      protected final File baseDir;
65      /**
66       * The name of the provider of the options.
67       */
68      protected final String providerName;
69  
70      protected void processTestFunctionAnnotations() {
71          testMap.putAll(OptionCollectionTest.processTestFunctionAnnotations(this));
72      }
73  
74      protected void addTest(OptionCollectionTest.OptionTest test) {
75          testMap.put(test.name(), test);
76      }
77  
78      /**
79       * Gets the document name based on the baseDir.
80       * @return The document name based on the baseDir.
81       */
82      protected DocumentName baseName() {
83          return DocumentName.builder(baseDir).build();
84      }
85  
86      protected AbstractOptionsProvider(final String providerName, final File baseDir) {
87          this.providerName = providerName;
88          this.baseDir = baseDir;
89      }
90  
91      private void removeUnsupportedArgs(final Collection<String> unsupportedArgs) {
92          unsupportedArgs.forEach(testMap::remove);
93      }
94  
95      protected void validate(final Collection<String> unsupportedArgs) {
96          removeUnsupportedArgs(unsupportedArgs);
97          verifyAllMethodsDefinedAndNeeded(unsupportedArgs);
98      }
99  
100     private void verifyAllMethodsDefinedAndNeeded(final Collection<String> unsupportedArgs) {
101         // verify all options have functions.
102         final List<String> argNames = new ArrayList<>();
103         Arg.getOptions().getOptions().forEach(o -> {
104             if (o.getLongOpt() != null) {
105                 argNames.add(o.getLongOpt());
106             }
107         });
108         argNames.removeAll(unsupportedArgs);
109         argNames.removeAll(testMap.keySet());
110         if (!argNames.isEmpty()) {
111             fail("Missing methods for: " + String.join(", ", argNames));
112         }
113 
114         // verify all functions have options.
115         argNames.clear();
116         argNames.addAll(testMap.keySet());
117         argNames.remove("help");
118         Arg.getOptions().getOptions().forEach(o -> {
119             if (o.getLongOpt() != null) {
120                 argNames.remove(o.getLongOpt());
121             }
122         });
123         if (!argNames.isEmpty()) {
124             fail("Extra methods defined: " + String.join(", ", argNames));
125         }
126         removeUnsupportedArgs(unsupportedArgs);
127     }
128 
129     @SafeVarargs
130     protected final ReportConfiguration generateConfig(final Pair<Option, String[]>... args) throws IOException {
131         List<Pair<Option, String[]>> options = Arrays.asList(args);
132         return generateConfig(options);
133     }
134 
135     /**
136      * Create the report configuration from the argument pairs.
137      * There must be at least one arg. It may be {@code ImmutablePair.nullPair()}.
138      *
139      * @param args Pairs comprising the argument option and the values for the option.
140      * @return The generated ReportConfiguration.
141      * @throws IOException on error.
142      */
143     protected abstract ReportConfiguration generateConfig(final List<Pair<Option, String[]>> args) throws IOException;
144 
145     /**
146      * Converts each {@code Pair<Option, String[]>} into the option string and its arguments.
147      * @param args the argument paris to process.
148      * @return an array of arguments for the processing.
149      */
150     public static String[] extractArgs(List<Pair<Option, String[]>> args) {
151         List<String> sArgs = new ArrayList<>();
152         for (Pair<Option, String[]> pair : args) {
153             if (pair.getKey() != null) {
154                 sArgs.add("--" + pair.getKey().getLongOpt());
155                 String[] oArgs = pair.getValue();
156                 if (oArgs != null) {
157                     Collections.addAll(sArgs, oArgs);
158                 }
159             }
160         }
161         return sArgs.toArray(new String[0]);
162     }
163 
164     protected File writeFile(final String name, final Iterable<String> lines) {
165         return org.apache.rat.utils.FileUtils.writeFile(baseDir, name, lines);
166     }
167 
168     final protected DocumentName mkDocName(final String name) {
169         return DocumentName.builder(new File(baseDir, name)).build();
170     }
171 
172     /** Display the option and value under test */
173     final protected String displayArgAndName(final Option option, final String fname) {
174         return String.format("%s %s", option.getLongOpt(), fname);
175     }
176 
177     final protected String dump(final DocumentNameMatcher nameMatcher, final DocumentName name) {
178         StringBuilder sb = new StringBuilder();
179         nameMatcher.decompose(name).forEach(s -> sb.append(s).append("\n"));
180         return sb.toString();
181     }
182 
183     final protected String dump(final Option option, final String fname, final DocumentNameMatcher matcher, final DocumentName name) {
184         return String.format("Argument and Name: %s%nMatcher decomposition:%n%s", displayArgAndName(option, fname),
185                 DocumentNameMatcherTest.processDecompose(matcher, name));
186     }
187 
188     @Override
189     public Stream<? extends Arguments> provideArguments(final ParameterDeclarations parameters, final ExtensionContext context) {
190         List<Arguments> lst = new ArrayList<>();
191         List<String> missingTests = new ArrayList<>();
192 
193         for (String key : OptionsList.getKeys()) {
194             OptionCollectionTest.OptionTest test = testMap.get(key);
195             if (test == null) {
196                 missingTests.add(key);
197             } else {
198                 lst.add(Arguments.of(key, test));
199             }
200         }
201         if (!missingTests.isEmpty()) {
202             System.out.println("The following tests are excluded: '" + String.join("', '", missingTests) + "'");
203         }
204         return lst.stream();
205     }
206 }