From e1955589f6f56337a669fe89ad22a68b349f3880 Mon Sep 17 00:00:00 2001 From: Jon Marius Venstad Date: Wed, 17 Nov 2021 15:09:03 +0100 Subject: Revert "Jonmv/vespa cli test runner" --- .../controller/deployment/InternalStepRunner.java | 31 ++-- .../resources/test_runner_services.xml-cd-legacy | 2 +- .../resources/test_runner_services.xml-cd-osgi | 8 +- .../com/yahoo/vespa/testrunner/JunitRunner.java | 55 +++---- .../com/yahoo/vespa/testrunner/TestReport.java | 66 +++++--- .../com/yahoo/vespa/testrunner/TestRunner.java | 27 +--- .../yahoo/vespa/testrunner/TestRunnerHandler.java | 163 ++++++++++++++------ .../vespa/testrunner/legacy/LegacyTestRunner.java | 22 +++ .../yahoo/vespa/testrunner/legacy/TestProfile.java | 30 ++++ .../vespa/testrunner/TestRunnerHandlerTest.java | 57 +++---- .../vespa/hosted/testrunner/PomXmlGenerator.java | 1 + .../yahoo/vespa/hosted/testrunner/TestProfile.java | 30 ---- .../yahoo/vespa/hosted/testrunner/TestRunner.java | 18 +-- .../vespa/hosted/testrunner/TestRunnerHandler.java | 171 +++++++++++++++++++++ .../hosted/testrunner/PomXmlGeneratorTest.java | 1 + .../hosted/testrunner/TestRunnerHandlerTest.java | 63 ++++++++ .../vespa/hosted/testrunner/TestRunnerTest.java | 17 +- 17 files changed, 532 insertions(+), 230 deletions(-) create mode 100644 vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/legacy/LegacyTestRunner.java create mode 100644 vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/legacy/TestProfile.java delete mode 100644 vespa-testrunner-components/src/main/java/com/yahoo/vespa/hosted/testrunner/TestProfile.java create mode 100644 vespa-testrunner-components/src/main/java/com/yahoo/vespa/hosted/testrunner/TestRunnerHandler.java create mode 100644 vespa-testrunner-components/src/test/java/com/yahoo/vespa/hosted/testrunner/TestRunnerHandlerTest.java diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java index ddf89e2f376..577dab69279 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java @@ -919,16 +919,24 @@ public class InternalStepRunner implements StepRunner { String runtimeProviderClass = config.runtimeProviderClass(); String tenantCdBundle = config.tenantCdBundle(); - String extraJUnitComponents = - "\n" + + String handlerAndExtraComponents = useOsgiBasedTestRuntime + ? " \n" + - "\n" + - " \n" + - " \n" + - " artifacts\n" + - " " + systemUsesAthenz + "\n" + - " \n" + - " \n"; + "\n" + + " \n" + + " \n" + + " artifacts\n" + + " " + systemUsesAthenz + "\n" + + " \n" + + " \n" + + "\n" + + " \n" + + " http://*/tester/v1/*\n" + + " \n" + : + " \n" + + " http://*/tester/v1/*\n" + + " \n"; String servicesXml = "\n" + @@ -944,10 +952,7 @@ public class InternalStepRunner implements StepRunner { " \n" + " \n" + "\n" + - " \n" + - " http://*/tester/v1/*\n" + - " \n" + - (useOsgiBasedTestRuntime ? extraJUnitComponents : "") + + handlerAndExtraComponents + "\n" + " \n" + " " + resourceString + "\n" + diff --git a/controller-server/src/test/resources/test_runner_services.xml-cd-legacy b/controller-server/src/test/resources/test_runner_services.xml-cd-legacy index c6046479934..125c5004d25 100644 --- a/controller-server/src/test/resources/test_runner_services.xml-cd-legacy +++ b/controller-server/src/test/resources/test_runner_services.xml-cd-legacy @@ -11,7 +11,7 @@ - + http://*/tester/v1/* diff --git a/controller-server/src/test/resources/test_runner_services.xml-cd-osgi b/controller-server/src/test/resources/test_runner_services.xml-cd-osgi index 01a7afb3bed..d1a83abff8d 100644 --- a/controller-server/src/test/resources/test_runner_services.xml-cd-osgi +++ b/controller-server/src/test/resources/test_runner_services.xml-cd-osgi @@ -11,10 +11,6 @@ - - http://*/tester/v1/* - - @@ -24,6 +20,10 @@ + + http://*/tester/v1/* + + 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 d1acc4faf0f..87b98c8efc1 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 @@ -11,6 +11,7 @@ import com.yahoo.component.AbstractComponent; import com.yahoo.io.IOUtils; import com.yahoo.jdisc.application.OsgiFramework; import com.yahoo.vespa.defaults.Defaults; +import com.yahoo.vespa.testrunner.legacy.LegacyTestRunner; import org.junit.jupiter.engine.JupiterTestEngine; import org.junit.platform.engine.discovery.DiscoverySelectors; import org.junit.platform.launcher.Launcher; @@ -25,12 +26,10 @@ import org.osgi.framework.BundleContext; import java.io.IOException; import java.net.URL; import java.nio.charset.Charset; -import java.util.Collection; +import java.util.ArrayList; import java.util.List; import java.util.Optional; -import java.util.SortedMap; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.logging.Level; @@ -45,7 +44,6 @@ import java.util.stream.Stream; public class JunitRunner extends AbstractComponent implements TestRunner { private static final Logger logger = Logger.getLogger(JunitRunner.class.getName()); - private final SortedMap logRecords = new ConcurrentSkipListMap<>(); private final BundleContext bundleContext; private final TestRuntimeProvider testRuntimeProvider; private volatile Future execution; @@ -86,23 +84,12 @@ public class JunitRunner extends AbstractComponent implements TestRunner { credentialsRoot.ifPresent(root -> System.setProperty("vespa.test.credentials.root", root)); } - private static TestDescriptor.TestCategory toCategory(TestRunner.Suite testProfile) { - switch(testProfile) { - case SYSTEM_TEST: return TestDescriptor.TestCategory.systemtest; - case STAGING_SETUP_TEST: return TestDescriptor.TestCategory.stagingsetuptest; - case STAGING_TEST: return TestDescriptor.TestCategory.stagingtest; - case PRODUCTION_TEST: return TestDescriptor.TestCategory.productiontest; - default: throw new RuntimeException("Unknown test profile: " + testProfile.name()); - } - } - @Override - public void test(TestRunner.Suite suite, byte[] testConfig) { - if (execution != null && ! execution.isDone()) { + public void executeTests(TestDescriptor.TestCategory category, byte[] testConfig) { + if (execution != null && !execution.isDone()) { throw new IllegalStateException("Test execution already in progress"); } try { - logRecords.clear(); testRuntimeProvider.initialize(testConfig); Optional testBundle = findTestBundle(); if (testBundle.isEmpty()) { @@ -113,17 +100,12 @@ public class JunitRunner extends AbstractComponent implements TestRunner { if (testDescriptor.isEmpty()) { throw new RuntimeException("Could not find test descriptor"); } - execution = CompletableFuture.supplyAsync(() -> launchJunit(loadClasses(testBundle.get(), testDescriptor.get(), toCategory(suite)))); + execution = CompletableFuture.supplyAsync(() -> launchJunit(loadClasses(testBundle.get(), testDescriptor.get(), category))); } catch (Exception e) { execution = CompletableFuture.completedFuture(createReportWithFailedInitialization(e)); } } - @Override - public Collection getLog(long after) { - return logRecords.tailMap(after + 1).values(); - } - private static TestReport createReportWithFailedInitialization(Exception exception) { TestReport.Failure failure = new TestReport.Failure("init", exception); return new TestReport.Builder() @@ -193,7 +175,8 @@ public class JunitRunner extends AbstractComponent implements TestRunner { Launcher launcher = LauncherFactory.create(launcherConfig); // Create log listener: - var logListener = VespaJunitLogListener.forBiConsumer((t, m) -> log(logRecords, m.get(), t)); + var logLines = new ArrayList(); + var logListener = VespaJunitLogListener.forBiConsumer((t, m) -> log(logLines, m.get(), t)); // Create a summary listener: var summaryListener = new SummaryGeneratingListener(); launcher.registerTestExecutionListeners(logListener, summaryListener); @@ -210,14 +193,14 @@ public class JunitRunner extends AbstractComponent implements TestRunner { .withIgnoredCount(report.getTestsSkippedCount()) .withFailedCount(report.getTestsFailedCount()) .withFailures(failures) - .withLogs(logRecords.values()) + .withLogs(logLines) .build(); } - private void log(SortedMap logs, String message, Throwable t) { + private void log(List logs, String message, Throwable t) { LogRecord logRecord = new LogRecord(Level.INFO, message); Optional.ofNullable(t).ifPresent(logRecord::setThrown); - logs.put(logRecord.getSequenceNumber(), logRecord); + logs.add(logRecord); } @Override @@ -226,20 +209,20 @@ public class JunitRunner extends AbstractComponent implements TestRunner { } @Override - public TestRunner.Status getStatus() { - if (execution == null) return TestRunner.Status.NOT_STARTED; - if (!execution.isDone()) return TestRunner.Status.RUNNING; + public LegacyTestRunner.Status getStatus() { + if (execution == null) return LegacyTestRunner.Status.NOT_STARTED; + if (!execution.isDone()) return LegacyTestRunner.Status.RUNNING; try { TestReport report = execution.get(); if (report.isSuccess()) { - return TestRunner.Status.SUCCESS; + return LegacyTestRunner.Status.SUCCESS; } else { - return TestRunner.Status.FAILURE; + return LegacyTestRunner.Status.FAILURE; } } catch (InterruptedException|ExecutionException e) { logger.log(Level.WARNING, "Error while getting test report", e); // Return FAILURE to enforce getting the test report from the caller. - return TestRunner.Status.FAILURE; + return LegacyTestRunner.Status.FAILURE; } } @@ -259,4 +242,10 @@ public class JunitRunner extends AbstractComponent implements TestRunner { } } + @Override + public String getReportAsJson() { + return Optional.ofNullable(getReport()) + .map(TestReport::toJson) + .orElse(""); + } } 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 b3ca47e2480..9a1200d0bf3 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 @@ -1,7 +1,13 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.testrunner; -import java.util.Collection; +import com.yahoo.exception.ExceptionUtils; +import com.yahoo.slime.Cursor; +import com.yahoo.slime.Slime; +import com.yahoo.slime.SlimeUtils; +import com.yahoo.yolean.Exceptions; + +import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.List; import java.util.logging.LogRecord; @@ -10,16 +16,15 @@ import java.util.logging.LogRecord; * @author mortent */ public class TestReport { - - final long totalCount; - final long successCount; - final long failedCount; - final long ignoredCount; - final long abortedCount; - final List failures; - final List logLines; - - private TestReport(long totalCount, long successCount, long failedCount, long ignoredCount, long abortedCount, List failures, List logLines) { + private final long totalCount; + private final long successCount; + private final long failedCount; + private final long ignoredCount; + private final long abortedCount; + private final List failures; + private final List logLines; + + public TestReport(long totalCount, long successCount, long failedCount, long ignoredCount, long abortedCount, List failures, List logLines) { this.totalCount = totalCount; this.successCount = successCount; this.failedCount = failedCount; @@ -29,6 +34,31 @@ public class TestReport { this.logLines = logLines; } + private void serializeFailure(Failure failure, Cursor slime) { + var testIdentifier = failure.testId(); + slime.setString("testName", failure.testId()); + slime.setString("testError",failure.exception().getMessage()); + slime.setString("exception", ExceptionUtils.getStackTraceAsString(failure.exception())); + } + + public String toJson() { + var slime = new Slime(); + var root = slime.setObject(); + var summary = root.setObject("summary"); + summary.setLong("total", totalCount); + summary.setLong("success", successCount); + summary.setLong("failed", failedCount); + summary.setLong("ignored", ignoredCount); + summary.setLong("aborted", abortedCount); + var failureRoot = summary.setArray("failures"); + this.failures.forEach(failure -> serializeFailure(failure, failureRoot.addObject())); + + var output = root.setArray("output"); + logLines.forEach(lr -> output.addString(lr.getMessage())); + + return Exceptions.uncheck(() -> new String(SlimeUtils.toJsonBytes(slime), StandardCharsets.UTF_8)); + } + public List logLines() { return logLines; } @@ -41,9 +71,7 @@ public class TestReport { return new Builder(); } - public static class Builder { - private long totalCount; private long successCount; private long failedCount; @@ -60,22 +88,18 @@ public class TestReport { this.totalCount = totalCount; return this; } - public Builder withSuccessCount(long successCount) { this.successCount = successCount; return this; } - public Builder withFailedCount(long failedCount) { this.failedCount = failedCount; return this; } - public Builder withIgnoredCount(long ignoredCount) { this.ignoredCount = ignoredCount; return this; } - public Builder withAbortedCount(long abortedCount) { this.abortedCount = abortedCount; return this; @@ -86,14 +110,12 @@ public class TestReport { return this; } - public Builder withLogs(Collection logRecords) { - this.logLines = List.copyOf(logRecords); + public Builder withLogs(List logRecords) { + this.logLines = logRecords; return this; } - } - public static class Failure { private final String testId; private final Throwable exception; @@ -110,7 +132,5 @@ public class TestReport { public Throwable exception() { return exception; } - } - } 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 db489f5aa3d..31474d6c348 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 @@ -1,31 +1,20 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.testrunner; -import java.util.Collection; -import java.util.logging.LogRecord; +import ai.vespa.hosted.api.TestDescriptor; +import com.yahoo.vespa.testrunner.legacy.LegacyTestRunner; /** - * @author jonmv * @author mortent */ public interface TestRunner { + void executeTests(TestDescriptor.TestCategory category, byte[] testConfig); - Collection getLog(long after); + boolean isSupported(); - Status getStatus(); + LegacyTestRunner.Status getStatus(); - void test(Suite suite, byte[] config); + TestReport getReport(); - default boolean isSupported() { return true; } - - default TestReport getReport() { return null; } - - enum Status { - NOT_STARTED, RUNNING, FAILURE, ERROR, SUCCESS - } - - enum Suite { - SYSTEM_TEST, STAGING_SETUP_TEST, STAGING_TEST, PRODUCTION_TEST - } - -} \ No newline at end of file + String getReportAsJson(); +} diff --git a/vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/TestRunnerHandler.java b/vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/TestRunnerHandler.java index da2db3798c2..4c359071fc9 100644 --- a/vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/TestRunnerHandler.java +++ b/vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/TestRunnerHandler.java @@ -1,26 +1,34 @@ // 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.TestDescriptor; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.inject.Inject; import com.yahoo.container.jdisc.EmptyResponse; import com.yahoo.container.jdisc.HttpRequest; import com.yahoo.container.jdisc.HttpResponse; import com.yahoo.container.jdisc.LoggingRequestHandler; -import com.yahoo.exception.ExceptionUtils; -import com.yahoo.restapi.MessageResponse; -import com.yahoo.restapi.SlimeJsonResponse; import com.yahoo.slime.Cursor; +import com.yahoo.slime.JsonFormat; import com.yahoo.slime.Slime; +import com.yahoo.slime.SlimeUtils; +import com.yahoo.vespa.testrunner.legacy.LegacyTestRunner; +import com.yahoo.vespa.testrunner.legacy.TestProfile; import com.yahoo.yolean.Exceptions; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.OutputStream; import java.io.PrintStream; import java.util.Collection; +import java.util.Collections; +import java.util.List; import java.util.Optional; import java.util.concurrent.Executor; import java.util.logging.Level; import java.util.logging.LogRecord; +import java.util.stream.Collectors; import static com.yahoo.jdisc.Response.Status; @@ -31,12 +39,18 @@ import static com.yahoo.jdisc.Response.Status; */ public class TestRunnerHandler extends LoggingRequestHandler { - private final TestRunner testRunner; + private static final String CONTENT_TYPE_APPLICATION_JSON = "application/json"; + + private final TestRunner junitRunner; + private final LegacyTestRunner testRunner; + private final boolean useOsgiMode; @Inject - public TestRunnerHandler(Executor executor, TestRunner junitRunner, TestRunner testRunner) { + public TestRunnerHandler(Executor executor, TestRunner junitRunner, LegacyTestRunner testRunner) { super(executor); - this.testRunner = junitRunner.isSupported() ? junitRunner : testRunner; + this.junitRunner = junitRunner; + this.testRunner = testRunner; + this.useOsgiMode = junitRunner.isSupported(); } @Override @@ -46,48 +60,81 @@ public class TestRunnerHandler extends LoggingRequestHandler { case GET: return handleGET(request); case POST: return handlePOST(request); - default: return new MessageResponse(Status.METHOD_NOT_ALLOWED, "Method '" + request.getMethod() + "' is not supported"); + default: return new Response(Status.METHOD_NOT_ALLOWED, "Method '" + request.getMethod() + "' is not supported"); } } catch (IllegalArgumentException e) { - return new MessageResponse(Status.BAD_REQUEST, Exceptions.toMessageString(e)); + return new Response(Status.BAD_REQUEST, Exceptions.toMessageString(e)); } catch (Exception e) { log.log(Level.WARNING, "Unexpected error handling '" + request.getUri() + "'", e); - return new MessageResponse(Status.INTERNAL_SERVER_ERROR, Exceptions.toMessageString(e)); + return new Response(Status.INTERNAL_SERVER_ERROR, Exceptions.toMessageString(e)); } } private HttpResponse handleGET(HttpRequest request) { String path = request.getUri().getPath(); - switch (path) { - case "/tester/v1/log": + if (path.equals("/tester/v1/log")) { + if (useOsgiMode) { long fetchRecordsAfter = Optional.ofNullable(request.getProperty("after")) - .map(Long::parseLong) - .orElse(-1L); - return new SlimeJsonResponse(logToSlime(testRunner.getLog(fetchRecordsAfter))); - case "/tester/v1/status": + .map(Long::parseLong) + .orElse(-1L); + + List logRecords = Optional.ofNullable(junitRunner.getReport()) + .map(TestReport::logLines) + .orElse(Collections.emptyList()).stream() + .filter(record -> record.getSequenceNumber()>fetchRecordsAfter) + .collect(Collectors.toList()); + return new SlimeJsonResponse(logToSlime(logRecords)); + } else { + return new SlimeJsonResponse(logToSlime(testRunner.getLog(request.hasProperty("after") + ? Long.parseLong(request.getProperty("after")) + : -1))); + } + } else if (path.equals("/tester/v1/status")) { + if (useOsgiMode) { + log.info("Responding with status " + junitRunner.getStatus()); + return new Response(junitRunner.getStatus().name()); + } else { log.info("Responding with status " + testRunner.getStatus()); - return new MessageResponse(testRunner.getStatus().name()); - case "/tester/v1/report": - TestReport report = testRunner.getReport(); - if (report == null) - return new EmptyResponse(200); - - return new SlimeJsonResponse(toSlime(report)); + return new Response(testRunner.getStatus().name()); + } + } else if (path.equals("/tester/v1/report")) { + if (useOsgiMode) { + String report = junitRunner.getReportAsJson(); + return new SlimeJsonResponse(SlimeUtils.jsonToSlime(report)); + } else { + return new EmptyResponse(200); + } } - return new MessageResponse(Status.NOT_FOUND, "Not found: " + request.getUri().getPath()); + return new Response(Status.NOT_FOUND, "Not found: " + request.getUri().getPath()); } private HttpResponse handlePOST(HttpRequest request) throws IOException { final String path = request.getUri().getPath(); if (path.startsWith("/tester/v1/run/")) { String type = lastElement(path); - TestRunner.Suite testSuite = TestRunner.Suite.valueOf(type.toUpperCase() + "_TEST"); + TestProfile testProfile = TestProfile.valueOf(type.toUpperCase() + "_TEST"); byte[] config = request.getData().readAllBytes(); - testRunner.test(testSuite, config); - log.info("Started tests of type " + type + " and status is " + testRunner.getStatus()); - return new MessageResponse("Successfully started " + type + " tests"); + if (useOsgiMode) { + junitRunner.executeTests(categoryFromProfile(testProfile), config); + log.info("Started tests of type " + type + " and status is " + junitRunner.getStatus()); + return new Response("Successfully started " + type + " tests"); + } else { + testRunner.test(testProfile, config); + log.info("Started tests of type " + type + " and status is " + testRunner.getStatus()); + return new Response("Successfully started " + type + " tests"); + } + } + return new Response(Status.NOT_FOUND, "Not found: " + request.getUri().getPath()); + } + + TestDescriptor.TestCategory categoryFromProfile(TestProfile testProfile) { + switch(testProfile) { + case SYSTEM_TEST: return TestDescriptor.TestCategory.systemtest; + case STAGING_SETUP_TEST: return TestDescriptor.TestCategory.stagingsetuptest; + case STAGING_TEST: return TestDescriptor.TestCategory.stagingtest; + case PRODUCTION_TEST: return TestDescriptor.TestCategory.productiontest; + default: throw new RuntimeException("Unknown test profile: " + testProfile.name()); } - return new MessageResponse(Status.NOT_FOUND, "Not found: " + request.getUri().getPath()); } private static String lastElement(String path) { @@ -130,32 +177,48 @@ public class TestRunnerHandler extends LoggingRequestHandler { : "error"; } - private static Slime toSlime(TestReport testReport) { - var slime = new Slime(); - var root = slime.setObject(); - if (testReport == null) - return slime; + private static class SlimeJsonResponse extends HttpResponse { + private final Slime slime; - var summary = root.setObject("summary"); - summary.setLong("total", testReport.totalCount); - summary.setLong("success", testReport.successCount); - summary.setLong("failed", testReport.failedCount); - summary.setLong("ignored", testReport.ignoredCount); - summary.setLong("aborted", testReport.abortedCount); - var failureRoot = summary.setArray("failures"); - testReport.failures.forEach(failure -> serializeFailure(failure, failureRoot.addObject())); + private SlimeJsonResponse(Slime slime) { + super(200); + this.slime = slime; + } - var output = root.setArray("output"); - testReport.logLines.forEach(lr -> output.addString(lr.getMessage())); + @Override + public void render(OutputStream outputStream) throws IOException { + new JsonFormat(true).encode(outputStream, slime); + } - return slime; + @Override + public String getContentType() { + return CONTENT_TYPE_APPLICATION_JSON; + } } - private static void serializeFailure(TestReport.Failure failure, Cursor slime) { - var testIdentifier = failure.testId(); - slime.setString("testName", failure.testId()); - slime.setString("testError",failure.exception().getMessage()); - slime.setString("exception", ExceptionUtils.getStackTraceAsString(failure.exception())); - } + private static class Response extends HttpResponse { + private static final ObjectMapper objectMapper = new ObjectMapper(); + private final String message; + private Response(String response) { + this(200, response); + } + + private Response(int statusCode, String message) { + super(statusCode); + this.message = message; + } + + @Override + public void render(OutputStream outputStream) throws IOException { + ObjectNode objectNode = objectMapper.createObjectNode(); + objectNode.put("message", message); + objectMapper.writeValue(outputStream, objectNode); + } + + @Override + public String getContentType() { + return CONTENT_TYPE_APPLICATION_JSON; + } + } } diff --git a/vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/legacy/LegacyTestRunner.java b/vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/legacy/LegacyTestRunner.java new file mode 100644 index 00000000000..418ab7fe5d0 --- /dev/null +++ b/vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/legacy/LegacyTestRunner.java @@ -0,0 +1,22 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.testrunner.legacy; + +import java.util.Collection; +import java.util.logging.LogRecord; + +/** + * @author mortent + */ +public interface LegacyTestRunner { + + Collection getLog(long after); + + Status getStatus(); + + void test(TestProfile testProfile, byte[] config); + + // TODO (mortent) : This seems to be duplicated in TesterCloud.Status and expects to have the same values + enum Status { + NOT_STARTED, RUNNING, FAILURE, ERROR, SUCCESS + } +} diff --git a/vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/legacy/TestProfile.java b/vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/legacy/TestProfile.java new file mode 100644 index 00000000000..ad65d150874 --- /dev/null +++ b/vespa-osgi-testrunner/src/main/java/com/yahoo/vespa/testrunner/legacy/TestProfile.java @@ -0,0 +1,30 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.testrunner.legacy; + +/** + * @author valerijf + * @author jvenstad + */ +public enum TestProfile { + + SYSTEM_TEST("system, com.yahoo.vespa.tenant.systemtest.base.SystemTest", true), + STAGING_SETUP_TEST("staging-setup", false), + STAGING_TEST("staging, com.yahoo.vespa.tenant.systemtest.base.StagingTest", true), + PRODUCTION_TEST("production, com.yahoo.vespa.tenant.systemtest.base.ProductionTest", false); + + private final String group; + private final boolean failIfNoTests; + + TestProfile(String group, boolean failIfNoTests) { + this.group = group; + this.failIfNoTests = failIfNoTests; + } + + public String group() { + return group; + } + + public boolean failIfNoTests() { + return failIfNoTests; + } +} 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 a78dc077446..0caac95ed34 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 @@ -1,25 +1,24 @@ // 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.TestDescriptor; import com.yahoo.container.jdisc.HttpRequest; import com.yahoo.container.jdisc.HttpResponse; import com.yahoo.test.json.JsonTestHelper; +import com.yahoo.vespa.testrunner.legacy.LegacyTestRunner; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.time.Instant; -import java.util.Collection; import java.util.List; import java.util.concurrent.Executors; import java.util.logging.Level; import java.util.logging.LogRecord; -import java.util.stream.Collectors; import static com.yahoo.jdisc.http.HttpRequest.Method.GET; -import static java.nio.charset.StandardCharsets.UTF_8; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -48,7 +47,7 @@ public class TestRunnerHandlerTest { testRunnerHandler = new TestRunnerHandler( Executors.newSingleThreadExecutor(), - new MockJunitRunner(TestRunner.Status.SUCCESS, testReport), + new MockJunitRunner(LegacyTestRunner.Status.SUCCESS, testReport), null); } @@ -57,7 +56,7 @@ public class TestRunnerHandlerTest { HttpResponse response = testRunnerHandler.handle(HttpRequest.createTestRequest("http://localhost:1234/tester/v1/report", GET)); ByteArrayOutputStream out = new ByteArrayOutputStream(); response.render(out); - JsonTestHelper.assertJsonEquals(out.toString(UTF_8), "{\"summary\":{\"total\":10,\"success\":1,\"failed\":2,\"ignored\":3,\"aborted\":4,\"failures\":[{\"testName\":\"Foo.bar()\",\"testError\":\"org.junit.ComparisonFailure: expected: but was:\",\"exception\":\"java.lang.RuntimeException: org.junit.ComparisonFailure: expected: but was:\\n\\tat Foo.bar(Foo.java:1123)\\n\"}]},\"output\":[\"Tests started\"]}"); + JsonTestHelper.assertJsonEquals(new String(out.toByteArray()), "{\"summary\":{\"total\":10,\"success\":1,\"failed\":2,\"ignored\":3,\"aborted\":4,\"failures\":[{\"testName\":\"Foo.bar()\",\"testError\":\"org.junit.ComparisonFailure: expected: but was:\",\"exception\":\"java.lang.RuntimeException: org.junit.ComparisonFailure: expected: but was:\\n\\tat Foo.bar(Foo.java:1123)\\n\"}]},\"output\":[\"Tests started\"]}"); } @@ -66,18 +65,18 @@ public class TestRunnerHandlerTest { 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\":\"Tests started\"}]}"); + JsonTestHelper.assertJsonEquals(new String(out.toByteArray()), "{\"logRecords\":[{\"id\":0,\"at\":1598432151660,\"type\":\"info\",\"message\":\"Tests started\"}]}"); // Should not get old log response = testRunnerHandler.handle(HttpRequest.createTestRequest("http://localhost:1234/tester/v1/log?after=0", GET)); out = new ByteArrayOutputStream(); response.render(out); - assertEquals("{\"logRecords\":[]}", out.toString(UTF_8)); + assertEquals("{\"logRecords\":[]}", new String(out.toByteArray())); } @Test - public void returnsEmptyResponsesWhenReportNotReady() throws IOException { + public void returnsEmptyLogWhenReportNotReady() throws IOException { TestRunner testRunner = mock(TestRunner.class); when(testRunner.isSupported()).thenReturn(true); when(testRunner.getReport()).thenReturn(null); @@ -85,26 +84,17 @@ public class TestRunnerHandlerTest { Executors.newSingleThreadExecutor(), testRunner, null); - { - HttpResponse response = testRunnerHandler.handle(HttpRequest.createTestRequest("http://localhost:1234/tester/v1/log", GET)); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - response.render(out); - assertEquals("{\"logRecords\":[]}", out.toString(UTF_8)); - } - - { - HttpResponse response = testRunnerHandler.handle(HttpRequest.createTestRequest("http://localhost:1234/tester/v1/report", GET)); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - response.render(out); - assertEquals("", out.toString(UTF_8)); - } + HttpResponse response = testRunnerHandler.handle(HttpRequest.createTestRequest("http://localhost:1234/tester/v1/log", GET)); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + response.render(out); + assertEquals("{\"logRecords\":[]}", new String(out.toByteArray())); } @Test public void usesLegacyTestRunnerWhenNotSupported() throws IOException { TestRunner testRunner = mock(TestRunner.class); when(testRunner.isSupported()).thenReturn(false); - TestRunner legacyTestRunner = mock(TestRunner.class); + LegacyTestRunner legacyTestRunner = mock(LegacyTestRunner.class); when(legacyTestRunner.getLog(anyLong())).thenReturn(List.of(logRecord("Legacy log message"))); testRunnerHandler = new TestRunnerHandler( @@ -114,7 +104,7 @@ public class TestRunnerHandlerTest { 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\"}]}"); + JsonTestHelper.assertJsonEquals(new String(out.toByteArray()), "{\"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 format */ @@ -127,23 +117,17 @@ public class TestRunnerHandlerTest { private static class MockJunitRunner implements TestRunner { - private final TestRunner.Status status; + private final LegacyTestRunner.Status status; private final TestReport testReport; - public MockJunitRunner(TestRunner.Status status, TestReport testReport) { + public MockJunitRunner(LegacyTestRunner.Status status, TestReport testReport) { this.status = status; this.testReport = testReport; } @Override - public void test(Suite suite, byte[] testConfig) { } - - @Override - public Collection getLog(long after) { - return getReport().logLines().stream() - .filter(entry -> entry.getSequenceNumber() > after) - .collect(Collectors.toList()); + public void executeTests(TestDescriptor.TestCategory category, byte[] testConfig) { } @Override @@ -152,7 +136,7 @@ public class TestRunnerHandlerTest { } @Override - public TestRunner.Status getStatus() { + public LegacyTestRunner.Status getStatus() { return status; } @@ -161,6 +145,9 @@ public class TestRunnerHandlerTest { return testReport; } + @Override + public String getReportAsJson() { + return getReport().toJson(); + } } - } diff --git a/vespa-testrunner-components/src/main/java/com/yahoo/vespa/hosted/testrunner/PomXmlGenerator.java b/vespa-testrunner-components/src/main/java/com/yahoo/vespa/hosted/testrunner/PomXmlGenerator.java index ff66b31dfa8..b3c49fc6f2d 100644 --- a/vespa-testrunner-components/src/main/java/com/yahoo/vespa/hosted/testrunner/PomXmlGenerator.java +++ b/vespa-testrunner-components/src/main/java/com/yahoo/vespa/hosted/testrunner/PomXmlGenerator.java @@ -2,6 +2,7 @@ package com.yahoo.vespa.hosted.testrunner; import com.yahoo.vespa.defaults.Defaults; +import com.yahoo.vespa.testrunner.legacy.TestProfile; import java.nio.file.Path; import java.util.List; diff --git a/vespa-testrunner-components/src/main/java/com/yahoo/vespa/hosted/testrunner/TestProfile.java b/vespa-testrunner-components/src/main/java/com/yahoo/vespa/hosted/testrunner/TestProfile.java deleted file mode 100644 index 95a2b2723b8..00000000000 --- a/vespa-testrunner-components/src/main/java/com/yahoo/vespa/hosted/testrunner/TestProfile.java +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.hosted.testrunner; - -/** - * @author valerijf - * @author jvenstad - */ -public enum TestProfile { - - SYSTEM_TEST("system, com.yahoo.vespa.tenant.systemtest.base.SystemTest", true), - STAGING_SETUP_TEST("staging-setup", false), - STAGING_TEST("staging, com.yahoo.vespa.tenant.systemtest.base.StagingTest", true), - PRODUCTION_TEST("production, com.yahoo.vespa.tenant.systemtest.base.ProductionTest", false); - - private final String group; - private final boolean failIfNoTests; - - TestProfile(String group, boolean failIfNoTests) { - this.group = group; - this.failIfNoTests = failIfNoTests; - } - - public String group() { - return group; - } - - public boolean failIfNoTests() { - return failIfNoTests; - } -} diff --git a/vespa-testrunner-components/src/main/java/com/yahoo/vespa/hosted/testrunner/TestRunner.java b/vespa-testrunner-components/src/main/java/com/yahoo/vespa/hosted/testrunner/TestRunner.java index 8aa2cd7bc72..0f6e26d256f 100644 --- a/vespa-testrunner-components/src/main/java/com/yahoo/vespa/hosted/testrunner/TestRunner.java +++ b/vespa-testrunner-components/src/main/java/com/yahoo/vespa/hosted/testrunner/TestRunner.java @@ -3,6 +3,8 @@ package com.yahoo.vespa.hosted.testrunner; import com.google.inject.Inject; import com.yahoo.vespa.defaults.Defaults; +import com.yahoo.vespa.testrunner.legacy.LegacyTestRunner; +import com.yahoo.vespa.testrunner.legacy.TestProfile; import org.fusesource.jansi.AnsiOutputStream; import org.fusesource.jansi.HtmlAnsiOutputStream; @@ -36,7 +38,7 @@ import static java.util.logging.Level.SEVERE; * @author valerijf * @author jvenstad */ -public class TestRunner implements com.yahoo.vespa.testrunner.TestRunner { +public class TestRunner implements LegacyTestRunner { private static final Logger logger = Logger.getLogger(TestRunner.class.getName()); private static final Level HTML = new Level("html", 1) { }; @@ -112,14 +114,14 @@ public class TestRunner implements com.yahoo.vespa.testrunner.TestRunner { return builder; } - public synchronized void test(Suite suite, byte[] testConfig) { + public synchronized void test(TestProfile testProfile, byte[] testConfig) { if (status == Status.RUNNING) throw new IllegalArgumentException("Tests are already running; should not receive this request now."); log.clear(); status = Status.RUNNING; - new Thread(() -> runTests(toProfile(suite), testConfig)).start(); + new Thread(() -> runTests(testProfile, testConfig)).start(); } public Collection getLog(long after) { @@ -208,14 +210,4 @@ public class TestRunner implements com.yahoo.vespa.testrunner.TestRunner { private NoTestsException(String message) { super(message); } } - static TestProfile toProfile(Suite suite) { - switch (suite) { - case SYSTEM_TEST: return TestProfile.SYSTEM_TEST; - case STAGING_SETUP_TEST: return TestProfile.STAGING_SETUP_TEST; - case STAGING_TEST: return TestProfile.STAGING_TEST; - case PRODUCTION_TEST: return TestProfile.PRODUCTION_TEST; - default: throw new IllegalArgumentException("Unknown test suite '" + suite + "'"); - } - } - } diff --git a/vespa-testrunner-components/src/main/java/com/yahoo/vespa/hosted/testrunner/TestRunnerHandler.java b/vespa-testrunner-components/src/main/java/com/yahoo/vespa/hosted/testrunner/TestRunnerHandler.java new file mode 100644 index 00000000000..621f0964a40 --- /dev/null +++ b/vespa-testrunner-components/src/main/java/com/yahoo/vespa/hosted/testrunner/TestRunnerHandler.java @@ -0,0 +1,171 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.testrunner; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.inject.Inject; +import com.yahoo.container.jdisc.HttpRequest; +import com.yahoo.container.jdisc.HttpResponse; +import com.yahoo.container.jdisc.LoggingRequestHandler; +import com.yahoo.io.IOUtils; +import com.yahoo.slime.Cursor; +import com.yahoo.slime.JsonFormat; +import com.yahoo.slime.Slime; +import com.yahoo.vespa.testrunner.legacy.TestProfile; +import com.yahoo.yolean.Exceptions; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.util.Collection; +import java.util.concurrent.Executor; +import java.util.logging.Level; +import java.util.logging.LogRecord; + +import static com.yahoo.jdisc.Response.Status; + +/** + * @author valerijf + * @author jvenstad + */ +public class TestRunnerHandler extends LoggingRequestHandler { + + private static final String CONTENT_TYPE_APPLICATION_JSON = "application/json"; + + private final TestRunner testRunner; + + @Inject + public TestRunnerHandler(Executor executor, TestRunner testRunner) { + super(executor); + this.testRunner = testRunner; + } + + @Override + public HttpResponse handle(HttpRequest request) { + try { + switch (request.getMethod()) { + case GET: return handleGET(request); + case POST: return handlePOST(request); + + default: return new Response(Status.METHOD_NOT_ALLOWED, "Method '" + request.getMethod() + "' is not supported"); + } + } catch (IllegalArgumentException e) { + return new Response(Status.BAD_REQUEST, Exceptions.toMessageString(e)); + } catch (Exception e) { + log.log(Level.WARNING, "Unexpected error handling '" + request.getUri() + "'", e); + return new Response(Status.INTERNAL_SERVER_ERROR, Exceptions.toMessageString(e)); + } + } + + private HttpResponse handleGET(HttpRequest request) { + String path = request.getUri().getPath(); + if (path.equals("/tester/v1/log")) { + return new SlimeJsonResponse(logToSlime(testRunner.getLog(request.hasProperty("after") + ? Long.parseLong(request.getProperty("after")) + : -1))); + } else if (path.equals("/tester/v1/status")) { + log.info("Responding with status " + testRunner.getStatus()); + return new Response(testRunner.getStatus().name()); + } + return new Response(Status.NOT_FOUND, "Not found: " + request.getUri().getPath()); + } + + private HttpResponse handlePOST(HttpRequest request) throws IOException { + final String path = request.getUri().getPath(); + if (path.startsWith("/tester/v1/run/")) { + String type = lastElement(path); + TestProfile testProfile = TestProfile.valueOf(type.toUpperCase() + "_TEST"); + byte[] config = IOUtils.readBytes(request.getData(), 1 << 16); + testRunner.test(testProfile, config); + log.info("Started tests of type " + type + " and status is " + testRunner.getStatus()); + return new Response("Successfully started " + type + " tests"); + } + return new Response(Status.NOT_FOUND, "Not found: " + request.getUri().getPath()); + } + + private static String lastElement(String path) { + if (path.endsWith("/")) + path = path.substring(0, path.length() - 1); + int lastSlash = path.lastIndexOf("/"); + if (lastSlash < 0) return path; + return path.substring(lastSlash + 1); + } + + static Slime logToSlime(Collection log) { + Slime slime = new Slime(); + Cursor root = slime.setObject(); + Cursor recordArray = root.setArray("logRecords"); + logArrayToSlime(recordArray, log); + return slime; + } + + static void logArrayToSlime(Cursor recordArray, Collection log) { + log.forEach(record -> { + Cursor recordObject = recordArray.addObject(); + recordObject.setLong("id", record.getSequenceNumber()); + recordObject.setLong("at", record.getMillis()); + recordObject.setString("type", typeOf(record.getLevel())); + String message = record.getMessage(); + if (record.getThrown() != null) { + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + record.getThrown().printStackTrace(new PrintStream(buffer)); + message += "\n" + buffer; + } + recordObject.setString("message", message); + }); + } + + public static String typeOf(Level level) { + return level.getName().equals("html") ? "html" + : level.intValue() < Level.INFO.intValue() ? "debug" + : level.intValue() < Level.WARNING.intValue() ? "info" + : level.intValue() < Level.SEVERE.intValue() ? "warning" + : "error"; + } + + private static class SlimeJsonResponse extends HttpResponse { + private final Slime slime; + + private SlimeJsonResponse(Slime slime) { + super(200); + this.slime = slime; + } + + @Override + public void render(OutputStream outputStream) throws IOException { + new JsonFormat(true).encode(outputStream, slime); + } + + @Override + public String getContentType() { + return CONTENT_TYPE_APPLICATION_JSON; + } + } + + private static class Response extends HttpResponse { + private static final ObjectMapper objectMapper = new ObjectMapper(); + private final String message; + + private Response(String response) { + this(200, response); + } + + private Response(int statusCode, String message) { + super(statusCode); + this.message = message; + } + + @Override + public void render(OutputStream outputStream) throws IOException { + ObjectNode objectNode = objectMapper.createObjectNode(); + objectNode.put("message", message); + objectMapper.writeValue(outputStream, objectNode); + } + + @Override + public String getContentType() { + return CONTENT_TYPE_APPLICATION_JSON; + } + } +} diff --git a/vespa-testrunner-components/src/test/java/com/yahoo/vespa/hosted/testrunner/PomXmlGeneratorTest.java b/vespa-testrunner-components/src/test/java/com/yahoo/vespa/hosted/testrunner/PomXmlGeneratorTest.java index 943583ae42b..391e2a0abbf 100644 --- a/vespa-testrunner-components/src/test/java/com/yahoo/vespa/hosted/testrunner/PomXmlGeneratorTest.java +++ b/vespa-testrunner-components/src/test/java/com/yahoo/vespa/hosted/testrunner/PomXmlGeneratorTest.java @@ -1,6 +1,7 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.hosted.testrunner; +import com.yahoo.vespa.testrunner.legacy.TestProfile; import org.junit.Test; import java.io.IOException; diff --git a/vespa-testrunner-components/src/test/java/com/yahoo/vespa/hosted/testrunner/TestRunnerHandlerTest.java b/vespa-testrunner-components/src/test/java/com/yahoo/vespa/hosted/testrunner/TestRunnerHandlerTest.java new file mode 100644 index 00000000000..fdc6b633630 --- /dev/null +++ b/vespa-testrunner-components/src/test/java/com/yahoo/vespa/hosted/testrunner/TestRunnerHandlerTest.java @@ -0,0 +1,63 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.testrunner; + +import com.yahoo.slime.SlimeUtils; +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.time.Instant; +import java.util.Collections; +import java.util.logging.Level; +import java.util.logging.LogRecord; + +import static org.junit.Assert.assertEquals; + +/** + * @author jvenstad + */ +public class TestRunnerHandlerTest { + + @Test + public void logSerialization() throws IOException { + Log log = new Log(); + LogRecord record = log.getLogRecord(); + String trace = log.getTrace(); + assertEquals("{\"logRecords\":[{\"id\":1,\"at\":2,\"type\":\"info\",\"message\":\"Hello.\\n" + trace + "\"}]}", + new String(SlimeUtils.toJsonBytes(TestRunnerHandler.logToSlime(Collections.singletonList(record))))); + } + + private static class Log { + + private final LogRecord record; + private final String trace; + + public Log() { + Exception exception = new RuntimeException(); + record = createRecord(exception); + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + exception.printStackTrace(new PrintStream(buffer)); + trace = buffer.toString() + .replaceAll("\n", "\\\\n") + .replaceAll("\t", "\\\\t"); + } + + LogRecord getLogRecord() { + return record; + } + + String getTrace() { + return trace; + } + + private static LogRecord createRecord(Exception exception) { + LogRecord record = new LogRecord(Level.INFO, "Hello."); + record.setSequenceNumber(1); + record.setInstant(Instant.ofEpochMilli(2)); + record.setThrown(exception); + return record; + } + } + +} diff --git a/vespa-testrunner-components/src/test/java/com/yahoo/vespa/hosted/testrunner/TestRunnerTest.java b/vespa-testrunner-components/src/test/java/com/yahoo/vespa/hosted/testrunner/TestRunnerTest.java index b513dfba8b5..4612f5b217a 100644 --- a/vespa-testrunner-components/src/test/java/com/yahoo/vespa/hosted/testrunner/TestRunnerTest.java +++ b/vespa-testrunner-components/src/test/java/com/yahoo/vespa/hosted/testrunner/TestRunnerTest.java @@ -1,6 +1,7 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.hosted.testrunner; +import com.yahoo.vespa.testrunner.legacy.TestProfile; import org.fusesource.jansi.Ansi; import org.junit.Before; import org.junit.Rule; @@ -13,8 +14,6 @@ import java.nio.file.Path; import java.util.Iterator; import java.util.logging.LogRecord; -import static com.yahoo.vespa.testrunner.TestRunner.Suite.STAGING_TEST; -import static com.yahoo.vespa.testrunner.TestRunner.Suite.SYSTEM_TEST; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -49,7 +48,7 @@ public class TestRunnerTest { public void ansiCodesAreConvertedToHtml() throws InterruptedException { TestRunner runner = new TestRunner(artifactsPath, testPath, logFile, configFile, settingsFile, __ -> new ProcessBuilder("echo", Ansi.ansi().fg(Ansi.Color.RED).a("Hello!").reset().toString())); - runner.test(SYSTEM_TEST, new byte[0]); + runner.test(TestProfile.SYSTEM_TEST, new byte[0]); while (runner.getStatus() == TestRunner.Status.RUNNING) { Thread.sleep(10); } @@ -66,7 +65,7 @@ public class TestRunnerTest { Files.delete(artifactsPath.resolve("my-tests.jar")); TestRunner runner = new TestRunner(artifactsPath, testPath, logFile, configFile, settingsFile, __ -> new ProcessBuilder("This is a command that doesn't exist, for sure!")); - runner.test(SYSTEM_TEST, new byte[0]); + runner.test(TestProfile.SYSTEM_TEST, new byte[0]); while (runner.getStatus() == TestRunner.Status.RUNNING) { Thread.sleep(10); } @@ -82,7 +81,7 @@ public class TestRunnerTest { public void errorLeadsToError() throws InterruptedException { TestRunner runner = new TestRunner(artifactsPath, testPath, logFile, configFile, settingsFile, __ -> new ProcessBuilder("false")); - runner.test(SYSTEM_TEST, new byte[0]); + runner.test(TestProfile.SYSTEM_TEST, new byte[0]); while (runner.getStatus() == TestRunner.Status.RUNNING) { Thread.sleep(10); } @@ -94,7 +93,7 @@ public class TestRunnerTest { public void failureLeadsToFailure() throws InterruptedException { TestRunner runner = new TestRunner(artifactsPath, testPath, logFile, configFile, settingsFile, __ -> new ProcessBuilder("false")); - runner.test(SYSTEM_TEST, new byte[0]); + runner.test(TestProfile.SYSTEM_TEST, new byte[0]); while (runner.getStatus() == TestRunner.Status.RUNNING) { Thread.sleep(10); } @@ -106,7 +105,7 @@ public class TestRunnerTest { public void filesAreGenerated() throws InterruptedException, IOException { TestRunner runner = new TestRunner(artifactsPath, testPath, logFile, configFile, settingsFile, __ -> new ProcessBuilder("echo", "Hello!")); - runner.test(SYSTEM_TEST, "config".getBytes()); + runner.test(TestProfile.SYSTEM_TEST, "config".getBytes()); while (runner.getStatus() == TestRunner.Status.RUNNING) { Thread.sleep(10); } @@ -120,7 +119,7 @@ public class TestRunnerTest { public void runnerCanBeReused() throws InterruptedException, IOException { TestRunner runner = new TestRunner(artifactsPath, testPath, logFile, configFile, settingsFile, __ -> new ProcessBuilder("sleep", "0.1")); - runner.test(SYSTEM_TEST, "config".getBytes()); + runner.test(TestProfile.SYSTEM_TEST, "config".getBytes()); assertEquals(TestRunner.Status.RUNNING, runner.getStatus()); while (runner.getStatus() == TestRunner.Status.RUNNING) { @@ -129,7 +128,7 @@ public class TestRunnerTest { assertEquals(1, runner.getLog(-1).size()); assertEquals(TestRunner.Status.SUCCESS, runner.getStatus()); - runner.test(STAGING_TEST, "newConfig".getBytes()); + runner.test(TestProfile.STAGING_TEST, "newConfig".getBytes()); while (runner.getStatus() == TestRunner.Status.RUNNING) { Thread.sleep(10); } -- cgit v1.2.3