aboutsummaryrefslogtreecommitdiffstats
path: root/vespa-osgi-testrunner
diff options
context:
space:
mode:
authorJon Marius Venstad <venstad@gmail.com>2021-11-17 21:53:25 +0100
committerJon Marius Venstad <venstad@gmail.com>2021-11-17 21:53:25 +0100
commit68df4a951a952ac1db1129d65641c2e869a2f58f (patch)
tree0b656fac804f955df4f76bf12e8a32231f84eb0a /vespa-osgi-testrunner
parentfbdc8549df12ffd68f470bdbabff72d49951cf61 (diff)
Set up VespaCliTestRunner as well when using new test framework
This should be OK to merge, as the runner will only be enabled if a "tests/" direcctory is present in the artifacts directoy, which should not currently be the case for anything else than our experiments
Diffstat (limited to 'vespa-osgi-testrunner')
-rw-r--r--vespa-osgi-testrunner/pom.xml6
-rw-r--r--vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/VespaCliTestRunner.java148
2 files changed, 154 insertions, 0 deletions
diff --git a/vespa-osgi-testrunner/pom.xml b/vespa-osgi-testrunner/pom.xml
index 845d0d31af4..6ec70b08d39 100644
--- a/vespa-osgi-testrunner/pom.xml
+++ b/vespa-osgi-testrunner/pom.xml
@@ -59,6 +59,12 @@
</dependency>
<dependency>
<groupId>com.yahoo.vespa</groupId>
+ <artifactId>config-provisioning</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.yahoo.vespa</groupId>
<artifactId>testutil</artifactId>
<version>${project.version}</version>
<scope>test</scope>
diff --git a/vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/VespaCliTestRunner.java b/vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/VespaCliTestRunner.java
new file mode 100644
index 00000000000..d8ff6334382
--- /dev/null
+++ b/vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/VespaCliTestRunner.java
@@ -0,0 +1,148 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.testrunner;
+
+import ai.vespa.hosted.api.TestConfig;
+import com.google.inject.Inject;
+import com.yahoo.slime.Cursor;
+import com.yahoo.slime.Slime;
+import com.yahoo.slime.SlimeUtils;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.UncheckedIOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.Optional;
+import java.util.SortedMap;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentSkipListMap;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+import java.util.stream.Stream;
+
+import static com.yahoo.vespa.testrunner.TestRunner.Status.ERROR;
+import static com.yahoo.vespa.testrunner.TestRunner.Status.FAILURE;
+import static com.yahoo.vespa.testrunner.TestRunner.Status.RUNNING;
+import static com.yahoo.vespa.testrunner.TestRunner.Status.SUCCESS;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+/**
+ * @author jonmv
+ */
+public class VespaCliTestRunner implements TestRunner {
+
+ private static final Logger logger = Logger.getLogger(VespaCliTestRunner.class.getName());
+
+ private final SortedMap<Long, LogRecord> log = new ConcurrentSkipListMap<>();
+ private final Path artifactsPath;
+ private AtomicReference<Status> status = new AtomicReference<>(Status.NOT_STARTED);
+
+ @Inject
+ public VespaCliTestRunner(VespaCliTestRunnerConfig config) {
+ this(config.artifactsPath());
+ }
+
+ VespaCliTestRunner(Path artifactsPath) {
+ this.artifactsPath = artifactsPath;
+ }
+
+ @Override
+ public Collection<LogRecord> getLog(long after) {
+ return log.tailMap(after + 1).values();
+ }
+
+ @Override
+ public Status getStatus() {
+ return status.get();
+ }
+
+ @Override
+ public CompletableFuture<?> test(Suite suite, byte[] config) {
+ if (status.getAndSet(RUNNING) == RUNNING)
+ throw new IllegalStateException("Tests already running, not supposed to be started now");
+
+ return CompletableFuture.runAsync(() -> runTests(suite, config));
+ }
+
+ @Override
+ public boolean isSupported() {
+ return getChildDirectory(artifactsPath, "tests").isPresent();
+ }
+
+ void runTests(Suite suite, byte[] config) {
+ Process process = null;
+ try {
+ process = testRunProcessBuilder(suite, toEndpointsConfig(config)).start();
+ BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
+ in.lines().forEach(line -> {
+ if (line.length() > 1 << 13)
+ line = line.substring(0, 1 << 13) + " ... (this log entry was truncated due to size)";
+
+ log(Level.INFO, line, null);
+ });
+ status.set(process.waitFor() == 0 ? SUCCESS : process.exitValue() == 3 ? FAILURE : ERROR);
+ }
+ catch (Exception e) {
+ if (process != null)
+ process.destroyForcibly();
+
+ log(Level.SEVERE, "Failed running tests", e);
+ status.set(ERROR);
+ }
+ }
+
+ private ProcessBuilder testRunProcessBuilder(Suite suite, String endpointsConfig) {
+ Path suitePath = getChildDirectory(artifactsPath, "tests")
+ .flatMap(testsPath -> getChildDirectory(testsPath, toSuiteDirectoryName(suite)))
+ .orElseThrow(() -> new IllegalStateException("No tests found, for suite '" + suite + "'"));
+
+ ProcessBuilder builder = new ProcessBuilder("vespa", "test", "--endpoints", endpointsConfig);
+ builder.redirectErrorStream(true);
+ builder.directory(suitePath.toFile());
+ return builder;
+ }
+
+ private static String toSuiteDirectoryName(Suite suite) {
+ switch (suite) {
+ case SYSTEM_TEST: return "system-test";
+ case STAGING_SETUP_TEST: return "staging-setup";
+ case STAGING_TEST: return "staging-test";
+ default: throw new IllegalArgumentException("Unsupported test suite '" + suite + "'");
+ }
+ }
+
+ private void log(Level level, String message, Throwable thrown) {
+ LogRecord record = new LogRecord(level, message);
+ record.setThrown(thrown);
+ logger.log(record);
+ log.put(record.getSequenceNumber(), record);
+ }
+
+ private static Optional<Path> getChildDirectory(Path parent, String name) {
+ try (Stream<Path> children = Files.list(parent)) {
+ return children.filter(Files::isDirectory)
+ .filter(path -> path.endsWith(name))
+ .findAny();
+ }
+ catch (IOException e) {
+ throw new UncheckedIOException("Failed to list files under " + parent, e);
+ }
+ }
+
+ static String toEndpointsConfig(byte[] testConfig) throws IOException {
+ TestConfig config = TestConfig.fromJson(testConfig);
+ Cursor root = new Slime().setObject();
+ Cursor endpointsArray = root.setArray("endpoints");
+ config.deployments().get(config.zone()).forEach((cluster, url) -> {
+ Cursor endpointObject = endpointsArray.addObject();
+ endpointObject.setString("cluster", cluster);
+ endpointObject.setString("url", url.toString());
+ });
+ return new String(SlimeUtils.toJsonBytes(root), UTF_8);
+ }
+
+}