All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.xwiki.rendering.test.cts.CompatibilityTestSuite Maven / Gradle / Ivy

There is a newer version: 16.8.0-rc-1
Show newest version
/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.xwiki.rendering.test.cts;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.junit.runner.Description;
import org.junit.runner.Runner;
import org.junit.runners.Suite;
import org.xwiki.component.manager.ComponentManager;
import org.xwiki.rendering.parser.Parser;
import org.xwiki.rendering.renderer.BlockRenderer;
import org.xwiki.test.jmock.XWikiComponentInitializer;

/**
 * Run all tests found in resources files located in the classpath, for a given Syntax.
 *
 * The algorithm is the following:
 * 
    *
  • Look for {@code cts/[scope]} resources in the classpath where {@code [scope]} represents the value of the * {@code @Scope} annotation prefixed by {@code cts\\.}. By default if no Scope annotation is defined, * {@code .*\\.xml} is used, leading to a total regex of {@code cts\\..*\\.xml}. This is the regex that's used * to look for resources in the classpath. For example the following test file would match: * {@code cts/simple/bold/bold1.inout.xml}. We call these {@code CTS} resources.
  • *
  • For each {@code CTS} resource found look for equivalent test input and output files for the tested Syntax. * For example if we have {@code cts/simple/bold/bold1.inout.xml} then if the Syntax is {@code xwiki/2.0} look * for {@code xwiki20/simple/bold/bold1.[in|out|inout].txt} test files. We call them {@code SYN} resources. *
  • *
  • For each {@code SYN IN} resource, parse it with the corresponding Syntax parser and render the generated XDOM * with the CTS Renderer, and compare the results with the {@code CTS OUT} resource. Note that if no * {@code SYN IN} resource is found generate a warning in the test logs.
  • *
  • For each {@code SYN OUT} resource, parse the {@code CTS IN} resource with the CTS Syntax parser and render the * generated XDOM with the Syntax Renderer, and compare the results with the {@code SYN OUT} resource. * Note that if no {@code SYN OUT} resource is found generate a warning in the test logs.
  • *
* *

* Usage Example *

*

 * @RunWith(CompatibilityTestSuite.class)
 * @Syntax("xwiki/2.0")
 * @Scope("simple")
 * public class IntegrationTests
 * {
 * }
 * 
*

* It's also possible to get access to the underlying Component Manager used, for example in order to register * Mock implementations of components. For example: *

*

 * @RunWith(CompatibilityTestSuite.class)
 * @Syntax("xwiki/2.0")
 * @Scope("simple")
 * public class IntegrationTests
 * {
 *     @Initialized
 *     public void initialize(ComponentManager componentManager)
 *     {
 *         // Init mocks here for example
 *     }
 * }
 * 
* * @version $Id: d7b8432b89dd84bc83464de880868a8c331949f9 $ * @since 4.1M1 */ public class CompatibilityTestSuite extends Suite { /** * Used to locate and parse Test Data. */ private static final TestDataParser PARSER = new TestDataParser(); /** * The Test instance (The Test instance is the class on which this Compatibility Test Suite is used). */ private final Object testInstance; /** * Used to find if there are Parser or Renderers for a given Syntax. */ private final ComponentManager componentManager; /** * We have one Test Runner per Syntax Test to execute, so that each test is reported individually and also to * provide test isolation. */ private final List runners = new ArrayList<>(); /** * Only called reflectively. Do not use programmatically. * * @param klass the test instance class on which this Test Suite is applied * @throws Exception if we fail to locate or load test data, if the {@link RenderingTest} isn't a valid JUnit Test * class or if we cannot locate the Component Manager */ public CompatibilityTestSuite(Class klass) throws Exception { super(RenderingTest.class, Collections.emptyList()); try { this.testInstance = klass.newInstance(); } catch (Exception e) { throw new RuntimeException(String.format("Failed to construct instance of [%s]", klass.getName()), e); } // If a Scope Annotation is present then use it to define the scope Scope scopeAnnotation = klass.getAnnotation(Scope.class); String packageFilter = ""; String pattern = Scope.DEFAULT_PATTERN; if (scopeAnnotation != null) { packageFilter = scopeAnnotation.value(); pattern = scopeAnnotation.pattern(); } // Get the specified Syntax from the Syntax annotation Syntax syntaxAnnotation = klass.getAnnotation(Syntax.class); if (syntaxAnnotation == null) { throw new RuntimeException("You must specify a Syntax using the @Syntax annotation"); } String syntaxId = syntaxAnnotation.value(); String metadataSyntaxId = syntaxAnnotation.metadata(); if (StringUtils.isEmpty(metadataSyntaxId)) { metadataSyntaxId = syntaxId; } // Initialize the Component Manager this.componentManager = new XWikiComponentInitializer().getComponentManager(); // Note: We use the Reflections framework to find all ClassLoader URLs that contain the "cts" package. List testDatas = PARSER.parseTestData(syntaxId, "cts", packageFilter, pattern); for (TestData testData : testDatas) { // The following cases can happen: // - There's no syntax test for the CTS test and there's no Parser/Renderer for that syntax: we don't add // the test at all // - The test is configured to be not applicable: we don't add the test at all // - The test is configured as not working: we ignore it in JUnit with a cause message in the test // description // - There's no syntax test for the CTS test but there's a Parser/Renderer for that syntax: we ignore it in // JUnit with a cause message in the test description if (isApplicable(testData)) { if (testData.syntaxData != null && !testData.isFailingTest()) { this.runners.add(new RenderingTestClassRunner( this.testInstance, getTestClass().getJavaClass(), testData, metadataSyntaxId)); } else { this.runners.add(new IgnoredRenderingTestClassRunner(getTestClass().getJavaClass(), testData)); } } } } @Override protected List getChildren() { return this.runners; } /** * {@inheritDoc} * *

* We override this method so that the JUnit results are not displayed in a test hierarchy with a single test * result for each node (as it would be otherwise since RenderingTest has a single test method). *

*/ @Override public Description getDescription() { return Description.createSuiteDescription(getTestClass().getJavaClass()); } /** * Verify if a test is applicable (ie it should be executed, even as ignored). A test is applicable if: *
    *
  • it's not marked as not applicable
  • *
  • it has a Syntax test
  • *
  • it doesn't have a Syntax test but there's a Parser or Renderer for the Syntax
  • *
* * @param testData the test data used to decide if the test is applicable or not * @return if the test should be executed or false otherwise */ private boolean isApplicable(TestData testData) { boolean isApplicable; if (testData.isNotApplicable()) { isApplicable = false; } else { if (hasParserOrRenderer(testData)) { isApplicable = true; } else { isApplicable = false; } } return isApplicable; } /** * @param testData the test data used to decide if the test has a Parser or Renderer for it * @return true if there's a Parser or Renderer for the passed test data, false otherwise */ private boolean hasParserOrRenderer(TestData testData) { return (testData.isSyntaxInputTest && hasParserForSyntax(testData.syntaxId)) || (!testData.isSyntaxInputTest && hasRendererForSyntax(testData.syntaxId)); } /** * @param syntaxId the syntax for which to verify if there's a Parser * @return true if a Parser exists for the passed syntax, false otherwise */ private boolean hasParserForSyntax(String syntaxId) { return this.componentManager.hasComponent(Parser.class, syntaxId); } /** * @param syntaxId the syntax for which to verify if there's a Renderer * @return true if a Renderer exists for the passed syntax, false otherwise */ private boolean hasRendererForSyntax(String syntaxId) { return this.componentManager.hasComponent(BlockRenderer.class, syntaxId); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy