/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.sling.launchpad.webapp.integrationtest; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import java.io.File; import java.io.FileInputStream; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.apache.sling.launchpad.webapp.integrationtest.util.ServerSideTestClient; import org.codehaus.plexus.util.Expand; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Execute all server-side test scripts found in a specified * (class) resource folder. */ @RunWith(Parameterized.class) public class ServerSideScriptsTest { /** Script directory default value */ private static String TEST_SCRIPT_DIR_DEFAULT = "scripts/sling-it"; /** Script directory for assumed to fail scripting tests default value */ private static String TEST_SCRIPT_DIR_FAIL_DEFAULT = "scripts/sling-it/expected-to-fail"; /** The resource type prefix for the uploaded test script folder */ private static final String RESOURCE_TYPE_PREFIX = "testing/sling/scripted-tests"; /** Logger. */ private final Logger logger = LoggerFactory.getLogger(this.getClass()); private static final class Definition { public final File testScriptFile; public final String testName; public final String scriptExtension; public final boolean willFail; public Definition(final File file, final boolean willFail) { final String name = file.getName(); final int pos = name.lastIndexOf('.'); this.scriptExtension = name.substring(pos); final String prefix = (willFail ? "fail-" : ""); this.testName = prefix + name.substring(0, pos); this.testScriptFile = file; this.willFail = willFail; } @Override public String toString() { return testScriptFile.getName(); } } private static class Collector { private final List<Definition> tests = new ArrayList<Definition>(); private final Logger logger = LoggerFactory.getLogger(this.getClass()); Collector() { addScripts(TEST_SCRIPT_DIR_DEFAULT, false); addScripts(TEST_SCRIPT_DIR_FAIL_DEFAULT, true); } /** * Get the directory for a resource path * @param resourcePath The resource path pointing to the script directory * @return A file object if the path points to a directory */ private File getScriptDirectory(final String resourcePath) { final URL url = ServerSideScriptsTest.class.getClassLoader().getResource(resourcePath); if (url != null) { if ( url.getProtocol().equals("file") ) { URI uri = null; try { uri = url.toURI(); final File dir = new File(uri); if ( dir.exists() && dir.isDirectory() ) { return dir; } } catch (final URISyntaxException e) { logger.info("Failed to get scripts from " + url , e); // ignore } } else if ( url.getProtocol().equals("jar") ) { final String urlString = url.toString(); final int pos = urlString.indexOf('!'); try { final String jarFilePath = urlString.substring(4, pos); final URL jarURL = new URL(jarFilePath); final URI uri = jarURL.toURI(); // create a temp dir final File baseDir = new File(System.getProperty("java.io.tmpdir")); final File tempDir = new File(baseDir, System.currentTimeMillis() + ".dir"); if (!tempDir.mkdir()) { throw new IllegalStateException("Failed to create temporary directory"); } tempDir.deleteOnExit(); final Expand expander = new Expand(); expander.setDest(tempDir); expander.setSrc(new File(uri)); expander.execute(); final File dir = new File(tempDir, resourcePath); if ( dir.exists() && dir.isDirectory() ) { return dir; } } catch (final Exception e) { logger.info("Script path is not readable: " + urlString, e); } } else { logger.info("Script path is in unknown url protocol: " + resourcePath + " - " + url); } } else { logger.info("Script path not found " + resourcePath); } return null; } /** * Collect all scripts of a directory specified by the resource path. * * @param resourcePath The resource path pointing to the script directory * @param willFail <code>false</code> if this test is expected to succeed, <code>true</code> otherwise */ private void addScripts(final String resourcePath, final boolean willFail) { final File scriptDir = getScriptDirectory(resourcePath); if ( scriptDir != null && scriptDir.list() != null && scriptDir.list().length > 0) { for(final File f : scriptDir.listFiles()) { if ( !f.isHidden() ) { if ( f.isFile() ) { logger.info("Found test script {}", f.getAbsolutePath()); final Definition test = new Definition(f, willFail); this.tests.add(test); } } } } else { logger.info("No test scripts found with resource path {}", resourcePath); } } List<Definition> getTests() { return tests; } }; private final ServerSideTestClient slingClient; private final Definition test; @Parameters(name="{index} - {0}") public static Collection<Object[]> data() { final List<Object []> result = new ArrayList<Object []>(); for(Definition t : new Collector().getTests()) { result.add(new Object[] { t }); } return result; } public ServerSideScriptsTest(Definition scriptableTest) { this.test = scriptableTest; this.slingClient = new ServerSideTestClient(); } @Before public void setup() throws Exception { slingClient.mkdirs("/apps/" + RESOURCE_TYPE_PREFIX); } @After public void cleanup() throws Exception { slingClient.delete("/apps/" + RESOURCE_TYPE_PREFIX); } @Test public void runScripts() throws Exception { final String resourceType = RESOURCE_TYPE_PREFIX + '/' + test.testName; final String scriptPath = "/apps/" + resourceType; String toDelete = null; try { // create test node this.slingClient.createNode(scriptPath, "jcr:primaryType", "sling:Folder", "jcr:mixinTypes", "sling:Test", "sling:resourceType", RESOURCE_TYPE_PREFIX + '/' + test.testName); toDelete = scriptPath; final String destPath = scriptPath + "/test.txt" + test.scriptExtension; logger.info("Setting up node {} for {}", destPath, test.testScriptFile.getAbsoluteFile()); this.slingClient.upload(destPath, new FileInputStream(test.testScriptFile), -1, false); // SLING-3087 with Oak, the Sling JUnit's scriptable module TestAllPaths class // might still see the old script, and not yet the new one, for some time. // There's probably a better way to avoid this...for now just wait a bit Thread.sleep(2000L); final long startTime = System.currentTimeMillis(); final ServerSideTestClient.TestResults results = slingClient.runTests("org.apache.sling.junit.scriptable.ScriptableTestsProvider"); assertEquals("Expecting 1 scriptable test", 1, results.getTestCount()); final int failureCount = test.willFail ? 1 : 0; if( results.getFailures().size() != failureCount) { fail("Expected " + failureCount + " failing tests but got " + results.getFailures().size() + " for " + test.testScriptFile.getAbsolutePath() + ": " + results.getFailures()); } logger.info("Execution of {} took {} msec", test, System.currentTimeMillis() - startTime); } finally { if(toDelete != null) { this.slingClient.delete(toDelete); } } } }