1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package org.xwiki.rendering.test.cts

File TestDataParser.java

 

Coverage histogram

../../../../../img/srcFileCovDistChart10.png
0% of files have more coverage

Code metrics

16
94
9
1
337
181
17
0.18
10.44
9
1.89

Classes

Class Line # Actions
TestDataParser 52 94 0% 17 0
1.0100%
 

Contributing tests

This file is covered by 2 tests. .

Source view

1    /*
2    * See the NOTICE file distributed with this work for additional
3    * information regarding copyright ownership.
4    *
5    * This is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU Lesser General Public License as
7    * published by the Free Software Foundation; either version 2.1 of
8    * the License, or (at your option) any later version.
9    *
10    * This software is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    * Lesser General Public License for more details.
14    *
15    * You should have received a copy of the GNU Lesser General Public
16    * License along with this software; if not, write to the Free
17    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19    */
20    package org.xwiki.rendering.test.cts;
21   
22    import java.io.IOException;
23    import java.net.URL;
24    import java.util.ArrayList;
25    import java.util.Collections;
26    import java.util.List;
27    import java.util.Properties;
28    import java.util.Set;
29    import java.util.TreeSet;
30    import java.util.regex.Pattern;
31   
32    import org.apache.commons.configuration.CompositeConfiguration;
33    import org.apache.commons.configuration.PropertiesConfiguration;
34    import org.apache.commons.io.IOUtils;
35    import org.apache.commons.lang3.StringUtils;
36    import org.apache.commons.lang3.tuple.ImmutablePair;
37    import org.apache.commons.lang3.tuple.Pair;
38    import org.reflections.Reflections;
39    import org.reflections.scanners.ResourcesScanner;
40    import org.reflections.util.ClasspathHelper;
41    import org.reflections.util.ConfigurationBuilder;
42    import org.reflections.util.FilterBuilder;
43   
44    /**
45    * Finds all test files in the current classloader, read them and return test data to represent them. See
46    * {@link CompatibilityTestSuite} for a description of the algorithm.
47    *
48    * @version $Id: 74344b66007edd46508fe1bca530becbbe53576c $
49    * @since 4.1M1
50    * @see CompatibilityTestSuite
51    */
 
52    public class TestDataParser
53    {
54    /**
55    * Represents the Java resource separator.
56    */
57    private static final String SLASH = "/";
58   
59    /**
60    * Represents the dot package separator.
61    */
62    private static final String DOT = ".";
63   
64    /**
65    * Read all test data. See {@link CompatibilityTestSuite} for a detailed explanation of the algorithm.
66    *
67    * @param syntaxId the id of the syntax for which to parse data for
68    * @param ctsRootPackageName the root of the CTS resources
69    * @param packageFilter the regex to filter packages
70    * @param pattern a regex to decide which {@code *.xml} resources should be found. The default should be to
71    * find them all
72    * @return the list of test data
73    * @throws Exception in case of error while reading test data
74    */
 
75  18 toggle public List<TestData> parseTestData(String syntaxId, String ctsRootPackageName, String packageFilter,
76    String pattern) throws Exception
77    {
78  18 ClassLoader classLoader = getClass().getClassLoader();
79   
80  18 List<TestData> data = new ArrayList<TestData>();
81  18 String syntaxDirectory = computeSyntaxDirectory(syntaxId);
82   
83    // Read the suite-level Configuration data.
84  18 TestDataConfiguration configuration = parseTestConfiguration(syntaxDirectory, ctsRootPackageName, classLoader);
85   
86  18 Set<String> relativeDirectoryNames = findRelativeTestDirectoryNames(ctsRootPackageName, packageFilter, pattern);
87  18 for (String relativeDirectoryName : relativeDirectoryNames) {
88  630 List<TestData> testDatas = parseSingleTestData(syntaxDirectory, ctsRootPackageName, relativeDirectoryName,
89    configuration, classLoader);
90  630 for (TestData testData : testDatas) {
91  1281 testData.syntaxId = syntaxId;
92  1281 testData.prefix = relativeDirectoryName;
93  1281 testData.configuration = configuration;
94  1281 data.add(testData);
95    }
96    }
97  18 return data;
98    }
99   
100    /**
101    * Parse data for single test.
102    *
103    * @param syntaxDirectory the syntax directory from where to read syntax test data (eg "xwiki20" for "xwiki/2.0"
104    * syntax)
105    * @param ctsRootPackageName the root of the CTS resources
106    * @param relativeDirectoryName the name of the relative directory for a CTS test (eg "/simple/bold/bold1")
107    * @param configuration the test configuration
108    * @param classLoader the class loader from which the test data is read from
109    * @return the TestData instances for both input and output tests, including possible input alias tests
110    * @throws IOException in case of error while reading test data
111    */
 
112  630 toggle public List<TestData> parseSingleTestData(String syntaxDirectory, String ctsRootPackageName,
113    String relativeDirectoryName, TestDataConfiguration configuration, ClassLoader classLoader) throws IOException
114    {
115    // Look for syntax-specific input/output file and read their content
116  630 TestData testDataIN = new TestData();
117  630 testDataIN.isSyntaxInputTest = true;
118  630 TestData testDataOUT = new TestData();
119   
120    // Look for CTS input/output file and read their contents
121  630 Pair<Pair<String, String>, Pair<String, String>> ctsData =
122    readDataForPrefix(ctsRootPackageName + SLASH + relativeDirectoryName, "xml", classLoader);
123   
124  630 Pair<Pair<String, String>, Pair<String, String>> syntaxData = readDataForPrefix(
125    syntaxDirectory + SLASH + relativeDirectoryName, configuration.fileExtension, classLoader);
126   
127  630 testDataIN.syntaxData = syntaxData.getLeft().getLeft();
128  630 testDataIN.syntaxExtension = syntaxData.getLeft().getRight();
129  630 testDataIN.ctsData = ctsData.getRight().getLeft();
130  630 testDataIN.ctsExtension = ctsData.getRight().getRight();
131  630 testDataOUT.syntaxData = syntaxData.getRight().getLeft();
132  630 testDataOUT.syntaxExtension = syntaxData.getRight().getRight();
133  630 testDataOUT.ctsData = ctsData.getLeft().getLeft();
134  630 testDataOUT.ctsExtension = ctsData.getLeft().getRight();
135   
136    // Read possible "in" aliases
137  630 List<TestData> testDataINAliases = parseAliasesTestData(syntaxDirectory, relativeDirectoryName, ctsData,
138    configuration, classLoader);
139   
140    // If the inherit configuration property is set and if the returned syntax is empty load from the inherit
141    // syntax.
142  630 if (configuration.inheritSyntax != null) {
143  185 Pair<Pair<String, String>, Pair<String, String>> inheritedSyntaxData = readDataForPrefix(
144    computeSyntaxDirectory(configuration.inheritSyntax) + SLASH + relativeDirectoryName,
145    configuration.fileExtension,
146    classLoader);
147  185 if (testDataIN.syntaxData == null) {
148  175 testDataIN.syntaxData = inheritedSyntaxData.getLeft().getLeft();
149  175 testDataIN.syntaxExtension = inheritedSyntaxData.getLeft().getRight();
150    }
151  185 if (testDataOUT.syntaxData == null) {
152  157 testDataOUT.syntaxData = inheritedSyntaxData.getRight().getLeft();
153  157 testDataOUT.syntaxExtension = inheritedSyntaxData.getRight().getRight();
154    }
155    }
156   
157  630 List<TestData> result = new ArrayList<TestData>();
158  630 result.add(testDataIN);
159  630 result.addAll(testDataINAliases);
160  630 result.add(testDataOUT);
161  630 return result;
162    }
163   
164    /**
165    * Parse Alias test data for inputs.
166    *
167    * @param syntaxDirectory the syntax directory from where to read syntax test data (eg "xwiki20" for "xwiki/2.0"
168    * syntax)
169    * @param relativeDirectoryName the name of the relative directory for a CTS test (eg "/simple/bold/bold1")
170    * @param ctsData the CTS data to use to construct the alias test data
171    * @param configuration the test configuration
172    * @param classLoader the class loader from which the test data is read from
173    * @return the TestData instances for both input and output tests, including possible input alias tests
174    * @throws IOException in case of error while reading test data
175    */
 
176  630 toggle private List<TestData> parseAliasesTestData(String syntaxDirectory, String relativeDirectoryName,
177    Pair<Pair<String, String>, Pair<String, String>> ctsData, TestDataConfiguration configuration,
178    ClassLoader classLoader) throws IOException
179    {
180  630 List<TestData> testDataINAliases = new ArrayList<TestData>();
181  630 Pair<Pair<String, String>, Pair<String, String>> syntaxDataAlias;
182  630 int i = 1;
183  630 do {
184  651 syntaxDataAlias = readDataForPrefix(
185    syntaxDirectory + SLASH + relativeDirectoryName, i + DOT + configuration.fileExtension, classLoader);
186  651 if (syntaxDataAlias.getLeft().getLeft() != null) {
187  21 TestData testDataINAlias = new TestData();
188  21 testDataINAlias.isSyntaxInputTest = true;
189  21 testDataINAlias.syntaxData = syntaxDataAlias.getLeft().getLeft();
190  21 testDataINAlias.syntaxExtension = syntaxDataAlias.getLeft().getRight();
191  21 testDataINAlias.ctsData = ctsData.getRight().getLeft();
192  21 testDataINAlias.ctsExtension = ctsData.getRight().getRight();
193  21 testDataINAliases.add(testDataINAlias);
194    }
195  651 i++;
196  651 } while (syntaxDataAlias.getLeft().getLeft() != null);
197  630 return testDataINAliases;
198    }
199   
200    /**
201    * Parse Test configuration by looking for a {@code config.properties} file in the Syntax directory.
202    *
203    * @param syntaxDirectory the syntax directory under which to look for the configuration file
204    * @param ctsRootPackageName the root of the CTS resources
205    * @param classLoader the class loader from which the test configuration is read from
206    * @return the configuration
207    * @throws Exception in case of error while reading test configuration
208    */
 
209  18 toggle public TestDataConfiguration parseTestConfiguration(String syntaxDirectory, String ctsRootPackageName,
210    ClassLoader classLoader) throws Exception
211    {
212  18 TestDataConfiguration configuration = new TestDataConfiguration();
213   
214  18 CompositeConfiguration compositeConfiguration = new CompositeConfiguration();
215  18 addConfigurationData(compositeConfiguration, ctsRootPackageName, classLoader);
216  18 addConfigurationData(compositeConfiguration, syntaxDirectory, classLoader);
217   
218    // TODO: Remove these unsafe casts, need to find out how to do that nicely...
219  18 configuration.notApplicableTests =
220    (List<String>) (List<?>) compositeConfiguration.getList("notApplicableTests", Collections.emptyList());
221  18 configuration.failingTests =
222    (List<String>) (List<?>) compositeConfiguration.getList("failingTests", Collections.emptyList());
223  18 configuration.testDescriptions = compositeConfiguration.getProperties("testDescriptions", new Properties());
224  18 configuration.inheritSyntax = compositeConfiguration.getString("inheritSyntax");
225  18 configuration.fileExtension = compositeConfiguration.getString("fileExtension", "txt");
226   
227  18 return configuration;
228    }
229   
230    /**
231    * Add Configuration Data loaded from "config.properties" resources.
232    *
233    * @param configuration the composite configuration to add to
234    * @param rootPackageName the package where the configuration properties file is located
235    * @param classLoader the class loader from which the configuration is read from
236    * @throws Exception in case of error while reading test configuration
237    */
 
238  36 toggle private void addConfigurationData(CompositeConfiguration configuration, String rootPackageName,
239    ClassLoader classLoader) throws Exception
240    {
241  36 URL configurationURL = classLoader.getResource(rootPackageName + "/config.properties");
242  36 if (configurationURL != null) {
243  33 configuration.addConfiguration(new PropertiesConfiguration(configurationURL));
244    }
245    }
246   
247    /**
248    * Read both input and output test data.
249    *
250    * @param prefix the prefix where to look for to read the test data
251    * @param fileExtension the test data file extension to look for
252    * @param classLoader the class loader from which the test data is read from
253    * @return the input and output test content along with their extensions
254    * @throws IOException in case of error while reading test data
255    */
 
256  2096 toggle private Pair<Pair<String, String>, Pair<String, String>> readDataForPrefix(String prefix, String fileExtension,
257    ClassLoader classLoader) throws IOException
258    {
259  2096 String in;
260  2096 String out;
261  2096 String inExtension = ".inout." + fileExtension;
262  2096 String outExtension = inExtension;
263  2096 String inOut = readData(prefix + inExtension, classLoader);
264  2096 if (inOut == null) {
265  1307 inExtension = ".in." + fileExtension;
266  1307 outExtension = ".out." + fileExtension;
267  1307 in = readData(prefix + inExtension, classLoader);
268  1307 out = readData(prefix + outExtension, classLoader);
269    } else {
270  789 in = inOut;
271  789 out = inOut;
272    }
273   
274  2096 return new ImmutablePair<Pair<String, String>, Pair<String, String>>(
275    new ImmutablePair<String, String>(in, inExtension),
276    new ImmutablePair<String, String>(out, outExtension));
277    }
278   
279    /**
280    * @param resourceName the resource to load
281    * @param classLoader the class loader from which the test data is read from
282    * @return the test content or null if not found
283    * @throws IOException in case of error while reading test data
284    */
 
285  4710 toggle private String readData(String resourceName, ClassLoader classLoader) throws IOException
286    {
287  4710 String input = null;
288   
289  4710 URL inputURL = classLoader.getResource(resourceName);
290  4710 if (inputURL != null) {
291  1218 input = IOUtils.toString(inputURL);
292    }
293  4710 return input;
294    }
295   
296    /**
297    * Find {@code *.xml} files in the classpath and return the list of all resources found, without their filename
298    * extensions. For example if <code>{ctsDirectoryName}/simple/bold/bold1.*.xml</code> is found, return
299    * {@code simple/bold/bold1}.
300    *
301    * @param ctsRootPackageName the root of the CTS resources
302    * @param packageFilter the regex to filter packages
303    * @param pattern a regex to decide which {@code *.xml} resources should be found. The default should be to find
304    * them all
305    * @return the list of relative test directories found
306    */
 
307  19 toggle public Set<String> findRelativeTestDirectoryNames(String ctsRootPackageName, String packageFilter, String pattern)
308    {
309  19 Reflections reflections = new Reflections(new ConfigurationBuilder()
310    .setScanners(new ResourcesScanner())
311    .setUrls(ClasspathHelper.forPackage(ctsRootPackageName))
312    .filterInputsBy(new FilterBuilder.Include(FilterBuilder.prefix(ctsRootPackageName + DOT + packageFilter))));
313   
314  19 Set<String> prefixes = new TreeSet<String>();
315  19 for (String fullTestDirectoryName : reflections.getResources(Pattern.compile(pattern))) {
316    // Remove the prefix and trailing extension
317  631 String testDirectoryName = StringUtils.substringAfter(fullTestDirectoryName, ctsRootPackageName + SLASH);
318  631 testDirectoryName = StringUtils.substringBeforeLast(testDirectoryName, ".inout.xml");
319  631 prefixes.add(testDirectoryName);
320    }
321   
322  19 return prefixes;
323    }
324   
325    /**
326    * Normalize a syntax directory by replacing removing "/" and "." characters. For example "xwiki/2.0" becomes
327    * "xwiki20".
328    *
329    * @param syntaxId the syntax id from which to compute a syntax directory
330    * @return the computed syntax directory
331    */
 
332  203 toggle private String computeSyntaxDirectory(String syntaxId)
333    {
334    // Remove "/" and "."
335  203 return syntaxId.replace(SLASH, "").replace(DOT, "");
336    }
337    }