blob: 24899ef5489c0e3cab02a4ca16b47224b91fd57d [file] [log] [blame]
/*
* 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.ignite.internal.processors.query.calcite.logical;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Pattern;
import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteLogger;
import org.apache.ignite.Ignition;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.internal.IgniteEx;
import org.apache.ignite.internal.IgniteKernal;
import org.apache.ignite.internal.IgnitionEx;
import org.apache.ignite.internal.processors.query.QueryEngine;
import org.apache.ignite.internal.processors.query.calcite.util.Commons;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
import org.apache.ignite.testframework.junits.logger.GridTestLog4jLogger;
import org.apache.ignite.thread.IgniteThread;
import org.junit.runner.Description;
import org.junit.runner.Runner;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunNotifier;
/**
*
*/
public class ScriptTestRunner extends Runner {
/** Filesystem. */
private static final FileSystem FS = FileSystems.getDefault();
/** Shared finder. */
private static final TcpDiscoveryVmIpFinder sharedFinder = new TcpDiscoveryVmIpFinder().setShared(true);
/** */
private static IgniteLogger log;
static {
try {
log = new GridTestLog4jLogger(U.resolveIgnitePath("modules/core/src/test/config/log4j2-test.xml"));
}
catch (Exception e) {
e.printStackTrace(System.err);
log = null;
assert false : "Cannot init logger";
}
}
/** */
private final Class<?> testCls;
/** Scripts root directory. */
private final Path scriptsRoot;
/** Regex to filter test path to run only specified tests. */
private final Pattern testRegex;
/** Nodes count. */
private final int nodes;
/** Restart cluster for each test group. */
private final boolean restartCluster;
/** Test script timeout. */
private final long timeout;
/** */
public ScriptTestRunner(Class<?> testCls) {
this.testCls = testCls;
ScriptRunnerTestsEnvironment env = testCls.getAnnotation(ScriptRunnerTestsEnvironment.class);
assert !F.isEmpty(env.scriptsRoot());
nodes = env.nodes();
scriptsRoot = FS.getPath(U.resolveIgnitePath(env.scriptsRoot()).getPath());
testRegex = F.isEmpty(env.regex()) ? null : Pattern.compile(env.regex());
restartCluster = env.restart();
timeout = env.timeout();
}
/** {@inheritDoc} */
@Override public Description getDescription() {
return Description.createSuiteDescription(testCls.getName(), "scripts");
}
/** {@inheritDoc} */
@Override public void run(final RunNotifier notifier) {
try {
Files.walk(scriptsRoot).sorted().forEach((p) -> {
if (p.equals(scriptsRoot))
return;
if (Files.isDirectory(p)) {
if (!F.isEmpty(Ignition.allGrids()) && restartCluster) {
log.info(">>> Restart cluster");
Ignition.stopAll(false);
}
return;
}
runTest(p, notifier);
});
}
catch (Exception e) {
throw new RuntimeException(e);
}
finally {
Ignition.stopAll(false);
}
}
/** */
private void runTest(Path test, RunNotifier notifier) {
String dirName;
if (test.getNameCount() - 1 > scriptsRoot.getNameCount())
dirName = test.subpath(scriptsRoot.getNameCount(), test.getNameCount() - 1).toString();
else
dirName = scriptsRoot.subpath(scriptsRoot.getNameCount() - 1, scriptsRoot.getNameCount()).toString();
String fileName = test.getFileName().toString();
Description desc = Description.createTestDescription(dirName, fileName);
if (testRegex != null && !testRegex.matcher(test.toString()).find())
return;
if (testRegex == null && !fileName.endsWith(".test") && !fileName.endsWith(".test_slow")) {
if (fileName.endsWith(".test_ignore"))
notifier.fireTestIgnored(desc);
return;
}
beforeTest();
notifier.fireTestStarted(desc);
try {
Ignite ign = F.first(Ignition.allGrids());
QueryEngine engine = Commons.lookupComponent(
((IgniteEx)ign).context(),
QueryEngine.class
);
SqlScriptRunner scriptTestRunner = new SqlScriptRunner(test, engine, log);
log.info(">>> Start: " + dirName + "/" + fileName);
runScript(scriptTestRunner);
}
catch (Throwable e) {
notifier.fireTestFailure(new Failure(desc, e));
}
finally {
log.info(">>> Finish: " + dirName + "/" + fileName);
notifier.fireTestFinished(desc);
}
}
/** */
private void beforeTest() {
if (F.isEmpty(Ignition.allGrids()))
startCluster();
else {
Ignite ign = F.first(Ignition.allGrids());
for (String cacheName : ign.cacheNames())
ign.destroyCache(cacheName);
}
}
/** */
private void startCluster() {
for (int i = 0; i < nodes; ++i) {
Ignition.start(
new IgniteConfiguration()
.setIgniteInstanceName("srv" + i)
.setDiscoverySpi(
new TcpDiscoverySpi()
.setIpFinder(sharedFinder)
)
.setGridLogger(log)
);
}
}
/** */
private void runScript(SqlScriptRunner scriptRunner) throws Throwable {
final AtomicReference<Throwable> ex = new AtomicReference<>();
Thread runner = new IgniteThread("srv0", "test-runner", new Runnable() {
@Override public void run() {
try {
scriptRunner.run();
}
catch (Throwable e) {
ex.set(e);
}
}
});
runner.start();
runner.join(timeout);
if (runner.isAlive()) {
U.error(log,
"Test has been timed out and will be interrupted");
List<Ignite> nodes = IgnitionEx.allGridsx();
for (Ignite node : nodes)
((IgniteKernal)node).dumpDebugInfo();
// We dump threads to stdout, because we can loose logs in case
// the build is cancelled on TeamCity.
U.dumpThreads(null);
U.dumpThreads(log);
// Try to interrupt runner several times for case when InterruptedException is handled invalid.
for (int i = 0; i < 100 && runner.isAlive(); ++i) {
U.interrupt(runner);
U.sleep(10);
}
U.join(runner, log);
// Restart cluster
Ignition.stopAll(true);
startCluster();
throw new TimeoutException("Test has been timed out");
}
Throwable t = ex.get();
if (t != null)
throw t;
}
}