summaryrefslogtreecommitdiffstats
path: root/vespa-osgi-testrunner
diff options
context:
space:
mode:
authorJon Marius Venstad <venstad@gmail.com>2022-02-22 16:04:16 +0100
committerJon Marius Venstad <venstad@gmail.com>2022-02-22 16:04:16 +0100
commit5d606e1287e754b427cca21c6e5565cb78a36ba0 (patch)
tree50c06b3465312e3ccdc37fbf9b0c43cf1bfd2633 /vespa-osgi-testrunner
parentdac253d8882407898cbeb0e57339e18135c8defd (diff)
Add NO_TESTS status to simplify detecting that no tests are available
Diffstat (limited to 'vespa-osgi-testrunner')
-rw-r--r--vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/AggregateTestRunner.java31
-rw-r--r--vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/JunitRunner.java16
-rw-r--r--vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/TestReport.java3
-rw-r--r--vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/TestRunner.java28
-rw-r--r--vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/VespaCliTestRunner.java31
-rw-r--r--vespa-osgi-testrunner/src/test/java/com/yahoo/vespa/testrunner/AggregateTestRunnerTest.java39
-rw-r--r--vespa-osgi-testrunner/src/test/java/com/yahoo/vespa/testrunner/TestRunnerHandlerTest.java24
-rw-r--r--vespa-osgi-testrunner/src/test/java/com/yahoo/vespa/testrunner/VespaCliTestRunnerTest.java11
8 files changed, 66 insertions, 117 deletions
diff --git a/vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/AggregateTestRunner.java b/vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/AggregateTestRunner.java
index 5637d25df71..bc4b776568a 100644
--- a/vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/AggregateTestRunner.java
+++ b/vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/AggregateTestRunner.java
@@ -7,24 +7,13 @@ import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger;
-import java.util.logging.Level;
import java.util.logging.LogRecord;
-import static java.util.stream.Collectors.toUnmodifiableList;
-
/**
* @author jonmv
*/
public class AggregateTestRunner implements TestRunner {
- static final TestRunner noRunner = new TestRunner() {
- final LogRecord record = new LogRecord(Level.WARNING, "No tests were found");
- @Override public Collection<LogRecord> getLog(long after) { return List.of(record); }
- @Override public Status getStatus() { return Status.FAILURE; }
- @Override public CompletableFuture<?> test(Suite suite, byte[] config) { return CompletableFuture.completedFuture(null); }
- @Override public boolean isSupported() { return true; }
- };
-
private final List<TestRunner> wrapped;
private final AtomicInteger current = new AtomicInteger(-1);
@@ -33,8 +22,7 @@ public class AggregateTestRunner implements TestRunner {
}
public static TestRunner of(Collection<TestRunner> testRunners) {
- List<TestRunner> supported = testRunners.stream().filter(TestRunner::isSupported).collect(toUnmodifiableList());
- return supported.isEmpty() ? noRunner : new AggregateTestRunner(supported);
+ return new AggregateTestRunner(List.copyOf(testRunners));
}
@Override
@@ -51,17 +39,13 @@ public class AggregateTestRunner implements TestRunner {
if (current.get() == -1)
return Status.NOT_STARTED;
- boolean failed = false;
- boolean inconclusive = false;
+ Status status = Status.NO_TESTS;
for (int i = 0; i <= current.get(); i++) {
if (i == wrapped.size())
- return failed ? Status.FAILURE : inconclusive ? Status.INCONCLUSIVE : Status.SUCCESS;
+ return status;
- switch (wrapped.get(i).getStatus()) {
- case ERROR: return Status.ERROR;
- case INCONCLUSIVE: inconclusive = true; break;
- case FAILURE: failed = true;
- }
+ Status next = wrapped.get(i).getStatus();
+ status = status.ordinal() < next.ordinal() ? status : next;
}
return Status.RUNNING;
}
@@ -89,11 +73,6 @@ public class AggregateTestRunner implements TestRunner {
}
@Override
- public boolean isSupported() {
- return wrapped.stream().anyMatch(TestRunner::isSupported);
- }
-
- @Override
public TestReport getReport() {
return wrapped.stream().map(TestRunner::getReport).filter(Objects::nonNull)
.reduce(AggregateTestRunner::merge).orElse(null);
diff --git a/vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/JunitRunner.java b/vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/JunitRunner.java
index 58f201cd599..929d01ee371 100644
--- a/vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/JunitRunner.java
+++ b/vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/JunitRunner.java
@@ -103,12 +103,13 @@ public class JunitRunner extends AbstractComponent implements TestRunner {
}
try {
logRecords.clear();
- testRuntimeProvider.initialize(testConfig);
Optional<Bundle> testBundle = findTestBundle();
if (testBundle.isEmpty()) {
- throw new RuntimeException("No test bundle available");
+ execution = CompletableFuture.completedFuture(TestReport.builder().build());
+ return execution;
}
+ testRuntimeProvider.initialize(testConfig);
Optional<TestDescriptor> testDescriptor = loadTestDescriptor(testBundle.get());
if (testDescriptor.isEmpty()) {
throw new RuntimeException("Could not find test descriptor");
@@ -133,11 +134,6 @@ public class JunitRunner extends AbstractComponent implements TestRunner {
.build();
}
- @Override
- public boolean isSupported() {
- return findTestBundle().isPresent();
- }
-
private Optional<Bundle> findTestBundle() {
return Stream.of(bundleContext.getBundles())
.filter(this::isTestBundle)
@@ -169,7 +165,7 @@ public class JunitRunner extends AbstractComponent implements TestRunner {
StringBuffer buffer = new StringBuffer();
testClasses.forEach(cl -> buffer.append("\t").append(cl.toString()).append(" / ").append(cl.getClassLoader().toString()).append("\n"));
- logger.info("Loaded testClasses: \n" + buffer.toString());
+ logger.info("Loaded testClasses: \n" + buffer);
return testClasses;
}
@@ -183,9 +179,7 @@ public class JunitRunner extends AbstractComponent implements TestRunner {
private TestReport launchJunit(List<Class<?>> testClasses, boolean isProductionTest) {
LauncherDiscoveryRequest discoveryRequest = LauncherDiscoveryRequestBuilder.request()
- .selectors(
- testClasses.stream().map(DiscoverySelectors::selectClass).collect(Collectors.toList())
- )
+ .selectors(testClasses.stream().map(DiscoverySelectors::selectClass).collect(Collectors.toList()))
.build();
var launcherConfig = LauncherConfig.builder()
diff --git a/vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/TestReport.java b/vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/TestReport.java
index 8cced4f8cd6..51e14f6b356 100644
--- a/vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/TestReport.java
+++ b/vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/TestReport.java
@@ -8,6 +8,7 @@ import java.util.logging.LogRecord;
import static com.yahoo.vespa.testrunner.TestRunner.Status.FAILURE;
import static com.yahoo.vespa.testrunner.TestRunner.Status.INCONCLUSIVE;
+import static com.yahoo.vespa.testrunner.TestRunner.Status.NO_TESTS;
import static com.yahoo.vespa.testrunner.TestRunner.Status.SUCCESS;
/**
@@ -40,7 +41,7 @@ public class TestReport {
}
public TestRunner.Status status() {
- return failedCount > 0 ? FAILURE : inconclusiveCount > 0 ? INCONCLUSIVE : SUCCESS;
+ return failedCount > 0 ? FAILURE : inconclusiveCount > 0 ? INCONCLUSIVE : totalCount > 0 ? SUCCESS : NO_TESTS;
}
public static Builder builder(){
diff --git a/vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/TestRunner.java b/vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/TestRunner.java
index 20ff19266cf..c38226f3c27 100644
--- a/vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/TestRunner.java
+++ b/vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/TestRunner.java
@@ -17,13 +17,33 @@ public interface TestRunner {
CompletableFuture<?> test(Suite suite, byte[] config);
- boolean isSupported();
-
default TestReport getReport() { return null; }
+ /** Test run status, ordered from most to least specific; the most specific result is chosen when combining multiple. */
enum Status {
- NOT_STARTED, RUNNING, FAILURE, INCONCLUSIVE, ERROR, SUCCESS
- }
+
+ /** Tests are currently running. */
+ RUNNING,
+
+ /** Framework exception; never got to run the tests, or failed parsing their output. */
+ ERROR,
+
+ /** Test code failed. */
+ FAILURE,
+
+ /** Tests should be re-run at a later time. */
+ INCONCLUSIVE,
+
+ /** All tests passed. */
+ SUCCESS,
+
+ /** No tests found. */
+ NO_TESTS,
+
+ /** Tests have not yet started. */
+ NOT_STARTED
+
+ }
enum Suite {
SYSTEM_TEST, STAGING_SETUP_TEST, STAGING_TEST, PRODUCTION_TEST
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
index 354d2476afb..c01286a8064 100644
--- 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
@@ -27,6 +27,7 @@ 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.INCONCLUSIVE;
+import static com.yahoo.vespa.testrunner.TestRunner.Status.NO_TESTS;
import static com.yahoo.vespa.testrunner.TestRunner.Status.RUNNING;
import static com.yahoo.vespa.testrunner.TestRunner.Status.SUCCESS;
import static com.yahoo.yolean.Exceptions.uncheck;
@@ -74,28 +75,23 @@ public class VespaCliTestRunner implements TestRunner {
return CompletableFuture.runAsync(() -> runTests(suite, config));
}
- @Override
- public boolean isSupported() {
- return Stream.of(Suite.SYSTEM_TEST, Suite.STAGING_SETUP_TEST, Suite.STAGING_TEST)
- .anyMatch(suite -> getChildDirectory(testsPath, toSuiteDirectoryName(suite)).isPresent());
- }
-
void runTests(Suite suite, byte[] config) {
Process process = null;
try {
TestConfig testConfig = TestConfig.fromJson(config);
- process = testRunProcessBuilder(suite, testConfig).start();
+ ProcessBuilder builder = testRunProcessBuilder(suite, testConfig);
+ if (builder == null) {
+ status.set(NO_TESTS);
+ return;
+ }
+ process = builder.start();
HtmlLogger htmlLogger = new HtmlLogger();
BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
in.lines().forEach(line -> log(htmlLogger.toLog(line)));
int exitCode = process.waitFor();
status.set(exitCode == 0 ? SUCCESS : FAILURE);
}
- catch (NoTestsException e) {
- log(Level.WARNING, "Did not find expected basic HTTP tests", e);
- status.set(FAILURE);
- }
catch (Exception e) {
if (process != null)
process.destroyForcibly();
@@ -114,10 +110,11 @@ public class VespaCliTestRunner implements TestRunner {
}
ProcessBuilder testRunProcessBuilder(Suite suite, TestConfig config) throws IOException {
- Path suitePath = getChildDirectory(testsPath, toSuiteDirectoryName(suite))
- .orElseThrow(() -> new NoTestsException("No tests found, for suite '" + suite + "'"));
+ Optional<Path> suitePath = getChildDirectory(testsPath, toSuiteDirectoryName(suite));
+ if (suitePath.isEmpty())
+ return null;
- ProcessBuilder builder = new ProcessBuilder("vespa", "test", suitePath.toAbsolutePath().toString(),
+ ProcessBuilder builder = new ProcessBuilder("vespa", "test", suitePath.get().toAbsolutePath().toString(),
"--application", config.application().toFullString(),
"--zone", config.zone().value(),
"--target", "cloud");
@@ -172,10 +169,4 @@ public class VespaCliTestRunner implements TestRunner {
return new String(SlimeUtils.toJsonBytes(root), UTF_8);
}
- static class NoTestsException extends RuntimeException {
-
- private NoTestsException(String message) { super(message); }
-
- }
-
}
diff --git a/vespa-osgi-testrunner/src/test/java/com/yahoo/vespa/testrunner/AggregateTestRunnerTest.java b/vespa-osgi-testrunner/src/test/java/com/yahoo/vespa/testrunner/AggregateTestRunnerTest.java
index a7ddbbf3fcc..29d0a1a4b1e 100644
--- a/vespa-osgi-testrunner/src/test/java/com/yahoo/vespa/testrunner/AggregateTestRunnerTest.java
+++ b/vespa-osgi-testrunner/src/test/java/com/yahoo/vespa/testrunner/AggregateTestRunnerTest.java
@@ -14,6 +14,7 @@ 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.INCONCLUSIVE;
import static com.yahoo.vespa.testrunner.TestRunner.Status.NOT_STARTED;
+import static com.yahoo.vespa.testrunner.TestRunner.Status.NO_TESTS;
import static com.yahoo.vespa.testrunner.TestRunner.Status.RUNNING;
import static com.yahoo.vespa.testrunner.TestRunner.Status.SUCCESS;
import static java.util.stream.Collectors.toList;
@@ -31,30 +32,34 @@ class AggregateTestRunnerTest {
@Test
void onlySupportedRunnersAreUsed() {
- MockTestRunner unsupported = new MockTestRunner(false);
- MockTestRunner suppported = new MockTestRunner(true);
- TestRunner runner = AggregateTestRunner.of(List.of(unsupported, suppported));
+ MockTestRunner unsupported = new MockTestRunner();
+ unsupported.status = NO_TESTS;
+ MockTestRunner supported = new MockTestRunner();
+ supported.status = SUCCESS;
+ TestRunner runner = AggregateTestRunner.of(List.of(unsupported, supported));
CompletableFuture<?> future = runner.test(null, null);
assertFalse(future.isDone());
- assertNull(unsupported.future);
- assertNotNull(suppported.future);
- suppported.future.complete(null);
+ assertNotNull(unsupported.future);
+ assertNull(supported.future);
+ unsupported.future.complete(null);
+ assertNotNull(supported.future);
+ supported.future.complete(null);
assertTrue(future.isDone());
}
@Test
void noTestsResultInFailure() {
- TestRunner runner = AggregateTestRunner.of(List.of(new MockTestRunner(false)));
- assertEquals("No tests were found", runner.getLog(-1).iterator().next().getMessage());
- assertSame(FAILURE, runner.getStatus());
+ TestRunner runner = AggregateTestRunner.of(List.of());
+ runner.test(null, null);
+ assertSame(NO_TESTS, runner.getStatus());
}
@Test
void chainedRunners() {
LogRecord record1 = new LogRecord(Level.INFO, "one");
LogRecord record2 = new LogRecord(Level.INFO, "two");
- MockTestRunner first = new MockTestRunner(true);
- MockTestRunner second = new MockTestRunner(true);
+ MockTestRunner first = new MockTestRunner();
+ MockTestRunner second = new MockTestRunner();
TestRunner runner = AggregateTestRunner.of(List.of(first, second));
assertSame(NOT_STARTED, runner.getStatus());
assertEquals(List.of(), runner.getLog(-1));
@@ -103,7 +108,7 @@ class AggregateTestRunnerTest {
assertFalse(first.future.isDone());
assertTrue(second.future.isDone());
assertEquals(List.of(record1), runner.getLog(-1));
- assertEquals(ERROR, runner.getStatus());
+ assertEquals(RUNNING, runner.getStatus());
// First wrapped runner completes exceptionally, but the second should be started as usual.
first.future.completeExceptionally(new RuntimeException("error"));
@@ -140,15 +145,10 @@ class AggregateTestRunnerTest {
static class MockTestRunner implements TestRunner {
final List<LogRecord> log = new ArrayList<>();
- final boolean supported;
CompletableFuture<?> future;
Status status = NOT_STARTED;
TestReport report;
- public MockTestRunner(boolean supported) {
- this.supported = supported;
- }
-
@Override
public Collection<LogRecord> getLog(long after) {
return log.stream().filter(record -> record.getSequenceNumber() > after).collect(toList());
@@ -165,11 +165,6 @@ class AggregateTestRunnerTest {
}
@Override
- public boolean isSupported() {
- return supported;
- }
-
- @Override
public TestReport getReport() {
return report;
}
diff --git a/vespa-osgi-testrunner/src/test/java/com/yahoo/vespa/testrunner/TestRunnerHandlerTest.java b/vespa-osgi-testrunner/src/test/java/com/yahoo/vespa/testrunner/TestRunnerHandlerTest.java
index 37ab8550357..9b08e398564 100644
--- a/vespa-osgi-testrunner/src/test/java/com/yahoo/vespa/testrunner/TestRunnerHandlerTest.java
+++ b/vespa-osgi-testrunner/src/test/java/com/yahoo/vespa/testrunner/TestRunnerHandlerTest.java
@@ -83,7 +83,6 @@ class TestRunnerHandlerTest {
@Test
public void returnsEmptyResponsesWhenReportNotReady() throws IOException {
TestRunner testRunner = mock(TestRunner.class);
- when(testRunner.isSupported()).thenReturn(true);
when(testRunner.getReport()).thenReturn(null);
testRunnerHandler = new TestRunnerHandler(
Executors.newSingleThreadExecutor(),
@@ -104,24 +103,6 @@ class TestRunnerHandlerTest {
}
}
- @Test
- public void usesLegacyTestRunnerWhenNotSupported() throws IOException {
- TestRunner testRunner = mock(TestRunner.class);
- when(testRunner.isSupported()).thenReturn(false);
- TestRunner legacyTestRunner = mock(TestRunner.class);
- when(legacyTestRunner.isSupported()).thenReturn(true);
- when(legacyTestRunner.getLog(anyLong())).thenReturn(List.of(logRecord("Legacy log message")));
- TestRunner aggregate = AggregateTestRunner.of(List.of(testRunner, legacyTestRunner));
- testRunnerHandler = new TestRunnerHandler(Executors.newSingleThreadExecutor(), aggregate);
-
- // Prime the aggregate to check for logs in the wrapped runners.
- aggregate.test(TestRunner.Suite.PRODUCTION_TEST, new byte[0]);
- HttpResponse response = testRunnerHandler.handle(HttpRequest.createTestRequest("http://localhost:1234/tester/v1/log", GET));
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- response.render(out);
- JsonTestHelper.assertJsonEquals(out.toString(UTF_8), "{\"logRecords\":[{\"id\":0,\"at\":1598432151660,\"type\":\"info\",\"message\":\"Legacy log message\"}]}");
- }
-
/* Creates a LogRecord that has a known instant and sequence number to get predictable serialization results. */
private static LogRecord logRecord(String logMessage) {
LogRecord logRecord = new LogRecord(Level.INFO, logMessage);
@@ -154,11 +135,6 @@ class TestRunnerHandlerTest {
}
@Override
- public boolean isSupported() {
- return true;
- }
-
- @Override
public TestRunner.Status getStatus() {
return status;
}
diff --git a/vespa-osgi-testrunner/src/test/java/com/yahoo/vespa/testrunner/VespaCliTestRunnerTest.java b/vespa-osgi-testrunner/src/test/java/com/yahoo/vespa/testrunner/VespaCliTestRunnerTest.java
index 46d7b78cdea..5fd73bb4494 100644
--- a/vespa-osgi-testrunner/src/test/java/com/yahoo/vespa/testrunner/VespaCliTestRunnerTest.java
+++ b/vespa-osgi-testrunner/src/test/java/com/yahoo/vespa/testrunner/VespaCliTestRunnerTest.java
@@ -2,7 +2,6 @@
package com.yahoo.vespa.testrunner;
import ai.vespa.hosted.api.TestConfig;
-import com.yahoo.vespa.testrunner.VespaCliTestRunner.NoTestsException;
import org.junit.jupiter.api.Test;
import java.io.IOException;
@@ -12,9 +11,7 @@ import java.nio.file.Path;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertNull;
/**
* @author jonmv
@@ -48,13 +45,9 @@ class VespaCliTestRunnerTest {
Path tests = Files.createDirectory(temp.resolve("tests"));
Path artifacts = Files.createDirectory(temp.resolve("artifacts"));
VespaCliTestRunner runner = new VespaCliTestRunner(artifacts, tests);
- assertFalse(runner.isSupported());
Path systemTests = Files.createDirectory(tests.resolve("system-test"));
- assertTrue(runner.isSupported());
- NoTestsException ise = assertThrows(NoTestsException.class,
- () -> runner.testRunProcessBuilder(TestRunner.Suite.STAGING_TEST, testConfig));
- assertEquals("No tests found, for suite 'STAGING_TEST'", ise.getMessage());
+ assertNull(runner.testRunProcessBuilder(TestRunner.Suite.STAGING_TEST, testConfig));
ProcessBuilder builder = runner.testRunProcessBuilder(TestRunner.Suite.SYSTEM_TEST, testConfig);
assertEquals(List.of("vespa", "test", systemTests.toAbsolutePath().toString(),