Class | Line # | Actions | |||||
---|---|---|---|---|---|---|---|
CompatibilityTestSuite | 89 | 36 | 0% | 16 | 7 |
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.util.ArrayList; | |
23 | import java.util.Collections; | |
24 | import java.util.List; | |
25 | ||
26 | import org.apache.commons.lang3.StringUtils; | |
27 | import org.junit.runner.Description; | |
28 | import org.junit.runner.Runner; | |
29 | import org.junit.runners.Suite; | |
30 | import org.xwiki.component.manager.ComponentManager; | |
31 | import org.xwiki.rendering.parser.Parser; | |
32 | import org.xwiki.rendering.renderer.BlockRenderer; | |
33 | import org.xwiki.test.jmock.XWikiComponentInitializer; | |
34 | ||
35 | /** | |
36 | * Run all tests found in resources files located in the classpath, for a given Syntax. | |
37 | * | |
38 | * The algorithm is the following: | |
39 | * <ul> | |
40 | * <li>Look for {@code cts/[scope]} resources in the classpath where {@code [scope]} represents the value of the | |
41 | * {@code @Scope} annotation prefixed by {@code cts\\.}. By default if no Scope annotation is defined, | |
42 | * {@code .*\\.xml} is used, leading to a total regex of {@code cts\\..*\\.xml}. This is the regex that's used | |
43 | * to look for resources in the classpath. For example the following test file would match: | |
44 | * {@code cts/simple/bold/bold1.inout.xml}. We call these {@code CTS} resources.</li> | |
45 | * <li>For each {@code CTS} resource found look for equivalent test input and output files for the tested Syntax. | |
46 | * For example if we have {@code cts/simple/bold/bold1.inout.xml} then if the Syntax is {@code xwiki/2.0} look | |
47 | * for {@code xwiki20/simple/bold/bold1.[in|out|inout].txt} test files. We call them {@code SYN} resources. | |
48 | * </li> | |
49 | * <li>For each {@code SYN IN} resource, parse it with the corresponding Syntax parser and render the generated XDOM | |
50 | * with the CTS Renderer, and compare the results with the {@code CTS OUT} resource. Note that if no | |
51 | * {@code SYN IN} resource is found generate a warning in the test logs.</li> | |
52 | * <li>For each {@code SYN OUT} resource, parse the {@code CTS IN} resource with the CTS Syntax parser and render the | |
53 | * generated XDOM with the Syntax Renderer, and compare the results with the {@code SYN OUT} resource. | |
54 | * Note that if no {@code SYN OUT} resource is found generate a warning in the test logs.</li> | |
55 | * </ul> | |
56 | * | |
57 | * <p> | |
58 | * Usage Example | |
59 | * </p> | |
60 | * <pre><code> | |
61 | * @RunWith(CompatibilityTestSuite.class) | |
62 | * @Syntax("xwiki/2.0") | |
63 | * @Scope("simple") | |
64 | * public class IntegrationTests | |
65 | * { | |
66 | * } | |
67 | * </code></pre> | |
68 | * <p> | |
69 | * It's also possible to get access to the underlying Component Manager used, for example in order to register | |
70 | * Mock implementations of components. For example: | |
71 | * </p> | |
72 | * <pre><code> | |
73 | * @RunWith(CompatibilityTestSuite.class) | |
74 | * @Syntax("xwiki/2.0") | |
75 | * @Scope("simple") | |
76 | * public class IntegrationTests | |
77 | * { | |
78 | * @Initialized | |
79 | * public void initialize(ComponentManager componentManager) | |
80 | * { | |
81 | * // Init mocks here for example | |
82 | * } | |
83 | * } | |
84 | * </code></pre> | |
85 | * | |
86 | * @version $Id: c521120a3eadec5ee8e55f02ff137e7841cf8df9 $ | |
87 | * @since 4.1M1 | |
88 | */ | |
89 | public class CompatibilityTestSuite extends Suite | |
90 | { | |
91 | /** | |
92 | * Used to locate and parse Test Data. | |
93 | */ | |
94 | private static final TestDataParser PARSER = new TestDataParser(); | |
95 | ||
96 | /** | |
97 | * The Test instance (The Test instance is the class on which this Compatibility Test Suite is used). | |
98 | */ | |
99 | private final Object testInstance; | |
100 | ||
101 | /** | |
102 | * Used to find if there are Parser or Renderers for a given Syntax. | |
103 | */ | |
104 | private final ComponentManager componentManager; | |
105 | ||
106 | /** | |
107 | * We have one Test Runner per Syntax Test to execute, so that each test is reported individually and also to | |
108 | * provide test isolation. | |
109 | */ | |
110 | private final List<Runner> runners = new ArrayList<Runner>(); | |
111 | ||
112 | /** | |
113 | * Only called reflectively. Do not use programmatically. | |
114 | * | |
115 | * @param klass the test instance class on which this Test Suite is applied | |
116 | * @throws Exception if we fail to locate or load test data, if the {@link RenderingTest} isn't a valid JUnit Test | |
117 | * class or if we cannot locate the Component Manager | |
118 | */ | |
119 | 17 | ![]() |
120 | { | |
121 | 17 | super(RenderingTest.class, Collections.<Runner>emptyList()); |
122 | ||
123 | 17 | try { |
124 | 17 | this.testInstance = klass.newInstance(); |
125 | } catch (Exception e) { | |
126 | 0 | throw new RuntimeException(String.format("Failed to construct instance of [%s]", klass.getName()), e); |
127 | } | |
128 | ||
129 | // If a Scope Annotation is present then use it to define the scope | |
130 | 17 | Scope scopeAnnotation = klass.getAnnotation(Scope.class); |
131 | 17 | String packageFilter = ""; |
132 | 17 | String pattern = Scope.DEFAULT_PATTERN; |
133 | 17 | if (scopeAnnotation != null) { |
134 | 0 | packageFilter = scopeAnnotation.value(); |
135 | 0 | pattern = scopeAnnotation.pattern(); |
136 | } | |
137 | ||
138 | // Get the specified Syntax from the Syntax annotation | |
139 | 17 | Syntax syntaxAnnotation = klass.getAnnotation(Syntax.class); |
140 | 17 | if (syntaxAnnotation == null) { |
141 | 0 | throw new RuntimeException("You must specify a Syntax using the @Syntax annotation"); |
142 | } | |
143 | 17 | String syntaxId = syntaxAnnotation.value(); |
144 | 17 | String metadataSyntaxId = syntaxAnnotation.metadata(); |
145 | 17 | if (StringUtils.isEmpty(metadataSyntaxId)) { |
146 | 17 | metadataSyntaxId = syntaxId; |
147 | } | |
148 | ||
149 | // Initialize the Component Manager | |
150 | 17 | this.componentManager = new XWikiComponentInitializer().getComponentManager(); |
151 | ||
152 | // Note: We use the Reflections framework to find all ClassLoader URLs that contain the "cts" package. | |
153 | 17 | List<TestData> testDatas = PARSER.parseTestData(syntaxId, "cts", packageFilter, pattern); |
154 | ||
155 | 17 | for (TestData testData : testDatas) { |
156 | // The following cases can happen: | |
157 | // - There's no syntax test for the CTS test and there's no Parser/Renderer for that syntax: we don't add | |
158 | // the test at all | |
159 | // - The test is configured to be not applicable: we don't add the test at all | |
160 | // - The test is configured as not working: we ignore it in JUnit with a cause message in the test | |
161 | // description | |
162 | // - There's no syntax test for the CTS test but there's a Parser/Renderer for that syntax: we ignore it in | |
163 | // JUnit with a cause message in the test description | |
164 | 1277 | if (isApplicable(testData)) { |
165 | 804 | if (testData.syntaxData != null && !testData.isFailingTest()) { |
166 | 537 | this.runners.add(new RenderingTestClassRunner( |
167 | this.testInstance, getTestClass().getJavaClass(), testData, metadataSyntaxId)); | |
168 | } else { | |
169 | 267 | this.runners.add(new IgnoredRenderingTestClassRunner(getTestClass().getJavaClass(), testData)); |
170 | } | |
171 | } | |
172 | } | |
173 | } | |
174 | ||
175 | 17 | ![]() |
176 | protected List<Runner> getChildren() | |
177 | { | |
178 | 17 | return this.runners; |
179 | } | |
180 | ||
181 | /** | |
182 | * {@inheritDoc} | |
183 | * | |
184 | * <p> | |
185 | * We override this method so that the JUnit results are not displayed in a test hierarchy with a single test | |
186 | * result for each node (as it would be otherwise since RenderingTest has a single test method). | |
187 | * </p> | |
188 | */ | |
189 | 34 | ![]() |
190 | public Description getDescription() | |
191 | { | |
192 | 34 | return Description.createSuiteDescription(getTestClass().getJavaClass()); |
193 | } | |
194 | ||
195 | /** | |
196 | * Verify if a test is applicable (ie it should be executed, even as ignored). A test is applicable if: | |
197 | * <ul> | |
198 | * <li>it's not marked as not applicable</li> | |
199 | * <li>it has a Syntax test</li> | |
200 | * <li>it doesn't have a Syntax test but there's a Parser or Renderer for the Syntax</li> | |
201 | * </ul> | |
202 | * | |
203 | * @param testData the test data used to decide if the test is applicable or not | |
204 | * @return if the test should be executed or false otherwise | |
205 | */ | |
206 | 1277 | ![]() |
207 | { | |
208 | 1277 | boolean isApplicable; |
209 | 1277 | if (testData.isNotApplicable()) { |
210 | 83 | isApplicable = false; |
211 | } else { | |
212 | 1194 | if (hasParserOrRenderer(testData)) { |
213 | 804 | isApplicable = true; |
214 | } else { | |
215 | 390 | isApplicable = false; |
216 | } | |
217 | } | |
218 | 1277 | return isApplicable; |
219 | } | |
220 | ||
221 | /** | |
222 | * @param testData the test data used to decide if the test has a Parser or Renderer for it | |
223 | * @return true if there's a Parser or Renderer for the passed test data, false otherwise | |
224 | */ | |
225 | 1194 | ![]() |
226 | { | |
227 | 1194 | return (testData.isSyntaxInputTest && hasParserForSyntax(testData.syntaxId)) |
228 | || (!testData.isSyntaxInputTest && hasRendererForSyntax(testData.syntaxId)); | |
229 | } | |
230 | ||
231 | /** | |
232 | * @param syntaxId the syntax for which to verify if there's a Parser | |
233 | * @return true if a Parser exists for the passed syntax, false otherwise | |
234 | */ | |
235 | 588 | ![]() |
236 | { | |
237 | 588 | return this.componentManager.hasComponent(Parser.class, syntaxId); |
238 | } | |
239 | ||
240 | /** | |
241 | * @param syntaxId the syntax for which to verify if there's a Renderer | |
242 | * @return true if a Renderer exists for the passed syntax, false otherwise | |
243 | */ | |
244 | 606 | ![]() |
245 | { | |
246 | 606 | return this.componentManager.hasComponent(BlockRenderer.class, syntaxId); |
247 | } | |
248 | } |