diff --git a/agent/src/main/java/com/teamscale/jacoco/agent/testimpact/CoverageToJsonStrategyBase.java b/agent/src/main/java/com/teamscale/jacoco/agent/testimpact/CoverageToJsonStrategyBase.java index 1c80b3259..6c76c7c18 100644 --- a/agent/src/main/java/com/teamscale/jacoco/agent/testimpact/CoverageToJsonStrategyBase.java +++ b/agent/src/main/java/com/teamscale/jacoco/agent/testimpact/CoverageToJsonStrategyBase.java @@ -141,7 +141,7 @@ private String createTestwiseCoverageReport(boolean partial) throws IOException, availableTests.clear(); testExecutions.clear(); - return JsonUtils.serialize(report); + return JsonUtils.serializeToJson(report); } } diff --git a/agent/src/main/java/com/teamscale/jacoco/agent/testimpact/TestExecutionWriter.java b/agent/src/main/java/com/teamscale/jacoco/agent/testimpact/TestExecutionWriter.java index 07e5f93af..e62261cda 100644 --- a/agent/src/main/java/com/teamscale/jacoco/agent/testimpact/TestExecutionWriter.java +++ b/agent/src/main/java/com/teamscale/jacoco/agent/testimpact/TestExecutionWriter.java @@ -29,7 +29,7 @@ public TestExecutionWriter(File testExecutionFile) { /** Appends the given {@link TestExecution} to the test execution list file. */ public synchronized void append(TestExecution testExecution) throws IOException { - String json = JsonUtils.serialize(testExecution); + String json = JsonUtils.serializeToJson(testExecution); // the file contains a JSON array if it exists and to append to it, we strip the trailing "]" and append // our new entry and a closing "]" diff --git a/agent/src/test/java/com/teamscale/jacoco/agent/options/AgentOptionsParserTest.java b/agent/src/test/java/com/teamscale/jacoco/agent/options/AgentOptionsParserTest.java index 263b323b1..54d0b0bfb 100644 --- a/agent/src/test/java/com/teamscale/jacoco/agent/options/AgentOptionsParserTest.java +++ b/agent/src/test/java/com/teamscale/jacoco/agent/options/AgentOptionsParserTest.java @@ -101,7 +101,7 @@ public void environmentConfigIdOverridesCommandLineOptions() throws Exception { registration.profilerConfiguration = new ProfilerConfiguration(); registration.profilerConfiguration.configurationId = "my-config"; registration.profilerConfiguration.configurationOptions = "teamscale-partition=foo"; - mockWebServer.enqueue(new MockResponse().setBody(JsonUtils.serialize(registration))); + mockWebServer.enqueue(new MockResponse().setBody(JsonUtils.serializeToJson(registration))); AgentOptionsParser parser = new AgentOptionsParser(new CommandLineLogger(), "my-config", null, teamscaleCredentials, null); AgentOptions options = parseAndThrow(parser, "teamscale-partition=bar"); @@ -125,7 +125,7 @@ public void environmentConfigFileOverridesConfigId() throws Exception { registration.profilerConfiguration = new ProfilerConfiguration(); registration.profilerConfiguration.configurationId = "my-config"; registration.profilerConfiguration.configurationOptions = "teamscale-partition=from-config-id"; - mockWebServer.enqueue(new MockResponse().setBody(JsonUtils.serialize(registration))); + mockWebServer.enqueue(new MockResponse().setBody(JsonUtils.serializeToJson(registration))); AgentOptionsParser parser = new AgentOptionsParser(new CommandLineLogger(), "my-config", configFile.toString(), teamscaleCredentials, null); AgentOptions options = parseAndThrow(parser, "teamscale-partition=from-command-line"); diff --git a/agent/src/test/java/com/teamscale/jacoco/agent/options/AgentOptionsTest.java b/agent/src/test/java/com/teamscale/jacoco/agent/options/AgentOptionsTest.java index 14b259ba2..1e7358eb3 100644 --- a/agent/src/test/java/com/teamscale/jacoco/agent/options/AgentOptionsTest.java +++ b/agent/src/test/java/com/teamscale/jacoco/agent/options/AgentOptionsTest.java @@ -389,7 +389,7 @@ public void testTeamscaleProxyOptionsAreUsedWhileFetchingConfigFromTeamscale() t profilerRegistration.profilerConfiguration = expectedProfilerConfiguration; mockProxyServer.enqueue(new MockResponse().setResponseCode(407)); - mockProxyServer.enqueue(new MockResponse().setResponseCode(200).setBody(JsonUtils.serialize(profilerRegistration))); + mockProxyServer.enqueue(new MockResponse().setResponseCode(200).setBody(JsonUtils.serializeToJson(profilerRegistration))); AgentOptions agentOptions= parseProxyOptions("config-id=config,", ProxySystemProperties.Protocol.HTTP, expectedHost, expectedPort, expectedUser, unexpectedPassword, passwordFile); diff --git a/common-system-test/build.gradle.kts b/common-system-test/build.gradle.kts index d8b2f23e1..5d7d0c942 100644 --- a/common-system-test/build.gradle.kts +++ b/common-system-test/build.gradle.kts @@ -1,6 +1,6 @@ plugins { `java-library` - com.teamscale.`java-convention` + com.teamscale.`kotlin-convention` // we do not enable code coverage recording for the system tests as we already need our agent attached // it would conflict with JaCoCo's } diff --git a/common-system-test/src/main/java/com/teamscale/test/commons/ExternalReport.java b/common-system-test/src/main/java/com/teamscale/test/commons/ExternalReport.java deleted file mode 100644 index 99bf202cf..000000000 --- a/common-system-test/src/main/java/com/teamscale/test/commons/ExternalReport.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.teamscale.test.commons; - -/** Holds a single report uploaded to our fake Teamscale server. */ -public class ExternalReport { - private final String reportString; - - public ExternalReport(String reportString) { - this.reportString = reportString; - } - - public String getReportString() { - return reportString; - } -} diff --git a/common-system-test/src/main/java/com/teamscale/test/commons/Session.java b/common-system-test/src/main/java/com/teamscale/test/commons/Session.java deleted file mode 100644 index 92c11b9c5..000000000 --- a/common-system-test/src/main/java/com/teamscale/test/commons/Session.java +++ /dev/null @@ -1,131 +0,0 @@ -package com.teamscale.test.commons; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.teamscale.client.EReportFormat; -import com.teamscale.client.JsonUtils; -import com.teamscale.report.compact.TeamscaleCompactCoverageReport; -import com.teamscale.report.testwise.model.TestwiseCoverageReport; -import org.conqat.lib.commons.collections.ListMap; -import spark.Request; - -import java.io.IOException; -import java.util.List; -import java.util.stream.Collectors; - -/** - * Represents an upload session to Teamscale which contains data from a single partition, for a specific - * commit/revision. A session may contain data in multiple formats. - */ -public class Session { - - private final String partition; - private final String revision; - private final String commit; - private final String repository; - private boolean committed; - - private final ListMap reports = new ListMap<>(); - - public Session(Request request) { - partition = request.queryParams("partition"); - revision = request.queryParams("revision"); - commit = request.queryParams("t"); - repository = request.queryParams("repository"); - } - - /** - * Retrieves the commit information for the session. Combines the revision and commit data into a single string - * representation separated by a comma. - */ - public String getCommit() { - return revision + ":" + repository + ", " + commit; - } - - /** Returns the partition name for which the session was opened. */ - public String getPartition() { - return partition; - } - - /** - * Marks the session as committed, which means no more data will be added, and a real Teamscale would start - * processing the data now. - */ - public void markCommitted() { - committed = true; - } - - /** Whether the session was commited. */ - public boolean isCommitted() { - return committed; - } - - /** Adds a new report into the session. */ - public void addReport(String format, String report) { - reports.add(EReportFormat.valueOf(format), new ExternalReport(report)); - } - - /** Returns all reports form this session. */ - public List getReports() { - return reports.getValues(); - } - - /** Returns all reports of the given format. */ - public List getReports(EReportFormat format) { - return reports.getCollection(format); - } - - /** Returns the only report in the given format. It asserts that there are no other reports present. */ - public String getOnlyReport(EReportFormat format) { - if (reports.getKeys().size() != 1) { - throw new AssertionError("Expected exactly one report format, but got " + reports.getKeys() + "!"); - } - if (!reports.containsCollection(format)) { - throw new AssertionError( - "No " + format.getReadableName() + " report found! Session contains " + reports.getKeys() - .stream().map(EReportFormat::getReadableName) - .collect(Collectors.toSet()) + " reports."); - } - if (reports.getCollection(format).size() != 1) { - throw new AssertionError( - "Expected exactly one " + format.getReadableName() + " report, but got " + - reports.getCollection(format).size() + "."); - } - return reports.getCollection(format).get(0).getReportString(); - } - - /** - * Returns the report at the given index in {@link #reports}, parsed as a {@link TestwiseCoverageReport}. - * - * @throws IOException when parsing the report fails. - */ - public TestwiseCoverageReport getTestwiseCoverageReport(int index) throws IOException { - return JsonUtils.deserialize( - reports.getCollection(EReportFormat.TESTWISE_COVERAGE).get(index).getReportString(), - TestwiseCoverageReport.class); - } - - /** Returns the only Testwise Coverage report in deserialized form. */ - public TestwiseCoverageReport getOnlyTestwiseCoverageReport() throws JsonProcessingException { - return JsonUtils.deserialize( - getOnlyReport(EReportFormat.TESTWISE_COVERAGE), - TestwiseCoverageReport.class); - } - - /** Returns the only Compact Coverage report in deserialized form. */ - public TeamscaleCompactCoverageReport getOnlyCompactCoverageReport() throws JsonProcessingException { - return JsonUtils.deserialize( - getOnlyReport(EReportFormat.TEAMSCALE_COMPACT_COVERAGE), - TeamscaleCompactCoverageReport.class); - } - - /** - * Returns the report at the given index in {@link #reports}, parsed as a {@link TeamscaleCompactCoverageReport}. - * - * @throws IOException when parsing the report fails. - */ - public TeamscaleCompactCoverageReport getCompactCoverageReport(int index) throws IOException { - return JsonUtils.deserialize( - reports.getCollection(EReportFormat.TEAMSCALE_COMPACT_COVERAGE).get(index).getReportString(), - TeamscaleCompactCoverageReport.class); - } -} diff --git a/common-system-test/src/main/java/com/teamscale/test/commons/SystemTestUtils.java b/common-system-test/src/main/java/com/teamscale/test/commons/SystemTestUtils.java deleted file mode 100644 index 98feb509f..000000000 --- a/common-system-test/src/main/java/com/teamscale/test/commons/SystemTestUtils.java +++ /dev/null @@ -1,198 +0,0 @@ -package com.teamscale.test.commons; - -import com.teamscale.report.testwise.model.TestInfo; -import okhttp3.MediaType; -import okhttp3.RequestBody; -import org.apache.commons.lang3.SystemUtils; -import org.conqat.lib.commons.io.ProcessUtils; -import org.jetbrains.annotations.NotNull; -import retrofit2.Call; -import retrofit2.Retrofit; -import retrofit2.http.Body; -import retrofit2.http.POST; -import retrofit2.http.PUT; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -/** - * Utilities for running system tests. - */ -public class SystemTestUtils { - - /** - * The port for the mock Teamscale server that was picked by the Gradle build script and is guaranteed to not - * conflict with other system tests. - */ - public static final Integer TEAMSCALE_PORT = Integer.getInteger("teamscalePort"); - - /** - * The port for the agent that was picked by the Gradle build script and is guaranteed to not conflict with other - * system tests. - */ - public static final Integer AGENT_PORT = Integer.getInteger("agentPort"); - - /** - * Turns the coverage of the given {@link TestInfo} into a string for simple assertions. - *

- * Example: {@code file1.java:1,7-12;file2.java:9-22,33} - */ - public static String getCoverageString(TestInfo info) { - return info.paths.stream().flatMap(path -> path.files.stream()) - .map(file -> file.fileName + ":" + file.coveredLines).collect( - Collectors.joining(";")); - } - - /** - * Runs the clean and verify goal of the Maven project at the given path. - * - * @throws IOException if running Maven fails. - */ - public static void runMavenTests(String mavenProjectPath) throws IOException { - runMavenTests(mavenProjectPath, new String[0]); - } - - - /** - * Runs the clean and verify goal of the Maven project at the given path with the provided arguments - * - * @throws IOException if running Maven fails. - */ - public static void runMavenTests(String mavenProjectPath, String... args) throws IOException { - String[] allArguments = new String[2 + args.length]; - allArguments[0] = "clean"; - allArguments[1] = "verify"; - System.arraycopy(args, 0, allArguments, 2, args.length); - runMaven(mavenProjectPath, allArguments); - } - - /** - * Runs Maven in the given Maven project path with the given arguments. - * - * @throws IOException if running Maven fails. - */ - public static void runMaven(String mavenProjectPath, String... mavenArguments) throws IOException { - ProcessUtils.ExecutionResult result; - try { - result = ProcessUtils.execute(buildMavenProcess(mavenProjectPath, mavenArguments)); - } catch (IOException e) { - throw new IOException("Failed to run ./mvnw clean verify in directory " + mavenProjectPath, e); - } - - // in case the process succeeded, we still log stdout and stderr in case later assertions fail. This helps - // debug test failures - System.out.println("Maven stdout: " + result.getStdout()); - System.out.println("Maven stderr: " + result.getStderr()); - - if (result.terminatedByTimeoutOrInterruption()) { - throw new IOException("Running Maven failed: " + result.getStdout() + "\n" + result.getStderr()); - } - } - - /** - * Runs Gradle in the given Gradle project path with the given arguments. - * - * @throws IOException if running Gradle fails. - */ - public static ProcessUtils.ExecutionResult runGradle(String gradleProjectPath, String... gradleArguments) throws IOException { - ProcessUtils.ExecutionResult result; - try { - result = ProcessUtils.execute(buildGradleProcess(gradleProjectPath, gradleArguments)); - } catch (IOException e) { - throw new IOException("Failed to run ./gradlew clean verify in directory " + gradleProjectPath, e); - } - - // in case the process succeeded, we still log stdout and stderr in case later assertions fail. This helps - // debug test failures - System.out.println("Gradle stdout: " + result.getStdout()); - System.out.println("Gradle stderr: " + result.getStderr()); - - if (result.terminatedByTimeoutOrInterruption()) { - throw new IOException("Running Gradle failed: " + result.getStdout() + "\n" + result.getStderr()); - } - return result; - } - - /** - * Creates the command-line arguments that can be passed to {@link ProcessBuilder} to invoke Maven with the given - * arguments. - */ - @NotNull - public static ProcessBuilder buildMavenProcess(String mavenProjectDirectory, String... mavenArguments) { - List arguments = new ArrayList<>(); - if (SystemUtils.IS_OS_WINDOWS) { - Collections.addAll(arguments, "cmd", "/c", "mvnw.cmd"); - } else { - arguments.add("./mvnw"); - } - - arguments.addAll(Arrays.asList(mavenArguments)); - - return new ProcessBuilder(arguments).directory(new File(mavenProjectDirectory)); - } - - /** - * Creates the command-line arguments that can be passed to {@link ProcessBuilder} to invoke Gradle with the given - * arguments. - */ - @NotNull - public static ProcessBuilder buildGradleProcess(String gradleProjectDirectory, String... gradleArguments) { - List arguments = new ArrayList<>(); - if (SystemUtils.IS_OS_WINDOWS) { - Collections.addAll(arguments, "cmd", "/c", "gradlew.bat"); - } else { - arguments.add("./gradlew"); - } - - arguments.addAll(Arrays.asList(gradleArguments)); - - return new ProcessBuilder(arguments).directory(new File(gradleProjectDirectory)); - } - - /** Retrieve all files in the `tia/reports` folder sorted by name. */ - public static List getReportFileNames(String mavenProjectPath, String folderName) throws IOException { - try (Stream stream = Files.walk(Paths.get(mavenProjectPath, "target", folderName, "reports"))) { - return stream.filter(Files::isRegularFile).sorted().collect(Collectors.toList()); - } - } - - /** - * Interface for interacting with an agent service that enables coverage dumping and partition management. - */ - public interface AgentService { - /** Dumps coverage */ - @POST("/dump") - Call dump(); - - /** Changes the current partition. */ - @PUT("/partition") - Call changePartition(@Body RequestBody newPartition); - - /** Changes the current partition. Convenience method to pass a plain string. */ - default Call changePartition(String newPartition) { - return changePartition(RequestBody.create(MediaType.parse("text/plain"), newPartition)); - } - } - - /** Instructs the agent via HTTP to dump the currently collected coverage. */ - public static void dumpCoverage(int agentPort) throws IOException { - new Retrofit.Builder().baseUrl("http://localhost:" + agentPort).build() - .create(AgentService.class).dump().execute(); - } - - /** Instructs the agent via HTTP to change the partition. */ - public static void changePartition(int agentPort, String newPartition) throws IOException { - new Retrofit.Builder().baseUrl("http://localhost:" + agentPort).build() - .create(AgentService.class).changePartition(newPartition).execute(); - } - -} diff --git a/common-system-test/src/main/java/com/teamscale/test/commons/TeamscaleMockServer.java b/common-system-test/src/main/java/com/teamscale/test/commons/TeamscaleMockServer.java deleted file mode 100644 index d3c437e0d..000000000 --- a/common-system-test/src/main/java/com/teamscale/test/commons/TeamscaleMockServer.java +++ /dev/null @@ -1,310 +0,0 @@ -package com.teamscale.test.commons; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.teamscale.client.EReportFormat; -import com.teamscale.client.JsonUtils; -import com.teamscale.client.PrioritizableTest; -import com.teamscale.client.PrioritizableTestCluster; -import com.teamscale.client.ProfilerConfiguration; -import com.teamscale.client.ProfilerRegistration; -import com.teamscale.client.TestWithClusterId; -import com.teamscale.report.testwise.model.TestwiseCoverageReport; -import spark.Request; -import spark.Response; -import spark.Service; -import spark.utils.IOUtils; - -import javax.servlet.MultipartConfigElement; -import javax.servlet.ServletException; -import javax.servlet.http.Part; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Base64; -import java.util.Collection; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.UUID; -import java.util.stream.Collectors; - -import static java.util.stream.Collectors.toList; -import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR; - -/** - * Mocks a Teamscale server: returns predetermined impacted tests and stores all uploaded reports so tests can run - * assertions on them. - */ -public class TeamscaleMockServer { - - /** All report upload sessions that were created. */ - private final Map sessions = new HashMap<>(); - /** All user agents that were present in the received requests. */ - public final Set collectedUserAgents = new HashSet<>(); - - /** A list of all commits for which impacted tests were requested. Can either be branch:timestamp or revision */ - public final List impactedTestCommits = new ArrayList<>(); - /** A list of all commits used as baseline for retrieving impacted tests */ - public final List baselines = new ArrayList<>(); - - /** All tests that the test engine has signaled to Teamscale as being available for execution. */ - public final Set allAvailableTests = new HashSet<>(); - private final Path tempDir = Files.createTempDirectory("TeamscaleMockServer"); - private final Service service; - private List impactedTests; - private final List profilerEvents = new ArrayList<>(); - private ProfilerConfiguration profilerConfiguration; - private String username = null; - private String accessToken = null; - - public TeamscaleMockServer(int port) throws IOException { - service = Service.ignite(); - service.port(port); - service.exception(Exception.class, (Exception exception, Request request, Response response) -> { - response.status(SC_INTERNAL_SERVER_ERROR); - response.body("Exception: " + exception.getMessage()); - }); - service.notFound((Request request, Response response) -> { - response.status(SC_INTERNAL_SERVER_ERROR); - return "Unexpected request: " + request.requestMethod() + " " + request.uri(); - }); - service.init(); - service.awaitInitialization(); - } - - /** Resets the data collected by the mock server for the next test. */ - public void reset() { - sessions.clear(); - allAvailableTests.clear(); - profilerEvents.clear(); - impactedTestCommits.clear(); - baselines.clear(); - } - - /** Returns all committed sessions. */ - public List getSessions() { - return sessions.values().stream().filter(Session::isCommitted) - .sorted(Comparator.comparing(Session::getPartition)).collect(toList()); - } - - /** Returns the session with the given partition name */ - public Session getSession(String partition) { - return getSessions().stream().filter(session -> session.getPartition().equals(partition)).findFirst() - .orElseThrow(() -> new AssertionError("No session found for partition: " + partition)); - } - - /** Asserts that there is only one session and returns it. */ - public Session getOnlySession() { - List sessions = getSessions(); - if (sessions.size() != 1) { - throw new AssertionError("Expected exactly one session, but got " + sessions.size()); - } - return sessions.get(0); - } - - /** Asserts that there is only one session and that it has the expected partition name and returns it. */ - public Session getOnlySession(String partition) { - Session session = getOnlySession(); - if (!session.getPartition().equals(partition)) { - throw new AssertionError( - "Expected only session to have partition " + partition + " but got " + session.getPartition() + "!"); - } - return session; - } - - /** - * Asserts that there is exactly one session with the expected partition name, containing exactly one report in the - * given format and returns it. - */ - public String getOnlyReport(String partition, EReportFormat format) { - Session session = getOnlySession(partition); - return session.getOnlyReport(format); - } - - /** - * Asserts that there is exactly one session with the expected partition name, containing exactly one Testwise - * Coverage report and returns a deserialized version of it. - */ - public TestwiseCoverageReport getOnlyTestwiseCoverageReport(String partition) throws JsonProcessingException { - Session session = getOnlySession(partition); - return session.getOnlyTestwiseCoverageReport(); - } - - /** Enables authentication for the mock server by setting the provided username and access token. */ - public TeamscaleMockServer withAuthentication(String username, String accessToken) { - this.username = username; - this.accessToken = accessToken; - return this; - } - - /** Configures the server to accept report uploads and store them within the mock for later retrieval. */ - public TeamscaleMockServer acceptingReportUploads() { - service.post("api/v2024.7.0/projects/:projectId/external-analysis/session/:sessionId/report", - this::handleReport); - service.post("api/v2024.7.0/projects/:projectId/external-analysis/session", - this::createSession); - service.post("api/v2024.7.0/projects/:projectId/external-analysis/session/:sessionId", - this::commitSession); - return this; - } - - private Object commitSession(Request request, Response response) { - requireAuthentication(request, response); - sessions.get(request.params("sessionId")).markCommitted(); - return ""; - } - - private Object createSession(Request request, Response response) throws JsonProcessingException { - requireAuthentication(request, response); - - String sessionId = UUID.randomUUID().toString(); - sessions.put(sessionId, new Session(request)); - return JsonUtils.serialize(sessionId); - } - - /** Configures the server to answer all impacted test calls with the given tests. */ - public TeamscaleMockServer withImpactedTests(String... impactedTests) { - this.impactedTests = Arrays.asList(impactedTests); - service.put("api/v2024.7.0/projects/:projectName/impacted-tests", this::handleImpactedTests); - return this; - } - - /** Configures the server to answer all impacted test calls with the given tests. */ - public TeamscaleMockServer withProfilerConfiguration(ProfilerConfiguration profilerConfiguration) { - this.profilerConfiguration = profilerConfiguration; - service.post("api/v2024.7.0/profilers", this::handleProfilerRegistration); - service.put("api/v2024.7.0/profilers/:profilerId", this::handleProfilerHeartbeat); - service.delete("api/v2024.7.0/profilers/:profilerId", this::handleProfilerUnregister); - service.post("api/v2024.7.0/profilers/:profilerId/logs", this::handleProfilerLogs); - return this; - } - - /** Configures the server to answer all POST/PUT requests with an error. */ - public TeamscaleMockServer disallowingStateChanges() { - service.post("", (request, response) -> { - throw new IllegalStateException("No POST to the mock server expected!"); - }); - service.put("", (request, response) -> { - throw new IllegalStateException("No PUT to the mock server expected!"); - }); - return this; - } - - private String handleImpactedTests(Request request, Response response) throws IOException { - requireAuthentication(request, response); - - collectedUserAgents.add(request.headers("User-Agent")); - impactedTestCommits.add(request.queryParams("end-revision") + ":" + request.queryParams( - "repository") + ", " + request.queryParams("end")); - baselines.add(request.queryParams("baseline-revision") + ", " + request.queryParams("baseline")); - List availableTests = JsonUtils.deserializeList(request.body(), TestWithClusterId.class); - allAvailableTests.addAll(availableTests); - Map, List> impactedTestsByCluster = availableTests.stream() - .filter(availableTest -> impactedTests.contains(availableTest.getTestName())).collect( - Collectors.groupingBy( - testWithClusterId -> Optional.ofNullable(testWithClusterId.getClusterId()))); - List testClusters = new ArrayList<>(); - impactedTestsByCluster.forEach( - (clusterId, impactedTests) -> testClusters.add(new PrioritizableTestCluster(clusterId.orElse(null), - impactedTests.stream().map(TestWithClusterId::getTestName).map(PrioritizableTest::new) - .collect(toList())))); - return JsonUtils.serialize(testClusters); - } - - private String handleProfilerRegistration(Request request, Response response) throws JsonProcessingException { - requireAuthentication(request, response); - - collectedUserAgents.add(request.headers("User-Agent")); - profilerEvents.add( - "Profiler registered and requested configuration " + request.queryParams("configuration-id")); - ProfilerRegistration registration = new ProfilerRegistration(); - registration.profilerConfiguration = this.profilerConfiguration; - registration.profilerId = "123"; - return JsonUtils.serialize(registration); - } - - private String handleProfilerHeartbeat(Request request, Response response) { - requireAuthentication(request, response); - - collectedUserAgents.add(request.headers("User-Agent")); - profilerEvents.add("Profiler " + request.params(":profilerId") + " sent heartbeat"); - return ""; - } - - private String handleProfilerLogs(Request request, Response response) { - requireAuthentication(request, response); - - collectedUserAgents.add(request.headers("User-Agent")); - profilerEvents.add("Profiler " + request.params(":profilerId") + " sent logs"); - return ""; - } - - private String handleProfilerUnregister(Request request, Response response) { - requireAuthentication(request, response); - - collectedUserAgents.add(request.headers("User-Agent")); - profilerEvents.add("Profiler " + request.params(":profilerId") + " unregistered"); - return "foo"; - } - - public List getProfilerEvents() { - return profilerEvents; - } - - private String handleReport(Request request, Response response) throws IOException, ServletException { - requireAuthentication(request, response); - - String sessionId = request.params(":sessionId"); - Session session; - if (sessionId.equals("auto-create")) { - session = new Session(request); - session.markCommitted(); - sessions.put(UUID.randomUUID().toString(), session); - } else { - session = sessions.get(sessionId); - } - - collectedUserAgents.add(request.headers("User-Agent")); - MultipartConfigElement multipartConfigElement = new MultipartConfigElement(tempDir.toString()); - request.raw().setAttribute("org.eclipse.jetty.multipartConfig", multipartConfigElement); - - Collection parts = request.raw().getParts(); - String format = request.queryParams("format"); - for (Part part : parts) { - String reportString = IOUtils.toString(part.getInputStream()); - session.addReport(format, reportString); - part.delete(); - } - - return "success"; - } - - /** - * Shuts down the mock server and waits for it to be stopped. - */ - public void shutdown() { - service.stop(); - service.awaitStop(); - } - - private void requireAuthentication(Request request, Response response) { - if (username != null && accessToken != null) { - String authHeader = request.headers("Authorization"); - if (authHeader == null || !authHeader.equals(buildBasicAuthHeader(username, accessToken))) { - response.status(401); - throw new IllegalArgumentException("Unauthorized"); - } - } - } - - private String buildBasicAuthHeader(String username, String accessToken) { - String credentials = username + ":" + accessToken; - return "Basic " + Base64.getEncoder().encodeToString(credentials.getBytes()); - } -} diff --git a/common-system-test/src/main/kotlin/com/teamscale/test/commons/ExternalReport.kt b/common-system-test/src/main/kotlin/com/teamscale/test/commons/ExternalReport.kt new file mode 100644 index 000000000..2c59ee82c --- /dev/null +++ b/common-system-test/src/main/kotlin/com/teamscale/test/commons/ExternalReport.kt @@ -0,0 +1,4 @@ +package com.teamscale.test.commons + +/** Holds a single report uploaded to our fake Teamscale server. */ +class ExternalReport(val reportString: String) diff --git a/common-system-test/src/main/kotlin/com/teamscale/test/commons/Session.kt b/common-system-test/src/main/kotlin/com/teamscale/test/commons/Session.kt new file mode 100644 index 000000000..fe5a0dea1 --- /dev/null +++ b/common-system-test/src/main/kotlin/com/teamscale/test/commons/Session.kt @@ -0,0 +1,106 @@ +package com.teamscale.test.commons + +import com.fasterxml.jackson.core.JsonProcessingException +import com.teamscale.client.EReportFormat +import com.teamscale.client.JsonUtils +import com.teamscale.report.compact.TeamscaleCompactCoverageReport +import com.teamscale.report.testwise.model.TestwiseCoverageReport +import org.conqat.lib.commons.collections.ListMap +import spark.Request +import java.io.IOException +import java.util.stream.Collectors + +/** + * Represents an upload session to Teamscale, which contains data from a single partition, for a specific + * commit/revision. A session may contain data in multiple formats. + */ +class Session(request: Request) { + /** Returns the partition name for which the session was opened. */ + val partition: String? = request.queryParams("partition") + private val revision: String? = request.queryParams("revision") + private val commit: String? = request.queryParams("t") + private val repository: String? = request.queryParams("repository") + + /** Whether the session was committed. */ + var isCommitted: Boolean = false + private set + + private val reports = ListMap() + + /** + * Retrieves the commit information for the session. Combines the revision and commit data into a single string + * representation separated by a comma. + */ + fun getCommit() = "$revision:$repository, $commit" + + /** + * Marks the session as committed, which means no more data will be added, and a real Teamscale would start + * processing the data now. + */ + fun markCommitted() { + isCommitted = true + } + + /** Adds a new report into the session. */ + fun addReport(format: String, report: String) { + reports.add(EReportFormat.valueOf(format), ExternalReport(report)) + } + + /** Returns all reports from this session. */ + fun getReports(): List = reports.getValues() + + /** Returns all reports of the given format. */ + fun getReports(format: EReportFormat): MutableList = reports.getCollection(format) + + /** Returns the only report in the given format. It asserts that there are no other reports present. */ + fun getOnlyReport(format: EReportFormat): String { + check(reports.getKeys().size == 1) { + "Expected exactly one report format, but got ${reports.getKeys()}!" + } + check(reports.containsCollection(format)) { + "No ${format.readableName} report found! Session contains ${ + reports.getKeys().map(EReportFormat::readableName).toSet() + } reports." + } + check(reports.getCollection(format).size == 1) { + "Expected exactly one ${format.readableName} report, but got ${reports.getCollection(format).size}." + } + return reports.getCollection(format).first().reportString + } + + /** + * Returns the report at the given index in [reports], parsed as a [TestwiseCoverageReport]. + * + * @throws IOException when parsing the report fails. + */ + @Throws(IOException::class) + fun getTestwiseCoverageReport(index: Int) = + reports.getCollection(EReportFormat.TESTWISE_COVERAGE).getOrNull(index)?.let { + JsonUtils.deserialize(it.reportString) + } + + @get:Throws(JsonProcessingException::class) + val onlyTestwiseCoverageReport: TestwiseCoverageReport + /** Returns the only Testwise Coverage report in deserialized form. */ + get() = JsonUtils.deserialize( + getOnlyReport(EReportFormat.TESTWISE_COVERAGE) + ) + + @get:Throws(JsonProcessingException::class) + val onlyCompactCoverageReport: TeamscaleCompactCoverageReport + /** Returns the only Compact Coverage report in deserialized form. */ + get() = JsonUtils.deserialize( + getOnlyReport(EReportFormat.TEAMSCALE_COMPACT_COVERAGE) + ) + + /** + * Returns the report at the given index in [reports], parsed as a [TeamscaleCompactCoverageReport]. + * + * @throws IOException when parsing the report fails. + */ + @Throws(IOException::class) + fun getCompactCoverageReport(index: Int) = + reports.getCollection(EReportFormat.TEAMSCALE_COMPACT_COVERAGE).getOrNull(index)?.let { + JsonUtils.deserialize(it.reportString) + } +} diff --git a/common-system-test/src/main/kotlin/com/teamscale/test/commons/SystemTestUtils.kt b/common-system-test/src/main/kotlin/com/teamscale/test/commons/SystemTestUtils.kt new file mode 100644 index 000000000..4c6a7beed --- /dev/null +++ b/common-system-test/src/main/kotlin/com/teamscale/test/commons/SystemTestUtils.kt @@ -0,0 +1,190 @@ +package com.teamscale.test.commons + +import com.teamscale.report.testwise.model.TestInfo +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.RequestBody +import okhttp3.RequestBody.Companion.toRequestBody +import org.apache.commons.lang3.SystemUtils +import org.conqat.lib.commons.io.ProcessUtils +import retrofit2.Call +import retrofit2.Retrofit +import retrofit2.create +import retrofit2.http.Body +import retrofit2.http.POST +import retrofit2.http.PUT +import java.io.File +import java.io.IOException +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.Paths +import kotlin.streams.asSequence + +/** + * Utilities for running system tests. + */ +object SystemTestUtils { + /** + * The port for the mock Teamscale server that was picked by the Gradle build script and is guaranteed to not + * conflict with other system tests. + */ + @JvmField + val TEAMSCALE_PORT: Int = Integer.getInteger("teamscalePort") + + /** + * The port for the agent that was picked by the Gradle build script and is guaranteed to not conflict with other + * system tests. + */ + @JvmField + val AGENT_PORT: Int = Integer.getInteger("agentPort") + + /** + * Turns the coverage of the given [TestInfo] into a string for simple assertions. + * + * + * Example: `file1.java:1,7-12;file2.java:9-22,33` + */ + @JvmStatic + val TestInfo.coverage + get() = paths.flatMap { it.files }.joinToString(";") { "${it.fileName}:${it.coveredLines}" } + + /** + * Runs the clean and verify goal of the Maven project at the given path with the provided arguments + * + * @throws IOException if running Maven fails. + */ + @JvmStatic + @Throws(IOException::class) + fun runMavenTests(mavenProjectPath: String, vararg args: String) { + val allArguments = mutableListOf("clean", "verify").apply { + addAll(args) + } + runMaven(mavenProjectPath, *allArguments.toTypedArray()) + } + + /** + * Runs Maven in the given Maven project path with the given arguments. + * + * @throws IOException if running Maven fails. + */ + @JvmStatic + @Throws(IOException::class) + fun runMaven(mavenProjectPath: String, vararg mavenArguments: String) { + val result: ProcessUtils.ExecutionResult + try { + result = ProcessUtils.execute(buildMavenProcess(mavenProjectPath, *mavenArguments)) + } catch (e: IOException) { + throw IOException("Failed to run ./mvnw clean verify in directory $mavenProjectPath", e) + } + + // in case the process succeeded, we still log stdout and stderr in case later assertions fail. This helps + // debug test failures + println("Maven stdout: ${result.stdout}") + println("Maven stderr: ${result.stderr}") + + if (result.terminatedByTimeoutOrInterruption()) { + throw IOException("Running Maven failed: ${result.stdout}\n${result.stderr}") + } + } + + /** + * Runs Gradle in the given Gradle project path with the given arguments. + * + * @throws IOException if running Gradle fails. + */ + @JvmStatic + @Throws(IOException::class) + fun runGradle(gradleProjectPath: String, vararg gradleArguments: String): ProcessUtils.ExecutionResult { + val result: ProcessUtils.ExecutionResult + try { + result = ProcessUtils.execute(buildGradleProcess(gradleProjectPath, *gradleArguments)) + } catch (e: IOException) { + throw IOException("Failed to run ./gradlew clean verify in directory $gradleProjectPath", e) + } + + // in case the process succeeded, we still log stdout and stderr in case later assertions fail. This helps + // debug test failures + println("Gradle stdout: ${result.stdout}") + println("Gradle stderr: ${result.stderr}") + + if (result.terminatedByTimeoutOrInterruption()) { + throw IOException("Running Gradle failed: ${result.stdout}\n${result.stderr}") + } + return result + } + + /** + * Creates the command-line arguments that can be passed to [ProcessBuilder] to invoke Maven with the given + * arguments. + */ + @JvmStatic + fun buildMavenProcess(mavenProjectDirectory: String, vararg mavenArguments: String): ProcessBuilder { + val arguments = mutableListOf() + if (SystemUtils.IS_OS_WINDOWS) { + arguments.addAll(listOf("cmd", "/c", "mvnw.cmd")) + } else { + arguments.add("./mvnw") + } + + arguments.addAll(listOf(*mavenArguments)) + + return ProcessBuilder(arguments).directory(File(mavenProjectDirectory)) + } + + /** + * Creates the command-line arguments that can be passed to [ProcessBuilder] to invoke Gradle with the given + * arguments. + */ + private fun buildGradleProcess(gradleProjectDirectory: String, vararg gradleArguments: String): ProcessBuilder { + val arguments = mutableListOf() + if (SystemUtils.IS_OS_WINDOWS) { + arguments.addAll(listOf("cmd", "/c", "gradlew.bat")) + } else { + arguments.add("./gradlew") + } + + arguments.addAll(listOf(*gradleArguments)) + + return ProcessBuilder(arguments).directory(File(gradleProjectDirectory)) + } + + /** Retrieve all files in the `tia/reports` folder sorted by name. */ + @JvmStatic + @Throws(IOException::class) + fun getReportFileNames(mavenProjectPath: String, folderName: String): List { + Files.walk(Paths.get(mavenProjectPath, "target", folderName, "reports")).use { stream -> + return stream.asSequence().filter { Files.isRegularFile(it) }.sorted().toList() + } + } + + /** Instructs the agent via HTTP to dump the currently collected coverage. */ + @JvmStatic + @Throws(IOException::class) + fun dumpCoverage(agentPort: Int) { + Retrofit.Builder().baseUrl("http://localhost:$agentPort").build() + .create().dump().execute() + } + + /** Instructs the agent via HTTP to change the partition. */ + @JvmStatic + @Throws(IOException::class) + fun changePartition(agentPort: Int, newPartition: String) { + val requestBody = newPartition.toRequestBody("text/plain".toMediaTypeOrNull()) + Retrofit.Builder().baseUrl("http://localhost:$agentPort").build() + .create() + .changePartition(requestBody) + .execute() + } + + /** + * Interface for interacting with an agent service that enables coverage dumping and partition management. + */ + interface AgentService { + /** Dumps coverage */ + @POST("/dump") + fun dump(): Call + + /** Changes the current partition. */ + @PUT("/partition") + fun changePartition(@Body newPartition: RequestBody): Call + } +} diff --git a/common-system-test/src/main/kotlin/com/teamscale/test/commons/TeamscaleMockServer.kt b/common-system-test/src/main/kotlin/com/teamscale/test/commons/TeamscaleMockServer.kt new file mode 100644 index 000000000..6c9857c8e --- /dev/null +++ b/common-system-test/src/main/kotlin/com/teamscale/test/commons/TeamscaleMockServer.kt @@ -0,0 +1,287 @@ +package com.teamscale.test.commons + +import com.fasterxml.jackson.core.JsonProcessingException +import com.teamscale.client.* +import com.teamscale.client.JsonUtils.deserializeList +import com.teamscale.client.JsonUtils.serializeToJson +import spark.* +import spark.utils.IOUtils +import java.io.IOException +import java.nio.file.Files +import java.util.* +import java.util.Base64 +import javax.servlet.MultipartConfigElement +import javax.servlet.ServletException +import javax.servlet.http.HttpServletResponse +import kotlin.collections.component1 +import kotlin.collections.component2 +import kotlin.collections.groupBy + +/** + * Mocks a Teamscale server: returns predetermined impacted tests and stores all uploaded reports so tests can run + * assertions on them. + */ +class TeamscaleMockServer(port: Int) { + /** All report upload sessions that were created. */ + private val sessions = hashMapOf() + + /** All user agents that were present in the received requests. */ + @JvmField + val collectedUserAgents = mutableSetOf() + + /** A list of all commits for which impacted tests were requested. Can either be branch:timestamp or revision */ + @JvmField + val impactedTestCommits = mutableListOf() + + /** A list of all commits used as baseline for retrieving impacted tests */ + @JvmField + val baselines = mutableListOf() + + /** All tests that the test engine has signaled to Teamscale as being available for execution. */ + @JvmField + val allAvailableTests = mutableSetOf() + + private val tempDir = Files.createTempDirectory("TeamscaleMockServer") + private val service = Service.ignite() + private var impactedTests = listOf() + val profilerEvents = mutableListOf() + private var profilerConfiguration: ProfilerConfiguration? = null + private var username: String? = null + private var accessToken: String? = null + + init { + service.port(port) + service.exception(Exception::class.java) { exception, _, response -> + response.status(HttpServletResponse.SC_INTERNAL_SERVER_ERROR) + response.body("Exception: " + exception!!.message) + } + service.notFound { request, response -> + response.status(HttpServletResponse.SC_INTERNAL_SERVER_ERROR) + "Unexpected request: ${request.requestMethod()} ${request.uri()}" + } + service.init() + service.awaitInitialization() + } + + /** Resets the data collected by the mock server for the next test. */ + fun reset() { + sessions.clear() + allAvailableTests.clear() + profilerEvents.clear() + impactedTestCommits.clear() + baselines.clear() + } + + /** Returns all committed sessions. */ + fun getSessions() = sessions.values.filter { it.isCommitted }.sortedBy { it.partition } + + /** Returns the session with the given partition name */ + fun getSession(partition: String) = + getSessions().firstOrNull { it.partition == partition } + ?: throw AssertionError("No session found for partition: $partition") + + val onlySession: Session + /** Asserts that there is only one session and returns it. */ + get() { + val sessions = getSessions() + check(sessions.size == 1) { + "Expected exactly one session, but got ${sessions.size}" + } + return sessions.first() + } + + /** Asserts that there is only one session and that it has the expected partition name and returns it. */ + fun getOnlySession(partition: String): Session { + val session = onlySession + check(session.partition == partition) { + "Expected only session to have partition $partition but got ${session.partition}!" + } + return session + } + + /** + * Asserts that there is exactly one session with the expected partition name, containing exactly one report in the + * given format and returns it. + */ + fun getOnlyReport(partition: String, format: EReportFormat): String { + val session = getOnlySession(partition) + return session.getOnlyReport(format) + } + + /** + * Asserts that there is exactly one session with the expected partition name, containing exactly one Testwise + * Coverage report and returns a deserialized version of it. + */ + @Throws(JsonProcessingException::class) + fun getOnlyTestwiseCoverageReport(partition: String) = + getOnlySession(partition).onlyTestwiseCoverageReport + + /** Enables authentication for the mock server by setting the provided username and access token. */ + fun withAuthentication(username: String, accessToken: String): TeamscaleMockServer { + this.username = username + this.accessToken = accessToken + return this + } + + /** Configures the server to accept report uploads and store them within the mock for later retrieval. */ + fun acceptingReportUploads(): TeamscaleMockServer { + service.post("api/v2024.7.0/projects/:projectId/external-analysis/session/:sessionId/report", ::handleReport) + service.post("api/v2024.7.0/projects/:projectId/external-analysis/session", ::createSession) + service.post("api/v2024.7.0/projects/:projectId/external-analysis/session/:sessionId", ::commitSession) + return this + } + + private fun commitSession(request: Request, response: Response): Any { + requireAuthentication(request, response) + sessions.get(request.params("sessionId"))?.markCommitted() + return "" + } + + @Throws(JsonProcessingException::class) + private fun createSession(request: Request, response: Response): Any { + requireAuthentication(request, response) + + val sessionId = UUID.randomUUID().toString() + sessions.put(sessionId, Session(request)) + return sessionId.serializeToJson() + } + + /** Configures the server to answer all impacted test calls with the given tests. */ + fun withImpactedTests(vararg impactedTests: String): TeamscaleMockServer { + this.impactedTests = listOf(*impactedTests) + service.put("api/v2024.7.0/projects/:projectName/impacted-tests", ::handleImpactedTests) + return this + } + + /** Configures the server to answer all impacted test calls with the given tests. */ + fun withProfilerConfiguration(profilerConfiguration: ProfilerConfiguration?): TeamscaleMockServer { + this.profilerConfiguration = profilerConfiguration + service.post("api/v2024.7.0/profilers", ::handleProfilerRegistration) + service.put("api/v2024.7.0/profilers/:profilerId", ::handleProfilerHeartbeat) + service.delete("api/v2024.7.0/profilers/:profilerId", ::handleProfilerUnregister) + service.post("api/v2024.7.0/profilers/:profilerId/logs", ::handleProfilerLogs) + return this + } + + /** Configures the server to answer all POST/PUT requests with an error. */ + fun disallowingStateChanges(): TeamscaleMockServer { + service.post("") { _, _ -> + throw IllegalStateException("No POST to the mock server expected!") + } + service.put("") { _, _ -> + throw IllegalStateException("No PUT to the mock server expected!") + } + return this + } + + @Throws(IOException::class) + private fun handleImpactedTests(request: Request, response: Response): String { + requireAuthentication(request, response) + + request.collectUserAgent() + impactedTestCommits.add("${request.queryParams("end-revision")}:${request.queryParams("repository")}, ${request.queryParams("end")}") + baselines.add("${request.queryParams("baseline-revision")}, ${request.queryParams("baseline")}") + val availableTests = deserializeList(request.body()) + allAvailableTests.addAll(availableTests) + return availableTests + .filter { availableTest -> impactedTests.contains(availableTest.testName) } + .groupBy { testWithClusterId -> Optional.ofNullable(testWithClusterId.clusterId) } + .map { (clusterId, impactedTests) -> + PrioritizableTestCluster( + clusterId.orElse(null), + impactedTests.map { PrioritizableTest(it.testName) } + ) + }.serializeToJson() + } + + @Throws(JsonProcessingException::class) + private fun handleProfilerRegistration(request: Request, response: Response): String { + requireAuthentication(request, response) + + request.collectUserAgent() + profilerEvents.add( + "Profiler registered and requested configuration ${request.queryParams("configuration-id")}" + ) + val registration = ProfilerRegistration() + registration.profilerConfiguration = this.profilerConfiguration + registration.profilerId = "123" + return registration.serializeToJson() + } + + private fun handleProfilerHeartbeat(request: Request, response: Response): String { + requireAuthentication(request, response) + + request.collectUserAgent() + profilerEvents.add("Profiler " + request.params(":profilerId") + " sent heartbeat") + return "" + } + + private fun handleProfilerLogs(request: Request, response: Response): String { + requireAuthentication(request, response) + + request.collectUserAgent() + profilerEvents.add("Profiler " + request.params(":profilerId") + " sent logs") + return "" + } + + private fun handleProfilerUnregister(request: Request, response: Response): String { + requireAuthentication(request, response) + + request.collectUserAgent() + profilerEvents.add("Profiler " + request.params(":profilerId") + " unregistered") + return "foo" + } + + @Throws(IOException::class, ServletException::class) + private fun handleReport(request: Request, response: Response): String { + requireAuthentication(request, response) + + val sessionId = request.params(":sessionId") + val session: Session + if (sessionId == "auto-create") { + session = Session(request) + session.markCommitted() + sessions.put(UUID.randomUUID().toString(), session) + } else { + session = sessions.get(sessionId)!! + } + + request.collectUserAgent() + val multipartConfigElement = MultipartConfigElement(tempDir.toString()) + request.raw().setAttribute("org.eclipse.jetty.multipartConfig", multipartConfigElement) + + val format = request.queryParams("format") + request.raw().parts.forEach { part -> + val reportString = IOUtils.toString(part.inputStream) + session.addReport(format, reportString) + part.delete() + } + + return "success" + } + + /** + * Shuts down the mock server and waits for it to be stopped. + */ + fun shutdown() { + service.stop() + service.awaitStop() + } + + private fun requireAuthentication(request: Request, response: Response) { + if (username != null && accessToken != null) { + val authHeader = request.headers("Authorization") + if (authHeader == null || authHeader != buildBasicAuthHeader(username, accessToken)) { + response.status(401) + throw IllegalArgumentException("Unauthorized") + } + } + } + + private fun buildBasicAuthHeader(username: String?, accessToken: String?) = + "Basic " + Base64.getEncoder().encodeToString("$username:$accessToken".toByteArray()) + + private fun Request.collectUserAgent() { + collectedUserAgents.add(headers("User-Agent")) + } +} diff --git a/report-generator/src/main/kotlin/com/teamscale/report/ReportUtils.kt b/report-generator/src/main/kotlin/com/teamscale/report/ReportUtils.kt index d12081c06..d0a95207a 100644 --- a/report-generator/src/main/kotlin/com/teamscale/report/ReportUtils.kt +++ b/report-generator/src/main/kotlin/com/teamscale/report/ReportUtils.kt @@ -3,13 +3,13 @@ package com.teamscale.report import com.fasterxml.jackson.core.JsonProcessingException import com.teamscale.client.FileSystemUtils import com.teamscale.client.JsonUtils +import com.teamscale.client.JsonUtils.serializeToJson import com.teamscale.client.TestDetails import com.teamscale.report.testwise.ETestArtifactFormat import com.teamscale.report.testwise.model.TestExecution import com.teamscale.report.testwise.model.TestwiseCoverageReport import java.io.File import java.io.IOException -import java.util.* /** Utilities for generating reports. */ object ReportUtils { @@ -36,9 +36,7 @@ object ReportUtils { /** Converts to given report to a json string. For testing only. */ @JvmStatic @Throws(JsonProcessingException::class) - fun getTestwiseCoverageReportAsString( - report: TestwiseCoverageReport - ) = JsonUtils.serialize(report) + fun getTestwiseCoverageReportAsString(report: TestwiseCoverageReport) = report.serializeToJson() /** Writes the report object to the given file as json. */ @Throws(IOException::class) diff --git a/system-tests/api-changing-settings-should-dump/build.gradle.kts b/system-tests/api-changing-settings-should-dump/build.gradle.kts index e1341aa2e..641f4624f 100644 --- a/system-tests/api-changing-settings-should-dump/build.gradle.kts +++ b/system-tests/api-changing-settings-should-dump/build.gradle.kts @@ -1,4 +1,5 @@ plugins { + com.teamscale.`kotlin-convention` com.teamscale.`system-test-convention` } diff --git a/system-tests/api-changing-settings-should-dump/src/main/java/systemundertest/SystemUnderTest.java b/system-tests/api-changing-settings-should-dump/src/main/java/systemundertest/SystemUnderTest.java deleted file mode 100644 index 987a51a19..000000000 --- a/system-tests/api-changing-settings-should-dump/src/main/java/systemundertest/SystemUnderTest.java +++ /dev/null @@ -1,14 +0,0 @@ -package systemundertest; - -/** Fake system under test to generate some coverage. */ -public class SystemUnderTest { - - public int foo() { - return 12; - } - - public int bar() { - return 13; - } - -} diff --git a/system-tests/api-changing-settings-should-dump/src/main/kotlin/systemundertest/SystemUnderTest.kt b/system-tests/api-changing-settings-should-dump/src/main/kotlin/systemundertest/SystemUnderTest.kt new file mode 100644 index 000000000..076f662f6 --- /dev/null +++ b/system-tests/api-changing-settings-should-dump/src/main/kotlin/systemundertest/SystemUnderTest.kt @@ -0,0 +1,8 @@ +package systemundertest + +/** Fake system under test to generate some coverage. */ +class SystemUnderTest { + fun foo() = 12 + + fun bar() = 13 +} diff --git a/system-tests/api-changing-settings-should-dump/src/test/java/com/teamscale/tia/client/ApiChangingSettingsShouldDumpSystemTest.java b/system-tests/api-changing-settings-should-dump/src/test/java/com/teamscale/tia/client/ApiChangingSettingsShouldDumpSystemTest.java deleted file mode 100644 index 88e436080..000000000 --- a/system-tests/api-changing-settings-should-dump/src/test/java/com/teamscale/tia/client/ApiChangingSettingsShouldDumpSystemTest.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.teamscale.tia.client; - -import com.teamscale.client.EReportFormat; -import com.teamscale.test.commons.Session; -import com.teamscale.test.commons.SystemTestUtils; -import com.teamscale.test.commons.TeamscaleMockServer; -import org.junit.jupiter.api.Test; -import systemundertest.SystemUnderTest; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Runs the system under test and then changes the partition of the agent. This should cause a dump to our - * {@link TeamscaleMockServer}. - */ -public class ApiChangingSettingsShouldDumpSystemTest { - - private static final int METHOD_FOO_COVERABLE_LINE = 7; - - @Test - public void systemTest() throws Exception { - System.setProperty("org.eclipse.jetty.util.log.class", "org.eclipse.jetty.util.log.StdErrLog"); - System.setProperty("org.eclipse.jetty.LEVEL", "OFF"); - - TeamscaleMockServer teamscaleMockServer = new TeamscaleMockServer( - SystemTestUtils.TEAMSCALE_PORT).acceptingReportUploads(); - - new SystemUnderTest().foo(); - SystemTestUtils.changePartition(SystemTestUtils.AGENT_PORT, "some_other_value"); - - Session session = teamscaleMockServer.getOnlySession(); - assertThat(session.getPartition()).isEqualTo("partition_before_change"); - assertThat(session.getOnlyReport(EReportFormat.JACOCO)).contains(coveredLine(METHOD_FOO_COVERABLE_LINE)); - } - - private String coveredLine(int lineNumber) { - return " reports = artifactoryMockServer.uploadedReports; - assertThat(reports).hasSize(1); - // Ensure infos from src/main/resources/git.properties are picked up - assertThat(reports.getFirst(0)).startsWith("uploads/master/1645713803000-86f9d655bf8a204d98bc3542e0d15cea38cc7c74/"); - } -} diff --git a/system-tests/artifactory-git-properties-detection/src/test/java/com/teamscale/client/ArtifactoryMockServer.java b/system-tests/artifactory-git-properties-detection/src/test/java/com/teamscale/client/ArtifactoryMockServer.java deleted file mode 100644 index d315e802c..000000000 --- a/system-tests/artifactory-git-properties-detection/src/test/java/com/teamscale/client/ArtifactoryMockServer.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.teamscale.client; - -import org.conqat.lib.commons.collections.PairList; -import spark.Request; -import spark.Response; -import spark.Service; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.function.BiConsumer; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; - -import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR; - -/** - * Mocks a Artifactory server: stores all uploaded reports so tests can run assertions on them. - */ -public class ArtifactoryMockServer { - - /** All reports uploaded to this Teamscale instance. */ - public final PairList uploadedReports = new PairList<>(); - private final Service service; - - public ArtifactoryMockServer(int port) { - service = Service.ignite(); - service.port(port); - service.put(":path", this::handleReport); - service.exception(Exception.class, (Exception exception, Request request, Response response) -> { - response.status(SC_INTERNAL_SERVER_ERROR); - response.body("Exception: " + exception.getMessage()); - }); - service.notFound((Request request, Response response) -> { - response.status(SC_INTERNAL_SERVER_ERROR); - return "Unexpected request: " + request.requestMethod() + " " + request.uri(); - }); - service.awaitInitialization(); - } - - private String handleReport(Request request, Response response) throws IOException { - readZipInputStream(new ByteArrayInputStream(request.bodyAsBytes()), (entry, content) -> { - if (!entry.isDirectory()) { - uploadedReports.add(request.params("path") + " -> " + entry.getName(), content.toString()); - } - }); - return "success"; - } - - /** - * Shuts down the mock server and waits for it to be stopped. - */ - public void shutdown() { - service.stop(); - service.awaitStop(); - } - - private static void readZipInputStream( - InputStream inputStream, BiConsumer consumerFunction) throws IOException { - try (ZipInputStream zipInput = new ZipInputStream(inputStream)) { - ZipEntry entry; - while ((entry = zipInput.getNextEntry()) != null) { - ByteArrayOutputStream outStream = new ByteArrayOutputStream(); - byte[] buffer = new byte[1024]; - int length; - while ((length = zipInput.read(buffer)) != -1) { - outStream.write(buffer, 0, length); - } - consumerFunction.accept(entry, outStream); - } - } - } -} diff --git a/system-tests/artifactory-git-properties-detection/src/test/kotlin/com/teamscale/client/ArtifactoryGitPropertiesDetectionTest.kt b/system-tests/artifactory-git-properties-detection/src/test/kotlin/com/teamscale/client/ArtifactoryGitPropertiesDetectionTest.kt new file mode 100644 index 000000000..120cb3354 --- /dev/null +++ b/system-tests/artifactory-git-properties-detection/src/test/kotlin/com/teamscale/client/ArtifactoryGitPropertiesDetectionTest.kt @@ -0,0 +1,44 @@ +package com.teamscale.client + +import com.teamscale.test.commons.SystemTestUtils +import com.teamscale.test.commons.SystemTestUtils.dumpCoverage +import org.assertj.core.api.Assertions +import org.assertj.core.api.Assertions.assertThat +import org.conqat.lib.commons.collections.Pair +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import systemundertest.SystemUnderTest + +class ArtifactoryGitPropertiesDetectionTest { + private lateinit var artifactoryMockServer: ArtifactoryMockServer + + @BeforeEach + fun startFakeTeamscaleServer() { + artifactoryMockServer = ArtifactoryMockServer(FAKE_ARTIFACTORY_PORT) + } + + @AfterEach + fun shutdownFakeTeamscaleServer() { + artifactoryMockServer.shutdown() + } + + @Test + @Throws(Exception::class) + fun systemTest() { + SystemUnderTest.foo() + + dumpCoverage(SystemTestUtils.AGENT_PORT) + + assertThat(artifactoryMockServer.uploadedReports).hasSize(1) + val reports = artifactoryMockServer.uploadedReports + assertThat(reports).hasSize(1) + // Ensure infos from src/main/resources/git.properties are picked up + assertThat(reports.getFirst(0)) + .startsWith("uploads/master/1645713803000-86f9d655bf8a204d98bc3542e0d15cea38cc7c74/") + } + + companion object { + private val FAKE_ARTIFACTORY_PORT = Integer.getInteger("artifactoryPort") + } +} diff --git a/system-tests/artifactory-git-properties-detection/src/test/kotlin/com/teamscale/client/ArtifactoryMockServer.kt b/system-tests/artifactory-git-properties-detection/src/test/kotlin/com/teamscale/client/ArtifactoryMockServer.kt new file mode 100644 index 000000000..a91f483da --- /dev/null +++ b/system-tests/artifactory-git-properties-detection/src/test/kotlin/com/teamscale/client/ArtifactoryMockServer.kt @@ -0,0 +1,75 @@ +package com.teamscale.client + +import org.conqat.lib.commons.collections.PairList +import spark.* +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.io.IOException +import java.io.InputStream +import java.util.function.BiConsumer +import java.util.zip.ZipEntry +import java.util.zip.ZipInputStream +import javax.servlet.http.HttpServletResponse + +/** + * Mocks a Artifactory server: stores all uploaded reports so tests can run assertions on them. + */ +class ArtifactoryMockServer(port: Int) { + /** All reports uploaded to this Teamscale instance. */ + val uploadedReports = PairList() + private val service = Service.ignite() + + init { + service.port(port) + service.put(":path", ::handleReport) + service.exception(Exception::class.java) { exception, _, response -> + response.status(HttpServletResponse.SC_INTERNAL_SERVER_ERROR) + response.body("Exception: ${exception.message}") + } + service.notFound { request, response -> + response.status(HttpServletResponse.SC_INTERNAL_SERVER_ERROR) + "Unexpected request: ${request.requestMethod()} ${request.uri()}" + } + service.awaitInitialization() + } + + @Throws(IOException::class) + private fun handleReport(request: Request, response: Response): String { + processZipEntries(ByteArrayInputStream(request.bodyAsBytes())) { entry, content -> + if (!entry.isDirectory) { + uploadedReports.add("${request.params("path")} -> ${entry.getName()}", content.toString()) + } + } + return "success" + } + + /** + * Shuts down the mock server and waits for it to be stopped. + */ + fun shutdown() { + service.stop() + service.awaitStop() + } + + companion object { + @Throws(IOException::class) + private fun processZipEntries( + zipStream: InputStream, + entryConsumer: (ZipEntry, ByteArrayOutputStream) -> Unit + ) { + val bufferSize = 1024 + ZipInputStream(zipStream).use { zipInput -> + while (true) { + val zipEntry = zipInput.nextEntry ?: break + val entryContent = ByteArrayOutputStream() + val buffer = ByteArray(bufferSize) + var bytesRead: Int + while (zipInput.read(buffer).also { bytesRead = it } != -1) { + entryContent.write(buffer, 0, bytesRead) + } + entryConsumer(zipEntry, entryContent) + } + } + } + } +} diff --git a/system-tests/cucumber-maven-tia/build.gradle.kts b/system-tests/cucumber-maven-tia/build.gradle.kts index 10a3c21f0..708306ce3 100644 --- a/system-tests/cucumber-maven-tia/build.gradle.kts +++ b/system-tests/cucumber-maven-tia/build.gradle.kts @@ -1,4 +1,5 @@ plugins { + com.teamscale.`kotlin-convention` com.teamscale.`system-test-convention` com.teamscale.coverage } diff --git a/system-tests/cucumber-maven-tia/src/test/java/com/teamscale/tia/TiaMavenCucumberSystemTest.java b/system-tests/cucumber-maven-tia/src/test/java/com/teamscale/tia/TiaMavenCucumberSystemTest.java deleted file mode 100644 index 805cf5765..000000000 --- a/system-tests/cucumber-maven-tia/src/test/java/com/teamscale/tia/TiaMavenCucumberSystemTest.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.teamscale.tia; - -import com.teamscale.report.testwise.model.ETestExecutionResult; -import com.teamscale.report.testwise.model.TestwiseCoverageReport; -import com.teamscale.test.commons.Session; -import com.teamscale.test.commons.SystemTestUtils; -import com.teamscale.test.commons.TeamscaleMockServer; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Runs several Maven projects' Surefire tests that have the agent attached and one of our JUnit run listeners enabled. - * Checks that this produces a correct coverage report. - */ -public class TiaMavenCucumberSystemTest { - - private static TeamscaleMockServer teamscaleMockServer = null; - - private static final String[] IMPACTED_TEST_PATHS = { // sorted alphabetically - "hellocucumber/RunCucumberTest/hellocucumber/calculator.feature/Actually we just want to test a http:\\/\\/link #1", // also tests addition, escaped / - "hellocucumber/RunCucumberTest/hellocucumber/calculator.feature/Add two numbers #1", - "hellocucumber/RunCucumberTest/hellocucumber/calculator.feature/Add two numbers #2", - "hellocucumber/RunCucumberTest/hellocucumber/calculator.feature/Add two numbers #3", - "hellocucumber/RunCucumberTest/hellocucumber/calculator.feature/Add two numbers #4", - "hellocucumber/RunCucumberTest/hellocucumber/calculator.feature/Subtract two numbers 99 & 99 #1" - }; - - private static final String COVERAGE_ADD = "Calculator.java:3,5;StepDefinitions.java:12,24-25,29-30,39-40"; - private static final String COVERAGE_SUBTRACT = "Calculator.java:3,9;StepDefinitions.java:12,24-25,34-35,39-40"; - - @BeforeEach - public void startFakeTeamscaleServer() throws Exception { - if (teamscaleMockServer == null) { - teamscaleMockServer = new TeamscaleMockServer(SystemTestUtils.TEAMSCALE_PORT) - .acceptingReportUploads() - .withImpactedTests(IMPACTED_TEST_PATHS); - } - teamscaleMockServer.reset(); - } - - @AfterEach - public void stopFakeTeamscaleServer() { - teamscaleMockServer.shutdown(); - } - - @Test - public void testMavenTia() throws Exception { - SystemTestUtils.runMavenTests("maven-project"); - - assertThat(teamscaleMockServer.allAvailableTests).extracting("partition").contains("MyPartition"); - - Session session = teamscaleMockServer.getOnlySession(); - assertThat(session.getReports()).hasSize(1); - assertThat(session.getPartition()).isEqualTo("MyPartition"); - - TestwiseCoverageReport unitTestReport = session.getOnlyTestwiseCoverageReport(); - assertThat(unitTestReport.partial).isTrue(); - assertThat(unitTestReport.tests) - .extracting(test -> test.uniformPath) - .containsExactlyInAnyOrder(IMPACTED_TEST_PATHS); - assertThat(unitTestReport.tests) - .extracting(test -> test.result) - .allMatch(result -> result == ETestExecutionResult.PASSED); - assertThat(unitTestReport.tests) - .extracting(SystemTestUtils::getCoverageString) - .containsExactlyInAnyOrder(COVERAGE_ADD, // must match TEST_PATHS - COVERAGE_ADD, - COVERAGE_ADD, - COVERAGE_ADD, - COVERAGE_ADD, - COVERAGE_SUBTRACT); - } - -} diff --git a/system-tests/cucumber-maven-tia/src/test/kotlin/com/teamscale/tia/TiaMavenCucumberSystemTest.kt b/system-tests/cucumber-maven-tia/src/test/kotlin/com/teamscale/tia/TiaMavenCucumberSystemTest.kt new file mode 100644 index 000000000..2a81ff8f8 --- /dev/null +++ b/system-tests/cucumber-maven-tia/src/test/kotlin/com/teamscale/tia/TiaMavenCucumberSystemTest.kt @@ -0,0 +1,88 @@ +package com.teamscale.tia + +import com.teamscale.client.TestWithClusterId +import com.teamscale.report.testwise.model.ETestExecutionResult +import com.teamscale.report.testwise.model.TestInfo +import com.teamscale.test.commons.ExternalReport +import com.teamscale.test.commons.Session +import com.teamscale.test.commons.SystemTestUtils +import com.teamscale.test.commons.SystemTestUtils.coverage +import com.teamscale.test.commons.SystemTestUtils.runMavenTests +import com.teamscale.test.commons.TeamscaleMockServer +import org.assertj.core.api.Assertions +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.iterable.ThrowingExtractor +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import java.util.function.Predicate + +/** + * Runs several Maven projects' Surefire tests that have the agent attached and one of our JUnit run listeners enabled. + * Checks that this produces a correct coverage report. + */ +class TiaMavenCucumberSystemTest { + private lateinit var teamscaleMockServer: TeamscaleMockServer + + @BeforeEach + @Throws(Exception::class) + fun startFakeTeamscaleServer() { + teamscaleMockServer = TeamscaleMockServer(SystemTestUtils.TEAMSCALE_PORT) + .acceptingReportUploads() + .withImpactedTests(*IMPACTED_TEST_PATHS) + } + + @AfterEach + fun stopFakeTeamscaleServer() { + teamscaleMockServer.shutdown() + } + + @Test + @Throws(Exception::class) + fun testMavenTia() { + runMavenTests("maven-project") + + assertThat(teamscaleMockServer.allAvailableTests) + .extracting("partition") + .contains("MyPartition") + + val session = teamscaleMockServer.onlySession + assertThat(session.getReports()).hasSize(1) + assertThat(session.partition).isEqualTo("MyPartition") + + val unitTestReport = session.onlyTestwiseCoverageReport + with(unitTestReport) { + assertThat(partial).isTrue() + assertThat(tests) + .extracting { it.uniformPath } + .containsExactlyInAnyOrder(*IMPACTED_TEST_PATHS) + assertThat(tests) + .extracting { it.result } + .allMatch { it == ETestExecutionResult.PASSED } + assertThat(tests) + .extracting { it.coverage } + .containsExactlyInAnyOrder( + COVERAGE_ADD, // must match TEST_PATHS + COVERAGE_ADD, + COVERAGE_ADD, + COVERAGE_ADD, + COVERAGE_ADD, + COVERAGE_SUBTRACT + ) + } + } + + companion object { + private val IMPACTED_TEST_PATHS = arrayOf( // sorted alphabetically + "hellocucumber/RunCucumberTest/hellocucumber/calculator.feature/Actually we just want to test a http:\\/\\/link #1", // also tests addition, escaped / + "hellocucumber/RunCucumberTest/hellocucumber/calculator.feature/Add two numbers #1", + "hellocucumber/RunCucumberTest/hellocucumber/calculator.feature/Add two numbers #2", + "hellocucumber/RunCucumberTest/hellocucumber/calculator.feature/Add two numbers #3", + "hellocucumber/RunCucumberTest/hellocucumber/calculator.feature/Add two numbers #4", + "hellocucumber/RunCucumberTest/hellocucumber/calculator.feature/Subtract two numbers 99 & 99 #1" + ) + + private const val COVERAGE_ADD = "Calculator.java:3,5;StepDefinitions.java:12,24-25,29-30,39-40" + private const val COVERAGE_SUBTRACT = "Calculator.java:3,9;StepDefinitions.java:12,24-25,34-35,39-40" + } +} diff --git a/system-tests/debug-logging-default-test/build.gradle.kts b/system-tests/debug-logging-default-test/build.gradle.kts index 9a3bc7176..01088bb95 100644 --- a/system-tests/debug-logging-default-test/build.gradle.kts +++ b/system-tests/debug-logging-default-test/build.gradle.kts @@ -1,4 +1,5 @@ plugins { + com.teamscale.`kotlin-convention` com.teamscale.`system-test-convention` } diff --git a/system-tests/debug-logging-default-test/src/main/java/systemundertest/SystemUnderTest.java b/system-tests/debug-logging-default-test/src/main/java/systemundertest/SystemUnderTest.java deleted file mode 100644 index 57b52ed96..000000000 --- a/system-tests/debug-logging-default-test/src/main/java/systemundertest/SystemUnderTest.java +++ /dev/null @@ -1,10 +0,0 @@ -package systemundertest; - -/** Fake system under test to generate some coverage. */ -public class SystemUnderTest { - - public int foo() { - return 2; - } - -} diff --git a/system-tests/debug-logging-default-test/src/test/java/com/teamscale/client/DebugLoggingDefaultTest.java b/system-tests/debug-logging-default-test/src/test/java/com/teamscale/client/DebugLoggingDefaultTest.java deleted file mode 100644 index f1623d2fd..000000000 --- a/system-tests/debug-logging-default-test/src/test/java/com/teamscale/client/DebugLoggingDefaultTest.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.teamscale.client; - -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Optional; -import java.util.stream.Stream; - -import static org.assertj.core.api.Assertions.assertThat; - -public class DebugLoggingDefaultTest { - - @Test - public void systemTest() throws Exception { - Path logDirectory = searchForAgentTempDirectory().resolve("logs"); - assertThat(logDirectory).exists(); - assertThat(Files.readAllLines(logDirectory.resolve("teamscale-jacoco-agent.log"))).isNotEmpty(); - } - - private Path searchForAgentTempDirectory() throws IOException { - Path tempDirectory = Paths.get(System.getProperty("java.io.tmpdir")); - try (Stream stream = Files.list(tempDirectory)) { - Optional maybeTempDirectory = stream. - filter(path -> path.getFileName().toString().contains("teamscale-java-profiler")) - .sorted().findFirst(); - return maybeTempDirectory.orElseThrow(() -> new AssertionError("Could not locate agent temp directory")); - } - } - -} diff --git a/system-tests/debug-logging-default-test/src/test/kotlin/com/teamscale/client/DebugLoggingDefaultTest.kt b/system-tests/debug-logging-default-test/src/test/kotlin/com/teamscale/client/DebugLoggingDefaultTest.kt new file mode 100644 index 000000000..cddc14308 --- /dev/null +++ b/system-tests/debug-logging-default-test/src/test/kotlin/com/teamscale/client/DebugLoggingDefaultTest.kt @@ -0,0 +1,23 @@ +package com.teamscale.client + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import java.nio.file.Paths +import kotlin.io.path.exists +import kotlin.io.path.readLines + +class DebugLoggingDefaultTest { + + @Test + fun systemTest() { + val logDirectory = searchForAgentTempDirectory().resolve("logs") + assertThat(logDirectory.exists()).isTrue() + assertThat(logDirectory.resolve("teamscale-jacoco-agent.log").readLines()).isNotEmpty() + } + + private fun searchForAgentTempDirectory() = + Paths.get(System.getProperty("java.io.tmpdir")) + .toFile().walk().maxDepth(1) + .firstOrNull { it.name.contains("teamscale-java-profiler") }?.toPath() + ?: throw AssertionError("Could not locate agent temp directory") +} diff --git a/system-tests/debug-logging-test/build.gradle.kts b/system-tests/debug-logging-test/build.gradle.kts index fe7d0a71a..8fa65cc00 100644 --- a/system-tests/debug-logging-test/build.gradle.kts +++ b/system-tests/debug-logging-test/build.gradle.kts @@ -1,4 +1,5 @@ plugins { + com.teamscale.`kotlin-convention` com.teamscale.`system-test-convention` } diff --git a/system-tests/debug-logging-test/src/main/java/jul/test/CustomLogManager.java b/system-tests/debug-logging-test/src/main/kotlin/jul/test/CustomLogManager.kt similarity index 64% rename from system-tests/debug-logging-test/src/main/java/jul/test/CustomLogManager.java rename to system-tests/debug-logging-test/src/main/kotlin/jul/test/CustomLogManager.kt index cdb249fba..0ea60207f 100644 --- a/system-tests/debug-logging-test/src/main/java/jul/test/CustomLogManager.java +++ b/system-tests/debug-logging-test/src/main/kotlin/jul/test/CustomLogManager.kt @@ -1,6 +1,6 @@ -package jul.test; +package jul.test -import java.util.logging.LogManager; +import java.util.logging.LogManager /** * A custom log manager extending the base LogManager class. @@ -10,8 +10,8 @@ * * The manager seems unused, but is referenced in the system property of the runWithoutGradleWorker task. */ -public class CustomLogManager extends LogManager { - public CustomLogManager() { - SystemUnderTest.logManagerInitialized = true; +class CustomLogManager : LogManager() { + init { + SystemUnderTest.logManagerInitialized = true } } diff --git a/system-tests/debug-logging-test/src/main/java/jul/test/SystemUnderTest.java b/system-tests/debug-logging-test/src/main/kotlin/jul/test/SystemUnderTest.kt similarity index 53% rename from system-tests/debug-logging-test/src/main/java/jul/test/SystemUnderTest.java rename to system-tests/debug-logging-test/src/main/kotlin/jul/test/SystemUnderTest.kt index def1e819e..e7724492e 100644 --- a/system-tests/debug-logging-test/src/main/java/jul/test/SystemUnderTest.java +++ b/system-tests/debug-logging-test/src/main/kotlin/jul/test/SystemUnderTest.kt @@ -1,20 +1,20 @@ -package jul.test; +package jul.test /** - * The {@code SystemUnderTest} class is responsible for asserting that the LogManager is not initialized yet. + * The `SystemUnderTest` class is responsible for asserting that the LogManager is not initialized yet. */ -public class SystemUnderTest { +object SystemUnderTest { + var logManagerInitialized: Boolean = false - public static boolean logManagerInitialized = false; - - public static void main(String[] args) { - if (logManagerInitialized) { - throw new RuntimeException("Expected the LogManager to not be initialized by the agent. " + + @JvmStatic + fun main(args: Array) { + check(!logManagerInitialized) { + "Expected the LogManager to not be initialized by the agent. " + "This is required to make the agent work in certain contexts like JBoss Wildfly. " + "It wants to set it's own LogManager, but cannot do so if our agent has already initialized it " + "accidentally e.g. by calling `java.util.logging.Logger.getLogger(...)`. " + "This should be called nowhere, because we transform all class files automatically to use " + - "Logger.global instead which works without initializing the LogManager."); + "Logger.global instead which works without initializing the LogManager." } } } diff --git a/system-tests/debug-logging-test/src/test/java/com/teamscale/client/DebugLoggingTest.java b/system-tests/debug-logging-test/src/test/java/com/teamscale/client/DebugLoggingTest.java deleted file mode 100644 index fa15742ed..000000000 --- a/system-tests/debug-logging-test/src/test/java/com/teamscale/client/DebugLoggingTest.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.teamscale.client; - -import org.junit.jupiter.api.Test; - -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; - -import static org.assertj.core.api.Assertions.assertThat; - -public class DebugLoggingTest { - - private static final Path LOG_DIRECTORY = Paths.get("logTest").resolve("logs"); - - @Test - public void systemTest() throws Exception { - assertThat(Files.exists(LOG_DIRECTORY)).isTrue(); - assertThat(Files.readAllLines(LOG_DIRECTORY.resolve("teamscale-jacoco-agent.log"))).isNotEmpty(); - } -} diff --git a/system-tests/debug-logging-test/src/test/kotlin/com/teamscale/client/DebugLoggingTest.kt b/system-tests/debug-logging-test/src/test/kotlin/com/teamscale/client/DebugLoggingTest.kt new file mode 100644 index 000000000..78b6f4a1d --- /dev/null +++ b/system-tests/debug-logging-test/src/test/kotlin/com/teamscale/client/DebugLoggingTest.kt @@ -0,0 +1,22 @@ +package com.teamscale.client + +import org.junit.jupiter.api.Test +import java.nio.file.Paths + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Assertions.assertTrue +import kotlin.io.path.exists +import kotlin.io.path.readLines + +class DebugLoggingTest { + @Test + @Throws(Exception::class) + fun systemTest() { + assertTrue(LOG_DIRECTORY.exists()) + assertThat(LOG_DIRECTORY.resolve("teamscale-jacoco-agent.log").readLines()).isNotEmpty() + } + + companion object { + private val LOG_DIRECTORY = Paths.get("logTest").resolve("logs") + } +} diff --git a/system-tests/default-excludes-test/build.gradle.kts b/system-tests/default-excludes-test/build.gradle.kts index edea00626..2d2497064 100644 --- a/system-tests/default-excludes-test/build.gradle.kts +++ b/system-tests/default-excludes-test/build.gradle.kts @@ -1,4 +1,5 @@ plugins { + com.teamscale.`kotlin-convention` com.teamscale.`system-test-convention` } diff --git a/system-tests/default-excludes-test/src/main/java/notexcluded/sun/NotExcludedClass.java b/system-tests/default-excludes-test/src/main/java/notexcluded/sun/NotExcludedClass.java deleted file mode 100644 index b34d182ed..000000000 --- a/system-tests/default-excludes-test/src/main/java/notexcluded/sun/NotExcludedClass.java +++ /dev/null @@ -1,8 +0,0 @@ -package notexcluded.sun; - -public class NotExcludedClass { - - public int y() { - return 2; - } -} diff --git a/system-tests/default-excludes-test/src/main/java/shadow/ShadowedClass.java b/system-tests/default-excludes-test/src/main/java/shadow/ShadowedClass.java deleted file mode 100644 index b1e0d25cd..000000000 --- a/system-tests/default-excludes-test/src/main/java/shadow/ShadowedClass.java +++ /dev/null @@ -1,7 +0,0 @@ -package shadow; - -public class ShadowedClass { - public int x() { - return 1; - } -} diff --git a/system-tests/default-excludes-test/src/main/java/systemundertest/SystemUnderTest.java b/system-tests/default-excludes-test/src/main/java/systemundertest/SystemUnderTest.java deleted file mode 100644 index 9ebe079c6..000000000 --- a/system-tests/default-excludes-test/src/main/java/systemundertest/SystemUnderTest.java +++ /dev/null @@ -1,13 +0,0 @@ -package systemundertest; - -import notexcluded.sun.NotExcludedClass; -import shadow.ShadowedClass; - -/** Fake system under test to generate some coverage. */ -public class SystemUnderTest { - - public int foo() { - return new ShadowedClass().x() + new NotExcludedClass().y(); - } - -} diff --git a/system-tests/default-excludes-test/src/main/kotlin/notexcluded/sun/NotExcludedClass.kt b/system-tests/default-excludes-test/src/main/kotlin/notexcluded/sun/NotExcludedClass.kt new file mode 100644 index 000000000..9102226b4 --- /dev/null +++ b/system-tests/default-excludes-test/src/main/kotlin/notexcluded/sun/NotExcludedClass.kt @@ -0,0 +1,5 @@ +package notexcluded.sun + +class NotExcludedClass { + fun y() = 2 +} diff --git a/system-tests/default-excludes-test/src/main/kotlin/shadow/ShadowedClass.kt b/system-tests/default-excludes-test/src/main/kotlin/shadow/ShadowedClass.kt new file mode 100644 index 000000000..f186f133c --- /dev/null +++ b/system-tests/default-excludes-test/src/main/kotlin/shadow/ShadowedClass.kt @@ -0,0 +1,5 @@ +package shadow + +class ShadowedClass { + fun x() = 1 +} diff --git a/system-tests/default-excludes-test/src/main/kotlin/systemundertest/SystemUnderTest.kt b/system-tests/default-excludes-test/src/main/kotlin/systemundertest/SystemUnderTest.kt new file mode 100644 index 000000000..f2aa1dad6 --- /dev/null +++ b/system-tests/default-excludes-test/src/main/kotlin/systemundertest/SystemUnderTest.kt @@ -0,0 +1,9 @@ +package systemundertest + +import notexcluded.sun.NotExcludedClass +import shadow.ShadowedClass + +/** Fake system under test to generate some coverage. */ +class SystemUnderTest { + fun foo() = ShadowedClass().x() + NotExcludedClass().y() +} diff --git a/system-tests/default-excludes-test/src/test/java/com/teamscale/tia/client/DefaultExcludesSystemTest.java b/system-tests/default-excludes-test/src/test/java/com/teamscale/tia/client/DefaultExcludesSystemTest.java deleted file mode 100644 index c5456a231..000000000 --- a/system-tests/default-excludes-test/src/test/java/com/teamscale/tia/client/DefaultExcludesSystemTest.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.teamscale.tia.client; - -import com.teamscale.client.EReportFormat; -import com.teamscale.test.commons.SystemTestUtils; -import com.teamscale.test.commons.TeamscaleMockServer; -import org.junit.jupiter.api.Test; -import systemundertest.SystemUnderTest; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Runs the system under test and then forces a dump of the agent to our {@link TeamscaleMockServer}. Checks the - * resulting report to ensure the default excludes are applied. - */ -public class DefaultExcludesSystemTest { - - @Test - public void systemTest() throws Exception { - System.setProperty("org.eclipse.jetty.util.log.class", "org.eclipse.jetty.util.log.StdErrLog"); - System.setProperty("org.eclipse.jetty.LEVEL", "OFF"); - - TeamscaleMockServer teamscaleMockServer = new TeamscaleMockServer(SystemTestUtils.TEAMSCALE_PORT) - .withAuthentication("fake", "fake") - .acceptingReportUploads(); - - new SystemUnderTest().foo(); - SystemTestUtils.dumpCoverage(SystemTestUtils.AGENT_PORT); - - String report = teamscaleMockServer.getOnlyReport("part", EReportFormat.JACOCO); - assertThat(report).doesNotContain("shadow"); - assertThat(report).doesNotContain("junit"); - assertThat(report).doesNotContain("eclipse"); - assertThat(report).doesNotContain("apache"); - assertThat(report).doesNotContain("javax"); - assertThat(report).doesNotContain("slf4j"); - assertThat(report).doesNotContain("com/sun"); - assertThat(report).contains("SystemUnderTest"); - assertThat(report).contains("NotExcludedClass"); - } - -} diff --git a/system-tests/default-excludes-test/src/test/kotlin/com/teamscale/tia/client/DefaultExcludesSystemTest.kt b/system-tests/default-excludes-test/src/test/kotlin/com/teamscale/tia/client/DefaultExcludesSystemTest.kt new file mode 100644 index 000000000..a48387fb3 --- /dev/null +++ b/system-tests/default-excludes-test/src/test/kotlin/com/teamscale/tia/client/DefaultExcludesSystemTest.kt @@ -0,0 +1,33 @@ +package com.teamscale.tia.client + +import com.teamscale.client.EReportFormat +import com.teamscale.test.commons.SystemTestUtils +import com.teamscale.test.commons.SystemTestUtils.dumpCoverage +import com.teamscale.test.commons.TeamscaleMockServer +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import systemundertest.SystemUnderTest + +/** + * Runs the system under test and then forces a dump of the agent to our [TeamscaleMockServer]. Checks the + * resulting report to ensure the default excludes are applied. + */ +class DefaultExcludesSystemTest { + @Test + @Throws(Exception::class) + fun systemTest() { + System.setProperty("org.eclipse.jetty.util.log.class", "org.eclipse.jetty.util.log.StdErrLog") + System.setProperty("org.eclipse.jetty.LEVEL", "OFF") + + val teamscaleMockServer = TeamscaleMockServer(SystemTestUtils.TEAMSCALE_PORT) + .withAuthentication("fake", "fake") + .acceptingReportUploads() + + SystemUnderTest().foo() + dumpCoverage(SystemTestUtils.AGENT_PORT) + + val report = teamscaleMockServer.getOnlyReport("part", EReportFormat.JACOCO) + assertThat(report).doesNotContain("shadow", "junit", "eclipse", "apache", "javax", "slf4j", "com/sun") + assertThat(report).contains("SystemUnderTest", "NotExcludedClass") + } +} diff --git a/system-tests/double-start-agent/build.gradle.kts b/system-tests/double-start-agent/build.gradle.kts index 47a3d2f90..c2beff4c4 100644 --- a/system-tests/double-start-agent/build.gradle.kts +++ b/system-tests/double-start-agent/build.gradle.kts @@ -1,4 +1,5 @@ plugins { + com.teamscale.`kotlin-convention` com.teamscale.`system-test-convention` } diff --git a/system-tests/double-start-agent/src/main/java/systemundertest/SystemUnderTest.java b/system-tests/double-start-agent/src/main/java/systemundertest/SystemUnderTest.java deleted file mode 100644 index aa4cf27f2..000000000 --- a/system-tests/double-start-agent/src/main/java/systemundertest/SystemUnderTest.java +++ /dev/null @@ -1,8 +0,0 @@ -package systemundertest; - -public class SystemUnderTest { - - public int foo() { - return 2; - } -} diff --git a/system-tests/double-start-agent/src/main/kotlin/systemundertest/SystemUnderTest.kt b/system-tests/double-start-agent/src/main/kotlin/systemundertest/SystemUnderTest.kt new file mode 100644 index 000000000..8bb274ff9 --- /dev/null +++ b/system-tests/double-start-agent/src/main/kotlin/systemundertest/SystemUnderTest.kt @@ -0,0 +1,5 @@ +package systemundertest + +class SystemUnderTest { + fun foo() = 2 +} diff --git a/system-tests/double-start-agent/src/test/java/com/teamscale/client/DoubleStartAgentTest.java b/system-tests/double-start-agent/src/test/java/com/teamscale/client/DoubleStartAgentTest.java deleted file mode 100644 index 7df3dbd38..000000000 --- a/system-tests/double-start-agent/src/test/java/com/teamscale/client/DoubleStartAgentTest.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.teamscale.client; - -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.List; -import java.util.stream.Stream; - -import static org.assertj.core.api.Assertions.assertThat; - -public class DoubleStartAgentTest { - - private static final Path LOG_DIRECTORY = Paths.get("logTest").resolve("logs"); - - @Test - public void systemTest() throws IOException { - assertThat(LOG_DIRECTORY).exists(); - List lines = Files.readAllLines(LOG_DIRECTORY.resolve("teamscale-jacoco-agent.log")); - Stream agentStartLines = lines.stream().filter(line -> line.contains("Starting JaCoCo agent")); - assertThat(agentStartLines).hasSize(1); - } -} diff --git a/system-tests/double-start-agent/src/test/kotlin/com/teamscale/client/DoubleStartAgentTest.kt b/system-tests/double-start-agent/src/test/kotlin/com/teamscale/client/DoubleStartAgentTest.kt new file mode 100644 index 000000000..6facc32b4 --- /dev/null +++ b/system-tests/double-start-agent/src/test/kotlin/com/teamscale/client/DoubleStartAgentTest.kt @@ -0,0 +1,22 @@ +package com.teamscale.client + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import java.io.IOException +import java.nio.file.Paths +import kotlin.io.path.readLines + +class DoubleStartAgentTest { + @Test + @Throws(IOException::class) + fun systemTest() { + assertThat(LOG_DIRECTORY).exists() + val lines = LOG_DIRECTORY.resolve("teamscale-jacoco-agent.log").readLines() + val agentStartLines = lines.filter { it.contains("Starting JaCoCo agent") } + assertThat(agentStartLines).hasSize(1) + } + + companion object { + private val LOG_DIRECTORY = Paths.get("logTest").resolve("logs") + } +} diff --git a/system-tests/gradle-cucumber/build.gradle.kts b/system-tests/gradle-cucumber/build.gradle.kts index 394ee070b..3b8c4c0a4 100644 --- a/system-tests/gradle-cucumber/build.gradle.kts +++ b/system-tests/gradle-cucumber/build.gradle.kts @@ -1,4 +1,5 @@ plugins { + com.teamscale.`kotlin-convention` com.teamscale.`system-test-convention` com.teamscale.coverage } diff --git a/system-tests/gradle-cucumber/src/test/java/com/teamscale/tia/CucumberGradleTiaSystemTest.java b/system-tests/gradle-cucumber/src/test/java/com/teamscale/tia/CucumberGradleTiaSystemTest.java deleted file mode 100644 index 5ca6a7d67..000000000 --- a/system-tests/gradle-cucumber/src/test/java/com/teamscale/tia/CucumberGradleTiaSystemTest.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.teamscale.tia; - -import com.teamscale.report.testwise.model.TestwiseCoverageReport; -import com.teamscale.test.commons.SystemTestUtils; -import com.teamscale.test.commons.TeamscaleMockServer; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - - -/** - * Runs cucumber tests in the nested gradle-project folder - */ -public class CucumberGradleTiaSystemTest { - - private TeamscaleMockServer teamscaleMockServer = null; - - @BeforeEach - public void startFakeTeamscaleServer() throws Exception { - teamscaleMockServer = new TeamscaleMockServer(SystemTestUtils.TEAMSCALE_PORT) - .withImpactedTests("hellocucumber/RunCucumberTest/hellocucumber/calculator.feature/Add two numbers -1 & -10 #1") - .acceptingReportUploads(); - } - - @AfterEach - public void stopFakeTeamscaleServer() { - teamscaleMockServer.shutdown(); - } - - @Test - public void testImpactedCucumberTestsAreRun() throws Exception { - SystemTestUtils.runGradle("gradle-project", "clean", "tiaTests", "teamscaleReportUpload", "--no-daemon"); - - TestwiseCoverageReport testReport = teamscaleMockServer.getOnlyTestwiseCoverageReport("Cucumber Tests"); - // We can't just assert for testReport.test == 1 here because the Gradle plugin uploads all test cases. - // The ones that were not executed have a mostly empty body, though. E.g. - // { - // "uniformPath" : "hellocucumber/RunCucumberTest/hellocucumber/calculator.feature/Add two numbers 10 & 15 #1", - // "paths" : [ ] - // } - assertThat(testReport.tests.stream().filter(test -> test.result != null).count()).isEqualTo(1); - } - -} diff --git a/system-tests/gradle-cucumber/src/test/kotlin/com/teamscale/tia/CucumberGradleTiaSystemTest.kt b/system-tests/gradle-cucumber/src/test/kotlin/com/teamscale/tia/CucumberGradleTiaSystemTest.kt new file mode 100644 index 000000000..a984ccbd9 --- /dev/null +++ b/system-tests/gradle-cucumber/src/test/kotlin/com/teamscale/tia/CucumberGradleTiaSystemTest.kt @@ -0,0 +1,45 @@ +package com.teamscale.tia + +import com.teamscale.test.commons.SystemTestUtils +import com.teamscale.test.commons.SystemTestUtils.runGradle +import com.teamscale.test.commons.TeamscaleMockServer +import org.assertj.core.api.Assertions +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +/** + * Runs cucumber tests in the nested `gradle-project` folder + */ +class CucumberGradleTiaSystemTest { + private lateinit var teamscaleMockServer: TeamscaleMockServer + + @BeforeEach + @Throws(Exception::class) + fun startFakeTeamscaleServer() { + teamscaleMockServer = TeamscaleMockServer(SystemTestUtils.TEAMSCALE_PORT) + .withImpactedTests("hellocucumber/RunCucumberTest/hellocucumber/calculator.feature/Add two numbers -1 & -10 #1") + .acceptingReportUploads() + } + + @AfterEach + fun stopFakeTeamscaleServer() { + teamscaleMockServer.shutdown() + } + + @Test + @Throws(Exception::class) + fun testImpactedCucumberTestsAreRun() { + runGradle("gradle-project", "clean", "tiaTests", "teamscaleReportUpload", "--no-daemon") + + val testReport = teamscaleMockServer.getOnlyTestwiseCoverageReport("Cucumber Tests") + // We can't just assert for testReport.test == 1 here because the Gradle plugin uploads all test cases. + // The ones that were not executed have a mostly empty body, though. E.g. + // { + // "uniformPath" : "hellocucumber/RunCucumberTest/hellocucumber/calculator.feature/Add two numbers 10 & 15 #1", + // "paths" : [ ] + // } + Assertions.assertThat(testReport.tests.count { it.result != null }) + .isEqualTo(1) + } +} diff --git a/system-tests/gradle-multi-module/build.gradle.kts b/system-tests/gradle-multi-module/build.gradle.kts index 394ee070b..3b8c4c0a4 100644 --- a/system-tests/gradle-multi-module/build.gradle.kts +++ b/system-tests/gradle-multi-module/build.gradle.kts @@ -1,4 +1,5 @@ plugins { + com.teamscale.`kotlin-convention` com.teamscale.`system-test-convention` com.teamscale.coverage } diff --git a/system-tests/gradle-multi-module/src/test/java/com/teamscale/tia/ConfigurationCacheTest.java b/system-tests/gradle-multi-module/src/test/java/com/teamscale/tia/ConfigurationCacheTest.java deleted file mode 100644 index 3f86a6ac1..000000000 --- a/system-tests/gradle-multi-module/src/test/java/com/teamscale/tia/ConfigurationCacheTest.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.teamscale.tia; - -import com.teamscale.test.commons.SystemTestUtils; -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.Test; - -/** - * Checks that the plugin is compatible with the Gradle configuration cache. - */ -public class ConfigurationCacheTest { - - /** Configuration cache is enabled via gradle.properties */ - @Test - public void testConfigurationCache() throws Exception { - var result = SystemTestUtils.runGradle("gradle-project", "clean", "systemTest"); - Assertions.assertThat(result.getStdout()).contains("BUILD SUCCESSFUL"); - result = SystemTestUtils.runGradle("gradle-project", "clean", "systemTest"); - Assertions.assertThat(result.getStdout()).contains("Reusing configuration cache"); - } -} diff --git a/system-tests/gradle-multi-module/src/test/java/com/teamscale/tia/TestwiseCoverageGradleSystemTest.java b/system-tests/gradle-multi-module/src/test/java/com/teamscale/tia/TestwiseCoverageGradleSystemTest.java deleted file mode 100644 index c4cae7761..000000000 --- a/system-tests/gradle-multi-module/src/test/java/com/teamscale/tia/TestwiseCoverageGradleSystemTest.java +++ /dev/null @@ -1,89 +0,0 @@ -package com.teamscale.tia; - -import com.teamscale.client.EReportFormat; -import com.teamscale.report.compact.TeamscaleCompactCoverageReport; -import com.teamscale.report.testwise.model.TestwiseCoverageReport; -import com.teamscale.test.commons.Session; -import com.teamscale.test.commons.SystemTestUtils; -import com.teamscale.test.commons.TeamscaleMockServer; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Runs tests in all submodules and expects the results of both in the upload. - */ -public class TestwiseCoverageGradleSystemTest { - - private TeamscaleMockServer teamscaleMockServer = null; - - @BeforeEach - public void startFakeTeamscaleServer() throws Exception { - teamscaleMockServer = new TeamscaleMockServer(SystemTestUtils.TEAMSCALE_PORT) - .acceptingReportUploads().withImpactedTests("com/example/app/MainTest/testMain()"); - } - - @AfterEach - public void stopFakeTeamscaleServer() { - teamscaleMockServer.shutdown(); - } - - @Test - public void testGradleAggregatedTestwiseCoverageUploadWithoutJVMTestSuite() throws Exception { - SystemTestUtils.runGradle("gradle-project", "clean", "teamscaleSystemTestReportUpload"); - - TestwiseCoverageReport testwiseReport = teamscaleMockServer.getOnlyTestwiseCoverageReport("System Tests"); - assertThat(testwiseReport.partial).isEqualTo(false); - assertThat(testwiseReport.tests.getFirst().uniformPath).isEqualTo("com/example/app/MainTest/testMain()"); - assertThat(testwiseReport.tests.getLast().uniformPath).isEqualTo("com/example/lib/CalculatorTest/testAdd()"); - assertThat(testwiseReport.tests.getFirst().paths).isNotEmpty(); - assertThat(testwiseReport.tests.getLast().paths).isNotEmpty(); - } - - @Test - public void testGradleAggregatedTestwiseCoverageUploadHasPartialFlagSet() throws Exception { - SystemTestUtils.runGradle("gradle-project", "clean", - "systemTest", "-Dimpacted", - "teamscaleSystemTestReportUpload"); - - TestwiseCoverageReport testwiseReport = teamscaleMockServer.getOnlyTestwiseCoverageReport("System Tests"); - assertThat(testwiseReport.partial).isEqualTo(true); - assertThat(testwiseReport.tests.getFirst().uniformPath).isEqualTo("com/example/app/MainTest/testMain()"); - assertThat(testwiseReport.tests.getLast().uniformPath).isEqualTo("com/example/lib/CalculatorTest/testAdd()"); - assertThat(testwiseReport.tests.getFirst().paths).isNotEmpty(); - assertThat(testwiseReport.tests.getLast().paths).isEmpty(); - } - - @Test - public void testGradleAggregatedCompactCoverageUploadWithoutJVMTestSuite() throws Exception { - SystemTestUtils.runGradle("gradle-project", "clean", "unitTest", "teamscaleUnitTestReportUpload"); - - Session session = teamscaleMockServer.getOnlySession("Unit Tests"); - assertThat(session.getReports()).hasSize(3); - assertThat(session.getReports(EReportFormat.JUNIT)).hasSize(2); - - TeamscaleCompactCoverageReport compactReport = session.getCompactCoverageReport(0); - assertThat(compactReport.getCoverage().getFirst().getFilePath()).isEqualTo("com/example/app/Main.java"); - assertThat(compactReport.getCoverage().getLast().getFilePath()).isEqualTo("com/example/lib/Calculator.java"); - assertThat(compactReport.getCoverage().getFirst().getFullyCoveredLines()).containsExactly(7, 8, 9); - assertThat(compactReport.getCoverage().getLast().getFullyCoveredLines()).containsExactly(3, 6, 16); - } - - @Test - public void testGradleAggregatedCompactCoverageUploadWithJVMTestSuite() throws Exception { - SystemTestUtils.runGradle("gradle-project", "clean", "teamscaleTestReportUpload"); - - Session session = teamscaleMockServer.getOnlySession("Default Tests"); - assertThat(session.getReports()).hasSize(3); - assertThat(session.getReports(EReportFormat.JUNIT)).hasSize(2); - - TeamscaleCompactCoverageReport compactReport = session.getCompactCoverageReport(0); - assertThat(compactReport.getCoverage().getFirst().getFilePath()).isEqualTo("com/example/app/Main.java"); - assertThat(compactReport.getCoverage().getLast().getFilePath()).isEqualTo("com/example/lib/Calculator.java"); - assertThat(compactReport.getCoverage().getFirst().getFullyCoveredLines()).containsExactly(7, 8, 9); - assertThat(compactReport.getCoverage().getLast().getFullyCoveredLines()).containsExactly(3, 6, 16); - } - -} diff --git a/system-tests/gradle-multi-module/src/test/kotlin/com/teamscale/tia/ConfigurationCacheTest.kt b/system-tests/gradle-multi-module/src/test/kotlin/com/teamscale/tia/ConfigurationCacheTest.kt new file mode 100644 index 000000000..ea47a6bac --- /dev/null +++ b/system-tests/gradle-multi-module/src/test/kotlin/com/teamscale/tia/ConfigurationCacheTest.kt @@ -0,0 +1,21 @@ +package com.teamscale.tia + +import com.teamscale.test.commons.SystemTestUtils.runGradle +import org.assertj.core.api.Assertions +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +/** + * Checks that the plugin is compatible with the [Gradle configuration cache](https://docs.gradle.org/current/userguide/configuration_cache.html). + */ +class ConfigurationCacheTest { + /** Configuration cache is enabled via gradle.properties */ + @Test + @Throws(Exception::class) + fun testConfigurationCache() { + var result = runGradle("gradle-project", "clean", "systemTest") + assertThat(result.stdout).contains("BUILD SUCCESSFUL") + result = runGradle("gradle-project", "clean", "systemTest") + assertThat(result.stdout).contains("Reusing configuration cache") + } +} diff --git a/system-tests/gradle-multi-module/src/test/kotlin/com/teamscale/tia/TestwiseCoverageGradleSystemTest.kt b/system-tests/gradle-multi-module/src/test/kotlin/com/teamscale/tia/TestwiseCoverageGradleSystemTest.kt new file mode 100644 index 000000000..bf4ab32ad --- /dev/null +++ b/system-tests/gradle-multi-module/src/test/kotlin/com/teamscale/tia/TestwiseCoverageGradleSystemTest.kt @@ -0,0 +1,98 @@ +package com.teamscale.tia + +import com.teamscale.client.EReportFormat +import com.teamscale.test.commons.SystemTestUtils +import com.teamscale.test.commons.SystemTestUtils.runGradle +import com.teamscale.test.commons.TeamscaleMockServer +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +/** + * Runs tests in all submodules and expects the results of both in the upload. + */ +class TestwiseCoverageGradleSystemTest { + private lateinit var teamscaleMockServer: TeamscaleMockServer + + @BeforeEach + @Throws(Exception::class) + fun startFakeTeamscaleServer() { + teamscaleMockServer = TeamscaleMockServer(SystemTestUtils.TEAMSCALE_PORT) + .acceptingReportUploads().withImpactedTests("com/example/app/MainTest/testMain()") + } + + @AfterEach + fun stopFakeTeamscaleServer() { + teamscaleMockServer.shutdown() + } + + @Test + @Throws(Exception::class) + fun testGradleAggregatedTestwiseCoverageUploadWithoutJVMTestSuite() { + runGradle("gradle-project", "clean", "teamscaleSystemTestReportUpload") + + val testwiseReport = teamscaleMockServer.getOnlyTestwiseCoverageReport("System Tests") + assertThat(testwiseReport.partial).isEqualTo(false) + assertThat(testwiseReport.tests.first().uniformPath) + .isEqualTo("com/example/app/MainTest/testMain()") + assertThat(testwiseReport.tests.last().uniformPath) + .isEqualTo("com/example/lib/CalculatorTest/testAdd()") + assertThat(testwiseReport.tests.first().paths).isNotEmpty() + assertThat(testwiseReport.tests.last().paths).isNotEmpty() + } + + @Test + @Throws(Exception::class) + fun testGradleAggregatedTestwiseCoverageUploadHasPartialFlagSet() { + runGradle( + "gradle-project", "clean", + "systemTest", "-Dimpacted", + "teamscaleSystemTestReportUpload" + ) + + val testwiseReport = teamscaleMockServer.getOnlyTestwiseCoverageReport("System Tests") + assertThat(testwiseReport.partial).isEqualTo(true) + assertThat(testwiseReport.tests.first().uniformPath) + .isEqualTo("com/example/app/MainTest/testMain()") + assertThat(testwiseReport.tests.last().uniformPath) + .isEqualTo("com/example/lib/CalculatorTest/testAdd()") + assertThat(testwiseReport.tests.first().paths).isNotEmpty() + assertThat(testwiseReport.tests.last().paths).isEmpty() + } + + @Test + @Throws(Exception::class) + fun testGradleAggregatedCompactCoverageUploadWithoutJVMTestSuite() { + runGradle("gradle-project", "clean", "unitTest", "teamscaleUnitTestReportUpload") + + val session = teamscaleMockServer.getOnlySession("Unit Tests") + assertThat(session.getReports()).hasSize(3) + assertThat(session.getReports(EReportFormat.JUNIT)).hasSize(2) + + val compactReport = session.getCompactCoverageReport(0)!! + + with(compactReport.coverage) { + assertThat(first().filePath).isEqualTo("com/example/app/Main.java") + assertThat(last().filePath).isEqualTo("com/example/lib/Calculator.java") + assertThat(first().fullyCoveredLines).containsExactly(7, 8, 9) + assertThat(last().fullyCoveredLines).containsExactly(3, 6, 16) + } + } + + @Test + @Throws(Exception::class) + fun testGradleAggregatedCompactCoverageUploadWithJVMTestSuite() { + runGradle("gradle-project", "clean", "teamscaleTestReportUpload") + + val session = teamscaleMockServer.getOnlySession("Default Tests") + assertThat(session.getReports()).hasSize(3) + assertThat(session.getReports(EReportFormat.JUNIT)).hasSize(2) + + val compactReport = session.getCompactCoverageReport(0)!! + assertThat(compactReport.coverage.first().filePath).isEqualTo("com/example/app/Main.java") + assertThat(compactReport.coverage.last().filePath).isEqualTo("com/example/lib/Calculator.java") + assertThat(compactReport.coverage.first().fullyCoveredLines).containsExactly(7, 8, 9) + assertThat(compactReport.coverage.last().fullyCoveredLines).containsExactly(3, 6, 16) + } +} diff --git a/system-tests/http-redirect-test/build.gradle.kts b/system-tests/http-redirect-test/build.gradle.kts index 8124425c3..395dcb1a0 100644 --- a/system-tests/http-redirect-test/build.gradle.kts +++ b/system-tests/http-redirect-test/build.gradle.kts @@ -1,4 +1,5 @@ plugins { + com.teamscale.`kotlin-convention` com.teamscale.`system-test-convention` } diff --git a/system-tests/http-redirect-test/src/main/java/systemundertest/SystemUnderTest.java b/system-tests/http-redirect-test/src/main/java/systemundertest/SystemUnderTest.java deleted file mode 100644 index 57b52ed96..000000000 --- a/system-tests/http-redirect-test/src/main/java/systemundertest/SystemUnderTest.java +++ /dev/null @@ -1,10 +0,0 @@ -package systemundertest; - -/** Fake system under test to generate some coverage. */ -public class SystemUnderTest { - - public int foo() { - return 2; - } - -} diff --git a/system-tests/http-redirect-test/src/main/kotlin/systemundertest/SystemUnderTest.kt b/system-tests/http-redirect-test/src/main/kotlin/systemundertest/SystemUnderTest.kt new file mode 100644 index 000000000..b7d25de8f --- /dev/null +++ b/system-tests/http-redirect-test/src/main/kotlin/systemundertest/SystemUnderTest.kt @@ -0,0 +1,6 @@ +package systemundertest + +/** Fake system under test to generate some coverage. */ +class SystemUnderTest { + fun foo() = 2 +} diff --git a/system-tests/http-redirect-test/src/test/java/com/teamscale/client/HttpRedirectSystemTest.java b/system-tests/http-redirect-test/src/test/java/com/teamscale/client/HttpRedirectSystemTest.java deleted file mode 100644 index 2567fe47d..000000000 --- a/system-tests/http-redirect-test/src/test/java/com/teamscale/client/HttpRedirectSystemTest.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.teamscale.client; - -import com.teamscale.test.commons.SystemTestUtils; -import com.teamscale.test.commons.TeamscaleMockServer; -import org.junit.jupiter.api.Test; -import systemundertest.SystemUnderTest; - -import java.util.Set; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Runs the system under test and then forces a dump of the agent to a {@link RedirectMockServer}. This sends redirects - * to our {@link TeamscaleMockServer}. Checks that the agent respects and follows these redirects. - */ -public class HttpRedirectSystemTest { - - private static final int REDIRECT_PORT = Integer.getInteger("redirectPort"); - - @Test - public void systemTest() throws Exception { - System.setProperty("org.eclipse.jetty.util.log.class", "org.eclipse.jetty.util.log.StdErrLog"); - System.setProperty("org.eclipse.jetty.LEVEL", "OFF"); - - RedirectMockServer redirectMockServer = new RedirectMockServer(REDIRECT_PORT, SystemTestUtils.TEAMSCALE_PORT); - TeamscaleMockServer teamscaleMockServer = new TeamscaleMockServer(SystemTestUtils.TEAMSCALE_PORT).acceptingReportUploads(); - - new SystemUnderTest().foo(); - SystemTestUtils.dumpCoverage(SystemTestUtils.AGENT_PORT); - - assertThat(teamscaleMockServer.getOnlySession("part").getReports()).hasSize(1); - checkCustomUserAgent(teamscaleMockServer); - - redirectMockServer.shutdown(); - teamscaleMockServer.shutdown(); - } - - private void checkCustomUserAgent(TeamscaleMockServer teamscaleMockServer) { - Set collectedUserAgents = teamscaleMockServer.collectedUserAgents; - assertThat(collectedUserAgents).containsExactly(TeamscaleServiceGenerator.USER_AGENT); - } - -} diff --git a/system-tests/http-redirect-test/src/test/java/com/teamscale/client/RedirectMockServer.java b/system-tests/http-redirect-test/src/test/java/com/teamscale/client/RedirectMockServer.java deleted file mode 100644 index 2592d4237..000000000 --- a/system-tests/http-redirect-test/src/test/java/com/teamscale/client/RedirectMockServer.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.teamscale.client; - -import spark.Request; -import spark.Response; -import spark.Service; - -import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR; - -/** - * Mocks a redirect server: redirects all requests to another server. - */ -public class RedirectMockServer { - - private final Service service; - - public RedirectMockServer(int port, int redirectTo) { - service = Service.ignite(); - service.port(port); - service.post("/*", (Request request, Response response) -> { - String url = request.url() + "?" + request.queryString(); - url = url.replace(Integer.toString(port), Integer.toString(redirectTo)); - response.redirect(url, 307); - return response; - }); - service.exception(Exception.class, (Exception exception, Request request, Response response) -> { - response.status(SC_INTERNAL_SERVER_ERROR); - response.body("Exception: " + exception.getMessage()); - }); - service.awaitInitialization(); - } - - - /** - * Shuts down the mock server and waits for it to be stopped. - */ - public void shutdown() { - service.stop(); - service.awaitStop(); - } - -} diff --git a/system-tests/http-redirect-test/src/test/kotlin/com/teamscale/client/HttpRedirectSystemTest.kt b/system-tests/http-redirect-test/src/test/kotlin/com/teamscale/client/HttpRedirectSystemTest.kt new file mode 100644 index 000000000..82ddbfa7d --- /dev/null +++ b/system-tests/http-redirect-test/src/test/kotlin/com/teamscale/client/HttpRedirectSystemTest.kt @@ -0,0 +1,42 @@ +package com.teamscale.client + +import com.teamscale.test.commons.SystemTestUtils +import com.teamscale.test.commons.SystemTestUtils.dumpCoverage +import com.teamscale.test.commons.TeamscaleMockServer +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import systemundertest.SystemUnderTest + +/** + * Runs the system under test and then forces a dump of the agent to a [RedirectMockServer]. This sends redirects + * to our [TeamscaleMockServer]. Checks that the agent respects and follows these redirects. + */ +class HttpRedirectSystemTest { + @Test + @Throws(Exception::class) + fun systemTest() { + System.setProperty("org.eclipse.jetty.util.log.class", "org.eclipse.jetty.util.log.StdErrLog") + System.setProperty("org.eclipse.jetty.LEVEL", "OFF") + + val redirectMockServer = RedirectMockServer(REDIRECT_PORT, SystemTestUtils.TEAMSCALE_PORT) + val teamscaleMockServer = TeamscaleMockServer(SystemTestUtils.TEAMSCALE_PORT).acceptingReportUploads() + + SystemUnderTest().foo() + dumpCoverage(SystemTestUtils.AGENT_PORT) + + assertThat(teamscaleMockServer.getOnlySession("part").getReports()).hasSize(1) + checkCustomUserAgent(teamscaleMockServer) + + redirectMockServer.shutdown() + teamscaleMockServer.shutdown() + } + + private fun checkCustomUserAgent(teamscaleMockServer: TeamscaleMockServer) { + val collectedUserAgents = teamscaleMockServer.collectedUserAgents + assertThat(collectedUserAgents).containsExactly(TeamscaleServiceGenerator.USER_AGENT) + } + + companion object { + private val REDIRECT_PORT = Integer.getInteger("redirectPort") + } +} diff --git a/system-tests/http-redirect-test/src/test/kotlin/com/teamscale/client/RedirectMockServer.kt b/system-tests/http-redirect-test/src/test/kotlin/com/teamscale/client/RedirectMockServer.kt new file mode 100644 index 000000000..cd0bba34f --- /dev/null +++ b/system-tests/http-redirect-test/src/test/kotlin/com/teamscale/client/RedirectMockServer.kt @@ -0,0 +1,34 @@ +package com.teamscale.client + +import spark.* +import javax.servlet.http.HttpServletResponse + +/** + * Mocks a redirect server: redirects all requests to another server. + */ +class RedirectMockServer(port: Int, redirectTo: Int) { + private val service: Service = Service.ignite() + + init { + service.port(port) + service.post("/*") { request, response -> + var url = "${request.url()}?${request.queryString()}" + url = url.replace(port.toString(), redirectTo.toString()) + response.redirect(url, 307) + response + } + service.exception(Exception::class.java) { exception, _, response -> + response.status(HttpServletResponse.SC_INTERNAL_SERVER_ERROR) + response.body("Exception: ${exception.message}") + } + service.awaitInitialization() + } + + /** + * Shuts down the mock server and waits for it to be stopped. + */ + fun shutdown() { + service.stop() + service.awaitStop() + } +} diff --git a/system-tests/http-server-shutdown/build.gradle.kts b/system-tests/http-server-shutdown/build.gradle.kts index 4de5d38ef..a1b3e040a 100644 --- a/system-tests/http-server-shutdown/build.gradle.kts +++ b/system-tests/http-server-shutdown/build.gradle.kts @@ -1,4 +1,5 @@ plugins { + com.teamscale.`kotlin-convention` com.teamscale.`system-test-convention` com.teamscale.coverage } diff --git a/system-tests/http-server-shutdown/src/main/java/systemundertest/SystemUnderTest.java b/system-tests/http-server-shutdown/src/main/java/systemundertest/SystemUnderTest.java deleted file mode 100644 index 57b52ed96..000000000 --- a/system-tests/http-server-shutdown/src/main/java/systemundertest/SystemUnderTest.java +++ /dev/null @@ -1,10 +0,0 @@ -package systemundertest; - -/** Fake system under test to generate some coverage. */ -public class SystemUnderTest { - - public int foo() { - return 2; - } - -} diff --git a/system-tests/http-server-shutdown/src/main/kotlin/systemundertest/SystemUnderTest.kt b/system-tests/http-server-shutdown/src/main/kotlin/systemundertest/SystemUnderTest.kt new file mode 100644 index 000000000..b7d25de8f --- /dev/null +++ b/system-tests/http-server-shutdown/src/main/kotlin/systemundertest/SystemUnderTest.kt @@ -0,0 +1,6 @@ +package systemundertest + +/** Fake system under test to generate some coverage. */ +class SystemUnderTest { + fun foo() = 2 +} diff --git a/system-tests/http-server-shutdown/src/test/java/com/teamscale/tia/HttpServerShutdownSystemTest.java b/system-tests/http-server-shutdown/src/test/java/com/teamscale/tia/HttpServerShutdownSystemTest.java deleted file mode 100644 index 8cdd35581..000000000 --- a/system-tests/http-server-shutdown/src/test/java/com/teamscale/tia/HttpServerShutdownSystemTest.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.teamscale.tia; - -import com.teamscale.test.commons.SystemTestUtils; -import org.assertj.core.api.Assertions; -import org.conqat.lib.commons.io.ProcessUtils; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; - -import java.util.concurrent.TimeUnit; - -/** - * Runs the agent with the HTTP server enabled and makes sure that the profiled application is shut down. This ensures - * that the HTTP server is not blocking application shutdown with non-daemon threads. - */ -public class HttpServerShutdownSystemTest { - - @Test - @Timeout(value = 10, unit = TimeUnit.SECONDS) // if the test exceeds the timeout, the shutdown didn't succeed - public void testShutdown() throws Exception { - String agentJar = System.getenv("AGENT_JAR"); - String sampleJar = System.getenv("SAMPLE_JAR"); - ProcessUtils.ExecutionResult result = ProcessUtils.execute( - new ProcessBuilder("java", - "-javaagent:" + agentJar + "=http-server-port=" + SystemTestUtils.AGENT_PORT, - "-jar", sampleJar)); - System.out.println(result.getStderr()); - System.out.println(result.getStdout()); - Assertions.assertThat(result.getReturnCode()).isEqualTo(0); - } - -} diff --git a/system-tests/http-server-shutdown/src/test/kotlin/com/teamscale/tia/HttpServerShutdownSystemTest.kt b/system-tests/http-server-shutdown/src/test/kotlin/com/teamscale/tia/HttpServerShutdownSystemTest.kt new file mode 100644 index 000000000..5f440f1b3 --- /dev/null +++ b/system-tests/http-server-shutdown/src/test/kotlin/com/teamscale/tia/HttpServerShutdownSystemTest.kt @@ -0,0 +1,32 @@ +package com.teamscale.tia + +import com.teamscale.test.commons.SystemTestUtils +import org.assertj.core.api.Assertions.assertThat +import org.conqat.lib.commons.io.ProcessUtils +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.Timeout +import java.util.concurrent.TimeUnit + +/** + * Runs the agent with the HTTP server enabled and makes sure that the profiled application is shut down. This ensures + * that the HTTP server is not blocking application shutdown with non-daemon threads. + */ +class HttpServerShutdownSystemTest { + @Test + @Timeout(value = 10, unit = TimeUnit.SECONDS) // if the test exceeds the timeout, the shutdown didn't succeed + @Throws(Exception::class) + fun testShutdown() { + val agentJar = System.getenv("AGENT_JAR") + val sampleJar = System.getenv("SAMPLE_JAR") + val result = ProcessUtils.execute( + ProcessBuilder( + "java", + "-javaagent:$agentJar=http-server-port=${SystemTestUtils.AGENT_PORT}", + "-jar", sampleJar + ) + ) + println(result.stderr) + println(result.stdout) + assertThat(result.returnCode).isEqualTo(0) + } +} diff --git a/system-tests/junit-run-listener-test/build.gradle.kts b/system-tests/junit-run-listener-test/build.gradle.kts index a983093a7..0711312c5 100644 --- a/system-tests/junit-run-listener-test/build.gradle.kts +++ b/system-tests/junit-run-listener-test/build.gradle.kts @@ -1,4 +1,5 @@ plugins { + com.teamscale.`kotlin-convention` com.teamscale.`system-test-convention` com.teamscale.coverage } diff --git a/system-tests/junit-run-listener-test/src/test/java/com/teamscale/tia/client/JUnitRunListenerSystemTest.java b/system-tests/junit-run-listener-test/src/test/java/com/teamscale/tia/client/JUnitRunListenerSystemTest.java deleted file mode 100644 index 83961cb19..000000000 --- a/system-tests/junit-run-listener-test/src/test/java/com/teamscale/tia/client/JUnitRunListenerSystemTest.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.teamscale.tia.client; - -import com.teamscale.report.testwise.model.ETestExecutionResult; -import com.teamscale.report.testwise.model.TestwiseCoverageReport; -import com.teamscale.test.commons.SystemTestUtils; -import com.teamscale.test.commons.TeamscaleMockServer; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertAll; - -/** - * Runs several Maven projects' Surefire tests that have the agent attached and one of our JUnit run listeners enabled. - * Checks that this produces a correct coverage report. - */ -public class JUnitRunListenerSystemTest { - - private static TeamscaleMockServer teamscaleMockServer = null; - - @BeforeEach - public void startFakeTeamscaleServer() throws Exception { - if (teamscaleMockServer == null) { - teamscaleMockServer = new TeamscaleMockServer(SystemTestUtils.TEAMSCALE_PORT).acceptingReportUploads(); - } - teamscaleMockServer.reset(); - } - - /** Tests the JUnit 5 TestExecutionListener. */ - @Test - public void testJUnit5TestExecutionListener() throws Exception { - SystemTestUtils.runMavenTests("junit5-maven-project"); - - TestwiseCoverageReport report = teamscaleMockServer.getOnlyTestwiseCoverageReport("part"); - assertThat(report.tests).hasSize(2); - assertAll(() -> { - assertThat(report.tests).extracting(test -> test.uniformPath) - .containsExactlyInAnyOrder("JUnit4ExecutedWithJUnit5Test/testAdd()", "JUnit5Test/testAdd()"); - assertThat(report.tests).extracting(test -> test.result) - .containsExactlyInAnyOrder(ETestExecutionResult.PASSED, ETestExecutionResult.PASSED); - assertThat(report.tests).extracting(SystemTestUtils::getCoverageString) - .containsExactly("SystemUnderTest.java:3,6", "SystemUnderTest.java:3,6"); - }); - } - - /** Tests the JUnit 4 RunListener. */ - @Test - public void testJUnit4RunListener() throws Exception { - SystemTestUtils.runMavenTests("junit4-maven-project"); - - TestwiseCoverageReport report = teamscaleMockServer.getOnlyTestwiseCoverageReport("part"); - assertThat(report.tests).hasSize(1); - assertAll(() -> { - assertThat(report.tests).extracting(test -> test.uniformPath) - .containsExactlyInAnyOrder("JUnit4Test/testAdd"); - assertThat(report.tests).extracting(test -> test.result) - .containsExactlyInAnyOrder(ETestExecutionResult.PASSED); - assertThat(report.tests).extracting(SystemTestUtils::getCoverageString) - .containsExactly("SystemUnderTest.java:3,6"); - }); - } - -} diff --git a/system-tests/junit-run-listener-test/src/test/kotlin/com/teamscale/tia/client/JUnitRunListenerSystemTest.kt b/system-tests/junit-run-listener-test/src/test/kotlin/com/teamscale/tia/client/JUnitRunListenerSystemTest.kt new file mode 100644 index 000000000..c924ac256 --- /dev/null +++ b/system-tests/junit-run-listener-test/src/test/kotlin/com/teamscale/tia/client/JUnitRunListenerSystemTest.kt @@ -0,0 +1,77 @@ +package com.teamscale.tia.client + +import com.teamscale.report.testwise.model.ETestExecutionResult +import com.teamscale.report.testwise.model.TestInfo +import com.teamscale.report.testwise.model.TestwiseCoverageReport +import com.teamscale.test.commons.SystemTestUtils +import com.teamscale.test.commons.SystemTestUtils.coverage +import com.teamscale.test.commons.SystemTestUtils.runMavenTests +import com.teamscale.test.commons.TeamscaleMockServer +import org.assertj.core.api.Assertions +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.iterable.ThrowingExtractor +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertAll +import org.junit.jupiter.api.function.Executable + +/** + * Runs several Maven projects' Surefire tests that have the agent attached and one of our JUnit run listeners enabled. + * Checks that this produces a correct coverage report. + */ +class JUnitRunListenerSystemTest { + @BeforeEach + @Throws(Exception::class) + fun startFakeTeamscaleServer() { + if (teamscaleMockServer == null) { + teamscaleMockServer = TeamscaleMockServer(SystemTestUtils.TEAMSCALE_PORT).acceptingReportUploads() + } + teamscaleMockServer?.reset() + } + + /** Tests the JUnit 5 TestExecutionListener. */ + @Test + @Throws(Exception::class) + fun testJUnit5TestExecutionListener() { + runMavenTests("junit5-maven-project") + + val report: TestwiseCoverageReport = teamscaleMockServer!!.getOnlyTestwiseCoverageReport("part") + assertThat(report.tests).hasSize(2) + assertAll({ + assertThat(report.tests) + .extracting { it.uniformPath } + .containsExactlyInAnyOrder("JUnit4ExecutedWithJUnit5Test/testAdd()", "JUnit5Test/testAdd()") + assertThat(report.tests) + .extracting { it.result } + .containsExactlyInAnyOrder(ETestExecutionResult.PASSED, ETestExecutionResult.PASSED) + assertThat(report.tests) + .extracting { it.coverage } + .containsExactly("SystemUnderTest.java:3,6", "SystemUnderTest.java:3,6") + }) + } + + /** Tests the JUnit 4 RunListener. */ + @Test + @Throws(Exception::class) + fun testJUnit4RunListener() { + runMavenTests("junit4-maven-project") + + val report = teamscaleMockServer!!.getOnlyTestwiseCoverageReport("part") + assertThat(report.tests).hasSize(1) + assertAll({ + Assertions.assertThat(report.tests) + .extracting { it.uniformPath } + .containsExactlyInAnyOrder("JUnit4Test/testAdd") + Assertions.assertThat(report.tests) + .extracting { it.result } + .containsExactlyInAnyOrder(ETestExecutionResult.PASSED) + Assertions.assertThat(report.tests) + .extracting { it.coverage } + .containsExactly("SystemUnderTest.java:3,6") + }) + } + + companion object { + private var teamscaleMockServer: TeamscaleMockServer? = null + } +} diff --git a/system-tests/kotlin-inline-function-test/src/test/java/com/teamscale/tia/client/KotlinInlineFunctionTest.java b/system-tests/kotlin-inline-function-test/src/test/java/com/teamscale/tia/client/KotlinInlineFunctionTest.java deleted file mode 100644 index 8ab89c989..000000000 --- a/system-tests/kotlin-inline-function-test/src/test/java/com/teamscale/tia/client/KotlinInlineFunctionTest.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.teamscale.tia.client; - -import com.teamscale.client.EReportFormat; -import com.teamscale.test.commons.SystemTestUtils; -import com.teamscale.test.commons.TeamscaleMockServer; -import foo.MainKt; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Runs the system under test and then forces a dump of the agent to our {@link TeamscaleMockServer}. - * Checks that the correct line numbers are generated for Kotlin inline functions. - */ -public class KotlinInlineFunctionTest { - - @Test - public void systemTest() throws Exception { - TeamscaleMockServer teamscaleMockServer = new TeamscaleMockServer(SystemTestUtils.TEAMSCALE_PORT).acceptingReportUploads(); - MainKt.main(); - SystemTestUtils.dumpCoverage(SystemTestUtils.AGENT_PORT); - - String report = teamscaleMockServer.getOnlyReport("part", EReportFormat.JACOCO); - assertThat(report).doesNotContain(") { + // doesn't need to do anything for this test + } +} diff --git a/system-tests/log-version-on-startup-test/src/test/java/com/teamscale/client/LogVersionOnStartupTest.java b/system-tests/log-version-on-startup-test/src/test/java/com/teamscale/client/LogVersionOnStartupTest.java deleted file mode 100644 index c2ff8eae6..000000000 --- a/system-tests/log-version-on-startup-test/src/test/java/com/teamscale/client/LogVersionOnStartupTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.teamscale.client; - -import org.junit.jupiter.api.Test; - -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.regex.Pattern; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Ensures that the agent logs its version on startup at level INFO. - */ -public class LogVersionOnStartupTest { - - private static final Path LOG_DIRECTORY = Paths.get("logTest").resolve("logs"); - private static final String AGENT_VERSION = System.getenv("AGENT_VERSION"); - - @Test - public void systemTest() throws Exception { - assertThat(Files.exists(LOG_DIRECTORY)).isTrue(); - String logContent = String.join("\n", Files.readAllLines(LOG_DIRECTORY.resolve("teamscale-jacoco-agent.log"))); - assertThat(logContent).containsPattern("INFO.*" + Pattern.quote(AGENT_VERSION)); - } -} diff --git a/system-tests/log-version-on-startup-test/src/test/kotlin/com/teamscale/client/LogVersionOnStartupTest.kt b/system-tests/log-version-on-startup-test/src/test/kotlin/com/teamscale/client/LogVersionOnStartupTest.kt new file mode 100644 index 000000000..20f59fa43 --- /dev/null +++ b/system-tests/log-version-on-startup-test/src/test/kotlin/com/teamscale/client/LogVersionOnStartupTest.kt @@ -0,0 +1,31 @@ +package com.teamscale.client + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test +import java.nio.file.Paths +import java.util.regex.Pattern +import kotlin.io.path.exists +import kotlin.io.path.readLines + +/** + * Ensures that the agent logs its version on startup at level INFO. + */ +class LogVersionOnStartupTest { + @Test + @Throws(Exception::class) + fun systemTest() { + assertTrue(LOG_DIRECTORY.exists()) + val logContent = LOG_DIRECTORY.resolve("teamscale-jacoco-agent.log") + .readLines() + .joinToString("\n") + assertThat(logContent).containsPattern( + "INFO.*" + Pattern.quote(AGENT_VERSION) + ) + } + + companion object { + private val LOG_DIRECTORY = Paths.get("logTest").resolve("logs") + private val AGENT_VERSION = System.getenv("AGENT_VERSION") + } +} diff --git a/system-tests/maven-external-upload-test/build.gradle.kts b/system-tests/maven-external-upload-test/build.gradle.kts index 1eaa62be2..508a88b91 100644 --- a/system-tests/maven-external-upload-test/build.gradle.kts +++ b/system-tests/maven-external-upload-test/build.gradle.kts @@ -1,4 +1,5 @@ plugins { + com.teamscale.`kotlin-convention` com.teamscale.`system-test-convention` } diff --git a/system-tests/maven-external-upload-test/src/test/java/com/teamscale/upload/MavenExternalUploadSystemTest.java b/system-tests/maven-external-upload-test/src/test/java/com/teamscale/upload/MavenExternalUploadSystemTest.java deleted file mode 100644 index 8fd896a9e..000000000 --- a/system-tests/maven-external-upload-test/src/test/java/com/teamscale/upload/MavenExternalUploadSystemTest.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.teamscale.upload; - -import com.teamscale.client.EReportFormat; -import com.teamscale.test.commons.Session; -import com.teamscale.test.commons.SystemTestUtils; -import com.teamscale.test.commons.TeamscaleMockServer; -import org.apache.commons.lang3.SystemUtils; -import org.conqat.lib.commons.filesystem.FileSystemUtils; -import org.conqat.lib.commons.io.ProcessUtils; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.fail; - -/** - * Runs several Maven projects' Surefire and Failsafe tests that produce coverage via the Jacoco Maven plugin. Checks - * that the Jacoco reports are correctly uploaded to a Teamscale instance. - */ -public class MavenExternalUploadSystemTest { - - private static final String MAVEN_COVERAGE_UPLOAD_GOAL = "com.teamscale:teamscale-maven-plugin:upload-coverage"; - - private static TeamscaleMockServer teamscaleMockServer = null; - - private static final String NESTED_MAVEN_PROJECT_NAME = "nested-project"; - - private static final String FAILING_MAVEN_PROJECT_NAME = "failing-project"; - - @BeforeEach - public void startFakeTeamscaleServer() throws Exception { - if (teamscaleMockServer == null) { - teamscaleMockServer = new TeamscaleMockServer(SystemTestUtils.TEAMSCALE_PORT).acceptingReportUploads(); - } - teamscaleMockServer.reset(); - } - - private ProcessUtils.ExecutionResult runCoverageUploadGoal(String projectPath) { - File workingDirectory = new File(projectPath); - String executable = "./mvnw"; - if (SystemUtils.IS_OS_WINDOWS) { - executable = Paths.get(projectPath, "mvnw.cmd").toUri().getPath(); - } - try { - return ProcessUtils.execute( - new ProcessBuilder(executable, MAVEN_COVERAGE_UPLOAD_GOAL).directory(workingDirectory)); - } catch (IOException e) { - fail(String.valueOf(e)); - } - return null; - } - - @Test - public void testMavenExternalUpload() throws Exception { - SystemTestUtils.runMavenTests(NESTED_MAVEN_PROJECT_NAME); - runCoverageUploadGoal(NESTED_MAVEN_PROJECT_NAME); - List sessions = teamscaleMockServer.getSessions(); - assertThat(sessions).hasSize(2); - assertThat(sessions.getFirst().getPartition()).isEqualTo("Integration Tests"); - assertThat(sessions.getLast().getPartition()).isEqualTo("My Custom Unit Tests Partition"); - - assertThat(sessions.getFirst().getReports(EReportFormat.JACOCO)).hasSize(3); - assertThat(sessions.getLast().getReports(EReportFormat.JACOCO)).hasSize(3); - } - - @Test - public void testIncorrectJaCoCoConfiguration() throws IOException { - SystemTestUtils.runMavenTests(FAILING_MAVEN_PROJECT_NAME); - ProcessUtils.ExecutionResult result = runCoverageUploadGoal(FAILING_MAVEN_PROJECT_NAME); - assertThat(result).isNotNull(); - assertThat(teamscaleMockServer.getSessions()).isEmpty(); - assertThat(result.getStdout()).contains( - String.format("Skipping upload for %s as %s is not configured to produce XML reports", - FAILING_MAVEN_PROJECT_NAME, "org.jacoco:jacoco-maven-plugin")); - } - - /** - * When no commit is given and no git repo is available, which is the usual fallback, a helpful error message should - * be shown (TS-40425). - */ - @Test - public void testErrorMessageOnMissingCommit(@TempDir Path tmpDir) throws IOException { - FileSystemUtils.copyFiles(new File("missing-commit-project"), tmpDir.toFile(), file -> true); - tmpDir.resolve("mvnw").toFile().setExecutable(true); - String projectPath = tmpDir.toAbsolutePath().toString(); - SystemTestUtils.runMavenTests(projectPath); - ProcessUtils.ExecutionResult result = runCoverageUploadGoal(projectPath); - assertThat(result).isNotNull(); - assertThat(result.getReturnCode()).isNotEqualTo(0); - assertThat(teamscaleMockServer.getSessions()).isEmpty(); - assertThat(result.getStdout()).contains("There is no or configured in the pom.xml and it was not possible to determine the current revision"); - } - - @AfterAll - public static void stopFakeTeamscaleServer() { - teamscaleMockServer.shutdown(); - } - -} diff --git a/system-tests/maven-external-upload-test/src/test/kotlin/com/teamscale/upload/MavenExternalUploadSystemTest.kt b/system-tests/maven-external-upload-test/src/test/kotlin/com/teamscale/upload/MavenExternalUploadSystemTest.kt new file mode 100644 index 000000000..b02b826ba --- /dev/null +++ b/system-tests/maven-external-upload-test/src/test/kotlin/com/teamscale/upload/MavenExternalUploadSystemTest.kt @@ -0,0 +1,112 @@ +package com.teamscale.upload + +import com.teamscale.client.EReportFormat +import com.teamscale.test.commons.ExternalReport +import com.teamscale.test.commons.Session +import com.teamscale.test.commons.SystemTestUtils +import com.teamscale.test.commons.SystemTestUtils.runMavenTests +import com.teamscale.test.commons.TeamscaleMockServer +import org.apache.commons.lang3.SystemUtils +import org.assertj.core.api.Assertions +import org.assertj.core.api.Assertions.assertThat +import org.conqat.lib.commons.filesystem.FileSystemUtils +import org.conqat.lib.commons.io.ProcessUtils +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.io.TempDir +import java.io.File +import java.io.FileFilter +import java.io.IOException +import java.nio.file.Path +import java.nio.file.Paths + +/** + * Runs several Maven projects' Surefire and Failsafe tests that produce coverage via the Jacoco Maven plugin. Checks + * that the Jacoco reports are correctly uploaded to a Teamscale instance. + */ +class MavenExternalUploadSystemTest { + @BeforeEach + @Throws(Exception::class) + fun startFakeTeamscaleServer() { + if (teamscaleMockServer == null) { + teamscaleMockServer = TeamscaleMockServer(SystemTestUtils.TEAMSCALE_PORT).acceptingReportUploads() + } + teamscaleMockServer?.reset() + } + + private fun runCoverageUploadGoal(projectPath: String): ProcessUtils.ExecutionResult? { + val workingDirectory = File(projectPath) + var executable = "./mvnw" + if (SystemUtils.IS_OS_WINDOWS) { + executable = Paths.get(projectPath, "mvnw.cmd").toUri().getPath() + } + try { + return ProcessUtils.execute( + ProcessBuilder(executable, MAVEN_COVERAGE_UPLOAD_GOAL).directory(workingDirectory) + ) + } catch (e: IOException) { + Assertions.fail(e.toString()) + } + return null + } + + @Test + @Throws(Exception::class) + fun testMavenExternalUpload() { + runMavenTests(NESTED_MAVEN_PROJECT_NAME) + runCoverageUploadGoal(NESTED_MAVEN_PROJECT_NAME) + val sessions = teamscaleMockServer!!.getSessions() + assertThat(sessions).hasSize(2) + assertThat(sessions.first().partition).isEqualTo("Integration Tests") + assertThat(sessions.last().partition).isEqualTo("My Custom Unit Tests Partition") + + assertThat(sessions.first().getReports(EReportFormat.JACOCO)).hasSize(3) + assertThat(sessions.last().getReports(EReportFormat.JACOCO)).hasSize(3) + } + + @Test + @Throws(IOException::class) + fun testIncorrectJaCoCoConfiguration() { + runMavenTests(FAILING_MAVEN_PROJECT_NAME) + val result = runCoverageUploadGoal(FAILING_MAVEN_PROJECT_NAME) + assertThat(result).isNotNull() + assertThat(teamscaleMockServer!!.getSessions()).isEmpty() + assertThat(result!!.stdout).contains( + "Skipping upload for $FAILING_MAVEN_PROJECT_NAME as org.jacoco:jacoco-maven-plugin is not configured to produce XML reports" + ) + } + + /** + * When no commit is given and no git repo is available, which is the usual fallback, a helpful error message should + * be shown (TS-40425). + */ + @Test + @Throws(IOException::class) + fun testErrorMessageOnMissingCommit(@TempDir tmpDir: Path) { + FileSystemUtils.copyFiles(File("missing-commit-project"), tmpDir.toFile()) { true } + tmpDir.resolve("mvnw").toFile().setExecutable(true) + val projectPath = tmpDir.toAbsolutePath().toString() + runMavenTests(projectPath) + val result = runCoverageUploadGoal(projectPath) + assertThat(result).isNotNull() + assertThat(result!!.returnCode).isNotEqualTo(0) + assertThat(teamscaleMockServer!!.getSessions()).isEmpty() + assertThat(result.stdout) + .contains("There is no or configured in the pom.xml and it was not possible to determine the current revision") + } + + companion object { + private var teamscaleMockServer: TeamscaleMockServer? = null + + private const val MAVEN_COVERAGE_UPLOAD_GOAL = "com.teamscale:teamscale-maven-plugin:upload-coverage" + private const val NESTED_MAVEN_PROJECT_NAME = "nested-project" + private const val FAILING_MAVEN_PROJECT_NAME = "failing-project" + + @JvmStatic + @AfterAll + fun stopFakeTeamscaleServer() { + teamscaleMockServer?.shutdown() + } + } +} diff --git a/system-tests/multiple-agents-test/build.gradle.kts b/system-tests/multiple-agents-test/build.gradle.kts index 4cd625239..95abfa08f 100644 --- a/system-tests/multiple-agents-test/build.gradle.kts +++ b/system-tests/multiple-agents-test/build.gradle.kts @@ -1,4 +1,5 @@ plugins { + com.teamscale.`kotlin-convention` com.teamscale.`system-test-convention` } diff --git a/system-tests/multiple-agents-test/src/main/java/systemundertest/SystemUnderTest.java b/system-tests/multiple-agents-test/src/main/java/systemundertest/SystemUnderTest.java deleted file mode 100644 index aa4cf27f2..000000000 --- a/system-tests/multiple-agents-test/src/main/java/systemundertest/SystemUnderTest.java +++ /dev/null @@ -1,8 +0,0 @@ -package systemundertest; - -public class SystemUnderTest { - - public int foo() { - return 2; - } -} diff --git a/system-tests/multiple-agents-test/src/main/kotlin/systemundertest/SystemUnderTest.kt b/system-tests/multiple-agents-test/src/main/kotlin/systemundertest/SystemUnderTest.kt new file mode 100644 index 000000000..8bb274ff9 --- /dev/null +++ b/system-tests/multiple-agents-test/src/main/kotlin/systemundertest/SystemUnderTest.kt @@ -0,0 +1,5 @@ +package systemundertest + +class SystemUnderTest { + fun foo() = 2 +} diff --git a/system-tests/multiple-agents-test/src/test/java/com/teamscale/client/MultipleAgentsTest.java b/system-tests/multiple-agents-test/src/test/java/com/teamscale/client/MultipleAgentsTest.java deleted file mode 100644 index bbfddb562..000000000 --- a/system-tests/multiple-agents-test/src/test/java/com/teamscale/client/MultipleAgentsTest.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.teamscale.client; - -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; - -public class MultipleAgentsTest { - - private static final Path LOG_DIRECTORY = Paths.get("logTest").resolve("logs"); - - @Test - public void systemTest() throws IOException { - assertThat(LOG_DIRECTORY).exists(); - List lines = Files.readAllLines(LOG_DIRECTORY.resolve("teamscale-jacoco-agent.log")); - assertThat(lines).anyMatch( - s -> s.contains("Using multiple java agents could interfere with coverage recording.") - ); - assertThat(lines).anyMatch( - s -> s.contains("For best results consider registering the Teamscale JaCoCo Agent first.") - ); - } -} diff --git a/system-tests/multiple-agents-test/src/test/kotlin/com/teamscale/client/MultipleAgentsTest.kt b/system-tests/multiple-agents-test/src/test/kotlin/com/teamscale/client/MultipleAgentsTest.kt new file mode 100644 index 000000000..7dca7763b --- /dev/null +++ b/system-tests/multiple-agents-test/src/test/kotlin/com/teamscale/client/MultipleAgentsTest.kt @@ -0,0 +1,24 @@ +package com.teamscale.client + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import java.io.IOException +import java.nio.file.Paths +import kotlin.io.path.readLines + +class MultipleAgentsTest { + @Test + @Throws(IOException::class) + fun systemTest() { + assertThat(LOG_DIRECTORY).exists() + val lines = LOG_DIRECTORY.resolve("teamscale-jacoco-agent.log").readLines() + assertThat(lines) + .anyMatch { it.contains("Using multiple java agents could interfere with coverage recording.") } + assertThat(lines) + .anyMatch { it.contains("For best results consider registering the Teamscale JaCoCo Agent first.") } + } + + companion object { + private val LOG_DIRECTORY = Paths.get("logTest").resolve("logs") + } +} diff --git a/system-tests/sut-uses-logback-test/build.gradle.kts b/system-tests/sut-uses-logback-test/build.gradle.kts index 34f5f9b66..f4e0d14ae 100644 --- a/system-tests/sut-uses-logback-test/build.gradle.kts +++ b/system-tests/sut-uses-logback-test/build.gradle.kts @@ -1,6 +1,7 @@ import org.gradle.jvm.tasks.Jar plugins { + com.teamscale.`kotlin-convention` com.teamscale.`system-test-convention` } diff --git a/system-tests/sut-uses-logback-test/src/main/java/jul/test/SystemUnderTest.java b/system-tests/sut-uses-logback-test/src/main/java/jul/test/SystemUnderTest.java deleted file mode 100644 index e056e5ad9..000000000 --- a/system-tests/sut-uses-logback-test/src/main/java/jul/test/SystemUnderTest.java +++ /dev/null @@ -1,11 +0,0 @@ -package jul.test; - -import org.slf4j.LoggerFactory; - -public class SystemUnderTest { - - public static void main(String[] args) { - LoggerFactory.getLogger(SystemUnderTest.class).warn("This warning is to test logging in the SUT"); - } - -} diff --git a/system-tests/sut-uses-logback-test/src/main/kotlin/jul/test/SystemUnderTest.kt b/system-tests/sut-uses-logback-test/src/main/kotlin/jul/test/SystemUnderTest.kt new file mode 100644 index 000000000..bdb379c40 --- /dev/null +++ b/system-tests/sut-uses-logback-test/src/main/kotlin/jul/test/SystemUnderTest.kt @@ -0,0 +1,10 @@ +package jul.test + +import org.slf4j.LoggerFactory + +object SystemUnderTest { + @JvmStatic + fun main(args: Array) { + LoggerFactory.getLogger(SystemUnderTest::class.java).warn("This warning is to test logging in the SUT") + } +} diff --git a/system-tests/sut-uses-logback-test/src/test/java/com/teamscale/client/SutUsesLogbackTest.java b/system-tests/sut-uses-logback-test/src/test/java/com/teamscale/client/SutUsesLogbackTest.java deleted file mode 100644 index cd23dd858..000000000 --- a/system-tests/sut-uses-logback-test/src/test/java/com/teamscale/client/SutUsesLogbackTest.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.teamscale.client; - -import org.conqat.lib.commons.io.ProcessUtils; -import org.junit.jupiter.api.Test; - -import java.nio.file.Path; -import java.nio.file.Paths; - -import static org.assertj.core.api.Assertions.assertThat; - -public class SutUsesLogbackTest { - - private static final String AGENT_JAR = System.getProperty("agentJar"); - - @Test - public void systemTest() throws Exception { - ProcessUtils.ExecutionResult result = ProcessUtils.execute( - new String[]{"java", "-javaagent:" + AGENT_JAR + "=debug=true", "-jar", "build/libs/app.jar"}); - - assertThat(result.getStdout()).contains("This warning is to test logging in the SUT"); - assertThat(result.getStdout()).doesNotContainIgnoringCase("error"); - assertThat(result.getStderr()).isEmpty(); - - Path appLogFile = Paths.get("logTest/app.log"); - assertThat(appLogFile).exists(); - assertThat(appLogFile).content().contains("This warning is to test logging in the SUT"); - } - -} diff --git a/system-tests/sut-uses-logback-test/src/test/kotlin/com/teamscale/client/SutUsesLogbackTest.kt b/system-tests/sut-uses-logback-test/src/test/kotlin/com/teamscale/client/SutUsesLogbackTest.kt new file mode 100644 index 000000000..d6b4a4aff --- /dev/null +++ b/system-tests/sut-uses-logback-test/src/test/kotlin/com/teamscale/client/SutUsesLogbackTest.kt @@ -0,0 +1,28 @@ +package com.teamscale.client + +import org.assertj.core.api.Assertions.assertThat +import org.conqat.lib.commons.io.ProcessUtils +import org.junit.jupiter.api.Test +import java.nio.file.Paths + +class SutUsesLogbackTest { + @Test + @Throws(Exception::class) + fun systemTest() { + val result = ProcessUtils.execute( + arrayOf("java", "-javaagent:$AGENT_JAR=debug=true", "-jar", "build/libs/app.jar") + ) + + assertThat(result.stdout).contains("This warning is to test logging in the SUT") + assertThat(result.stdout).doesNotContainIgnoringCase("error") + assertThat(result.stderr).isEmpty() + + val appLogFile = Paths.get("logTest/app.log") + assertThat(appLogFile).exists() + assertThat(appLogFile).content().contains("This warning is to test logging in the SUT") + } + + companion object { + private val AGENT_JAR = System.getProperty("agentJar") + } +} diff --git a/system-tests/teamscale-profiler-configuration-test/build.gradle.kts b/system-tests/teamscale-profiler-configuration-test/build.gradle.kts index e81e7869d..140b0ccda 100644 --- a/system-tests/teamscale-profiler-configuration-test/build.gradle.kts +++ b/system-tests/teamscale-profiler-configuration-test/build.gradle.kts @@ -1,6 +1,7 @@ import kotlin.io.path.writeText plugins { + com.teamscale.`kotlin-convention` com.teamscale.`system-test-convention` } diff --git a/system-tests/teamscale-profiler-configuration-test/src/test/java/com/teamscale/client/TeamscaleProfilerConfigurationSystemTest.java b/system-tests/teamscale-profiler-configuration-test/src/test/java/com/teamscale/client/TeamscaleProfilerConfigurationSystemTest.java deleted file mode 100644 index ddcca4cd7..000000000 --- a/system-tests/teamscale-profiler-configuration-test/src/test/java/com/teamscale/client/TeamscaleProfilerConfigurationSystemTest.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.teamscale.client; - -import com.teamscale.test.commons.TeamscaleMockServer; -import org.assertj.core.api.Assertions; -import org.conqat.lib.commons.io.ProcessUtils; -import org.junit.jupiter.api.Test; - -import java.util.LinkedHashSet; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Ensures that the teamscale.properties file is successfully located and used to retrieve the configuration from - * Teamscale. - */ -public class TeamscaleProfilerConfigurationSystemTest { - - /** These ports must match what is configured for the -javaagent line in this project's build.gradle. */ - private static final int FAKE_TEAMSCALE_PORT = 64100; - - @Test - public void systemTestRetrieveConfig() throws Exception { - ProfilerConfiguration profilerConfiguration = new ProfilerConfiguration(); - profilerConfiguration.configurationId = "my-config"; - profilerConfiguration.configurationOptions = "teamscale-partition=foo\nteamscale-project=p\nteamscale-commit=master:12345"; - TeamscaleMockServer teamscaleMockServer = new TeamscaleMockServer(FAKE_TEAMSCALE_PORT).acceptingReportUploads() - .withProfilerConfiguration(profilerConfiguration); - - String agentJar = System.getenv("AGENT_JAR"); - String sampleJar = System.getenv("SYSTEM_UNDER_TEST_JAR"); - ProcessUtils.ExecutionResult result = ProcessUtils.execute( - new ProcessBuilder("java", "-javaagent:" + agentJar + "=config-id=my-config", "-jar", sampleJar)); - System.out.println(result.getStderr()); - System.out.println(result.getStdout()); - Assertions.assertThat(result.getReturnCode()).isEqualTo(0); - - assertThat(teamscaleMockServer.getOnlySession().getPartition()).isEqualTo("foo"); - - teamscaleMockServer.shutdown(); - - assertThat(new LinkedHashSet<>(teamscaleMockServer.getProfilerEvents())).as("We expect a sequence of interactions with the mock. " + - "Note that unexpected interactions can be caused by old agent instances that have not been killed properly.") // - .containsExactly("Profiler registered and requested configuration my-config", - "Profiler 123 sent logs", - "Profiler 123 sent heartbeat", - "Profiler 123 unregistered"); - } - - /** - * Tests that the system under test does start up normally after the 2 minutes of timeout elapsed when Teamscale is - * not available. - */ - @Test - public void systemTestLenientFailure() throws Exception { - String agentJar = System.getenv("AGENT_JAR"); - String sampleJar = System.getenv("SYSTEM_UNDER_TEST_JAR"); - ProcessUtils.ExecutionResult result = ProcessUtils.execute( - new ProcessBuilder("java", "-javaagent:" + agentJar + "=config-id=some-config", "-jar", sampleJar)); - System.out.println(result.getStderr()); - System.out.println(result.getStdout()); - Assertions.assertThat(result.getReturnCode()).isEqualTo(0); - - assertThat(result.getStdout()).contains("Production code"); - } - -} diff --git a/system-tests/teamscale-profiler-configuration-test/src/test/kotlin/com/teamscale/client/TeamscaleProfilerConfigurationSystemTest.kt b/system-tests/teamscale-profiler-configuration-test/src/test/kotlin/com/teamscale/client/TeamscaleProfilerConfigurationSystemTest.kt new file mode 100644 index 000000000..b27ffac3b --- /dev/null +++ b/system-tests/teamscale-profiler-configuration-test/src/test/kotlin/com/teamscale/client/TeamscaleProfilerConfigurationSystemTest.kt @@ -0,0 +1,68 @@ +package com.teamscale.client + +import com.teamscale.test.commons.TeamscaleMockServer +import org.assertj.core.api.Assertions +import org.assertj.core.api.Assertions.assertThat +import org.conqat.lib.commons.io.ProcessUtils +import org.junit.jupiter.api.Test + +/** + * Ensures that the teamscale.properties file is successfully located and used to retrieve the configuration from + * Teamscale. + */ +class TeamscaleProfilerConfigurationSystemTest { + @Test + @Throws(Exception::class) + fun systemTestRetrieveConfig() { + val profilerConfiguration = ProfilerConfiguration().apply { + configurationId = "my-config" + configurationOptions = "teamscale-partition=foo\nteamscale-project=p\nteamscale-commit=master:12345" + } + val teamscaleMockServer = TeamscaleMockServer(FAKE_TEAMSCALE_PORT).acceptingReportUploads() + .withProfilerConfiguration(profilerConfiguration) + + val agentJar = System.getenv("AGENT_JAR") + val sampleJar = System.getenv("SYSTEM_UNDER_TEST_JAR") + val result = ProcessUtils.execute( + ProcessBuilder("java", "-javaagent:$agentJar=config-id=my-config", "-jar", sampleJar) + ) + println(result.stderr) + println(result.stdout) + assertThat(result.returnCode).isEqualTo(0) + assertThat(teamscaleMockServer.onlySession.partition).isEqualTo("foo") + + teamscaleMockServer.shutdown() + + assertThat(teamscaleMockServer.profilerEvents.toSet()).`as`( + "We expect a sequence of interactions with the mock. Note that unexpected interactions can be caused by old agent instances that have not been killed properly." + ).containsExactly( + "Profiler registered and requested configuration my-config", + "Profiler 123 sent logs", + "Profiler 123 sent heartbeat", + "Profiler 123 unregistered" + ) + } + + /** + * Tests that the system under test does start up normally after the 2 minutes of timeout elapsed when Teamscale is + * not available. + */ + @Test + @Throws(Exception::class) + fun systemTestLenientFailure() { + val agentJar = System.getenv("AGENT_JAR") + val sampleJar = System.getenv("SYSTEM_UNDER_TEST_JAR") + val result = ProcessUtils.execute( + ProcessBuilder("java", "-javaagent:$agentJar=config-id=some-config", "-jar", sampleJar) + ) + println(result.stderr) + println(result.stdout) + assertThat(result.returnCode).isEqualTo(0) + assertThat(result.stdout).contains("Production code") + } + + companion object { + /** These ports must match what is configured for the -javaagent line in this project's build.gradle. */ + private const val FAKE_TEAMSCALE_PORT = 64100 + } +} diff --git a/system-tests/teamscale-properties-test/build.gradle.kts b/system-tests/teamscale-properties-test/build.gradle.kts index 63d7aecd2..d8c5d001b 100644 --- a/system-tests/teamscale-properties-test/build.gradle.kts +++ b/system-tests/teamscale-properties-test/build.gradle.kts @@ -1,6 +1,7 @@ import kotlin.io.path.writeText plugins { + com.teamscale.`kotlin-convention` com.teamscale.`system-test-convention` } diff --git a/system-tests/teamscale-properties-test/src/main/java/systemundertest/SystemUnderTest.java b/system-tests/teamscale-properties-test/src/main/java/systemundertest/SystemUnderTest.java deleted file mode 100644 index 57b52ed96..000000000 --- a/system-tests/teamscale-properties-test/src/main/java/systemundertest/SystemUnderTest.java +++ /dev/null @@ -1,10 +0,0 @@ -package systemundertest; - -/** Fake system under test to generate some coverage. */ -public class SystemUnderTest { - - public int foo() { - return 2; - } - -} diff --git a/system-tests/teamscale-properties-test/src/main/kotlin/systemundertest/SystemUnderTest.kt b/system-tests/teamscale-properties-test/src/main/kotlin/systemundertest/SystemUnderTest.kt new file mode 100644 index 000000000..b7d25de8f --- /dev/null +++ b/system-tests/teamscale-properties-test/src/main/kotlin/systemundertest/SystemUnderTest.kt @@ -0,0 +1,6 @@ +package systemundertest + +/** Fake system under test to generate some coverage. */ +class SystemUnderTest { + fun foo() = 2 +} diff --git a/system-tests/teamscale-properties-test/src/test/java/com/teamscale/client/TeamscalePropertiesSystemTest.java b/system-tests/teamscale-properties-test/src/test/java/com/teamscale/client/TeamscalePropertiesSystemTest.java deleted file mode 100644 index adbf12e15..000000000 --- a/system-tests/teamscale-properties-test/src/test/java/com/teamscale/client/TeamscalePropertiesSystemTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.teamscale.client; - -import com.teamscale.test.commons.SystemTestUtils; -import com.teamscale.test.commons.TeamscaleMockServer; -import org.junit.jupiter.api.Test; -import systemundertest.SystemUnderTest; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Ensures that the teamscale.properties file is successfully located and parsed. - */ -public class TeamscalePropertiesSystemTest { - - @Test - public void systemTest() throws Exception { - TeamscaleMockServer teamscaleMockServer = new TeamscaleMockServer(SystemTestUtils.TEAMSCALE_PORT).acceptingReportUploads(); - - new SystemUnderTest().foo(); - SystemTestUtils.dumpCoverage(SystemTestUtils.AGENT_PORT); - - assertThat(teamscaleMockServer.getOnlySession().getReports()).hasSize(1); - teamscaleMockServer.shutdown(); - } - -} diff --git a/system-tests/teamscale-properties-test/src/test/kotlin/com/teamscale/client/TeamscalePropertiesSystemTest.kt b/system-tests/teamscale-properties-test/src/test/kotlin/com/teamscale/client/TeamscalePropertiesSystemTest.kt new file mode 100644 index 000000000..c45a9bb8a --- /dev/null +++ b/system-tests/teamscale-properties-test/src/test/kotlin/com/teamscale/client/TeamscalePropertiesSystemTest.kt @@ -0,0 +1,25 @@ +package com.teamscale.client + +import com.teamscale.test.commons.SystemTestUtils +import com.teamscale.test.commons.SystemTestUtils.dumpCoverage +import com.teamscale.test.commons.TeamscaleMockServer +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import systemundertest.SystemUnderTest + +/** + * Ensures that the teamscale.properties file is successfully located and parsed. + */ +class TeamscalePropertiesSystemTest { + @Test + @Throws(Exception::class) + fun systemTest() { + val teamscaleMockServer = TeamscaleMockServer(SystemTestUtils.TEAMSCALE_PORT).acceptingReportUploads() + + SystemUnderTest().foo() + dumpCoverage(SystemTestUtils.AGENT_PORT) + + assertThat(teamscaleMockServer.onlySession.getReports()).hasSize(1) + teamscaleMockServer.shutdown() + } +} diff --git a/system-tests/tia-client-test/build.gradle.kts b/system-tests/tia-client-test/build.gradle.kts index a3e7bfc64..494fd7b6e 100644 --- a/system-tests/tia-client-test/build.gradle.kts +++ b/system-tests/tia-client-test/build.gradle.kts @@ -1,4 +1,5 @@ plugins { + com.teamscale.`kotlin-convention` com.teamscale.`system-test-convention` } diff --git a/system-tests/tia-client-test/src/main/java/systemundertest/SystemUnderTest.java b/system-tests/tia-client-test/src/main/java/systemundertest/SystemUnderTest.java deleted file mode 100644 index 2adc6d0e6..000000000 --- a/system-tests/tia-client-test/src/main/java/systemundertest/SystemUnderTest.java +++ /dev/null @@ -1,15 +0,0 @@ -package systemundertest; - -/** Fake system under test that is exercised by the fake {@link testframework.CustomTestFramework}. */ -public class SystemUnderTest { - - /** Returns 5. */ - public int foo() { - return 5; - } - - /** Returns 6. */ - public int bar() { - return 6; - } -} diff --git a/system-tests/tia-client-test/src/main/java/testframework/CustomTestFramework.java b/system-tests/tia-client-test/src/main/java/testframework/CustomTestFramework.java deleted file mode 100644 index 7e26ddcfe..000000000 --- a/system-tests/tia-client-test/src/main/java/testframework/CustomTestFramework.java +++ /dev/null @@ -1,64 +0,0 @@ -package testframework; - -import com.teamscale.client.ClusteredTestDetails; -import com.teamscale.client.PrioritizableTest; -import com.teamscale.client.PrioritizableTestCluster; -import com.teamscale.report.testwise.model.ETestExecutionResult; -import com.teamscale.tia.client.AgentHttpRequestFailedException; -import com.teamscale.tia.client.RunningTest; -import com.teamscale.tia.client.TestRun; -import com.teamscale.tia.client.TestRunWithClusteredSuggestions; -import com.teamscale.tia.client.TiaAgent; -import okhttp3.HttpUrl; -import systemundertest.SystemUnderTest; - -import java.util.HashMap; -import java.util.Map; - -import static java.util.stream.Collectors.toList; - -/** Simulates a custom test framework that doesn't rely on JUnit. */ -public class CustomTestFramework { - - private final Map allTests = new HashMap<>(); - private final int agentPort; - - public CustomTestFramework(int agentPort) { - this.agentPort = agentPort; - allTests.put("testFoo", () -> { - if (new SystemUnderTest().foo() != 5) { - throw new AssertionError("Incorrect return value for foo"); - } - }); - allTests.put("testBar", () -> { - if (new SystemUnderTest().bar() != 7) { - throw new AssertionError("Incorrect return value for bar"); - } - }); - } - - /** Talks to the TIA agent and runs all impacted tests. Also uploads a coverage report at the end. */ - public void runTestsWithTia() throws AgentHttpRequestFailedException { - TiaAgent agent = new TiaAgent(false, HttpUrl.get("http://localhost:" + agentPort)); - TestRunWithClusteredSuggestions testRun = agent.startTestRun( - allTests.keySet().stream().map(name -> new ClusteredTestDetails(name, name, null, null, null)) - .collect(toList())); - - for (PrioritizableTestCluster cluster : testRun.prioritizedClusters) { - for (PrioritizableTest test : cluster.tests) { - Runnable runnable = allTests.get(test.testName); - RunningTest runningTest = testRun.startTest(test.testName); - try { - runnable.run(); - runningTest.endTest(new TestRun.TestResultWithMessage(ETestExecutionResult.PASSED, "")); - } catch (Throwable t) { - runningTest.endTest( - new TestRun.TestResultWithMessage(ETestExecutionResult.FAILURE, t.getMessage())); - } - } - } - - testRun.endTestRun(true); - } - -} diff --git a/system-tests/tia-client-test/src/main/kotlin/systemundertest/SystemUnderTest.kt b/system-tests/tia-client-test/src/main/kotlin/systemundertest/SystemUnderTest.kt new file mode 100644 index 000000000..4c7b1c8c2 --- /dev/null +++ b/system-tests/tia-client-test/src/main/kotlin/systemundertest/SystemUnderTest.kt @@ -0,0 +1,10 @@ +package systemundertest + +/** Fake system under test that is exercised by the fake [testframework.CustomTestFramework]. */ +class SystemUnderTest { + /** Returns 5. */ + fun foo() = 5 + + /** Returns 6. */ + fun bar() = 6 +} diff --git a/system-tests/tia-client-test/src/main/kotlin/testframework/CustomTestFramework.kt b/system-tests/tia-client-test/src/main/kotlin/testframework/CustomTestFramework.kt new file mode 100644 index 000000000..bb15094bf --- /dev/null +++ b/system-tests/tia-client-test/src/main/kotlin/testframework/CustomTestFramework.kt @@ -0,0 +1,55 @@ +package testframework + +import com.teamscale.client.ClusteredTestDetails +import com.teamscale.report.testwise.model.ETestExecutionResult +import com.teamscale.tia.client.AgentHttpRequestFailedException +import com.teamscale.tia.client.TestRun.TestResultWithMessage +import com.teamscale.tia.client.TiaAgent +import okhttp3.HttpUrl.Companion.toHttpUrl +import systemundertest.SystemUnderTest +import java.util.stream.Collectors + +/** Simulates a custom test framework that doesn't rely on JUnit. */ +class CustomTestFramework(private val agentPort: Int) { + private val allTests = hashMapOf Unit>() + + init { + allTests.put("testFoo") { + if (SystemUnderTest().foo() != 5) { + throw AssertionError("Incorrect return value for foo") + } + } + allTests.put("testBar") { + if (SystemUnderTest().bar() != 7) { + throw AssertionError("Incorrect return value for bar") + } + } + } + + /** Talks to the TIA agent and runs all impacted tests. Also uploads a coverage report at the end. */ + @Throws(AgentHttpRequestFailedException::class) + fun runTestsWithTia() { + val agent = TiaAgent(false, "http://localhost:$agentPort".toHttpUrl()) + val testRun = agent.startTestRun( + allTests.keys + .map { name -> ClusteredTestDetails(name, name, null, null, null) } + ) + + testRun.prioritizedClusters?.forEach { cluster -> + cluster.tests?.forEach { test -> + val runnable = allTests.get(test.testName) + val runningTest = testRun.startTest(test.testName) + try { + runnable?.invoke() + runningTest.endTest(TestResultWithMessage(ETestExecutionResult.PASSED, "")) + } catch (t: Throwable) { + runningTest.endTest( + TestResultWithMessage(ETestExecutionResult.FAILURE, t.message) + ) + } + } + } + + testRun.endTestRun(true) + } +} diff --git a/system-tests/tia-client-test/src/test/java/com/teamscale/tia/client/TiaClientSystemTest.java b/system-tests/tia-client-test/src/test/java/com/teamscale/tia/client/TiaClientSystemTest.java deleted file mode 100644 index c6f5e99df..000000000 --- a/system-tests/tia-client-test/src/test/java/com/teamscale/tia/client/TiaClientSystemTest.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.teamscale.tia.client; - -import com.teamscale.report.testwise.model.ETestExecutionResult; -import com.teamscale.report.testwise.model.TestwiseCoverageReport; -import com.teamscale.test.commons.SystemTestUtils; -import com.teamscale.test.commons.TeamscaleMockServer; -import okhttp3.HttpUrl; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import systemundertest.SystemUnderTest; -import testframework.CustomTestFramework; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertAll; - -/** - * Runs the custom test framework from src/main with our agent attached (the agent is configured in this project's - * build.gradle). The custom test framework contains an integration via the tia-client against the - * {@link TeamscaleMockServer}. Asserts that the resulting report looks as expected. - *

- * This ensures that our tia-client library doesn't break unexpectedly. - */ -public class TiaClientSystemTest { - - private static TeamscaleMockServer teamscaleMockServer = null; - - @BeforeEach - public void startFakeTeamscaleServer() throws Exception { - if (teamscaleMockServer == null) { - teamscaleMockServer = new TeamscaleMockServer(SystemTestUtils.TEAMSCALE_PORT).acceptingReportUploads() - .withImpactedTests("testFoo", "testBar"); - } - teamscaleMockServer.reset(); - } - - @Test - public void systemTest() throws Exception { - CustomTestFramework customTestFramework = new CustomTestFramework(SystemTestUtils.AGENT_PORT); - customTestFramework.runTestsWithTia(); - - TestwiseCoverageReport report = teamscaleMockServer.getOnlyTestwiseCoverageReport("part"); - assertThat(report.tests).hasSize(2); - assertAll(() -> { - assertThat(report.tests).extracting(test -> test.uniformPath) - .containsExactlyInAnyOrder("testBar", "testFoo"); - assertThat(report.tests).extracting(test -> test.result) - .containsExactlyInAnyOrder(ETestExecutionResult.FAILURE, ETestExecutionResult.PASSED); - assertThat(report.tests).extracting(SystemTestUtils::getCoverageString) - .containsExactlyInAnyOrder("SystemUnderTest.java:4,13", "SystemUnderTest.java:4,8"); - }); - } - - @Test - public void systemTestWithSpecialCharacter() throws Exception { - TiaAgent agent = new TiaAgent(false, HttpUrl.get("http://localhost:" + SystemTestUtils.AGENT_PORT)); - TestRun testRun = agent.startTestRunWithoutTestSelection(); - - String uniformPath = "my/strange;te st[(-+path!@#$%^&*(name"; - RunningTest runningTest = testRun.startTest(uniformPath); - new SystemUnderTest().foo(); - runningTest.endTest(new TestRun.TestResultWithMessage(ETestExecutionResult.PASSED, "")); - - testRun.endTestRun(true); - - TestwiseCoverageReport report = teamscaleMockServer.getOnlyTestwiseCoverageReport("part"); - assertThat(report.tests).hasSize(1); - assertThat(report.tests).element(0).extracting(test -> test.uniformPath).isEqualTo(uniformPath); - } - - @AfterAll - public static void stopFakeTeamscaleServer() { - teamscaleMockServer.shutdown(); - } -} diff --git a/system-tests/tia-client-test/src/test/kotlin/com/teamscale/tia/client/TiaClientSystemTest.kt b/system-tests/tia-client-test/src/test/kotlin/com/teamscale/tia/client/TiaClientSystemTest.kt new file mode 100644 index 000000000..e4c0eb75d --- /dev/null +++ b/system-tests/tia-client-test/src/test/kotlin/com/teamscale/tia/client/TiaClientSystemTest.kt @@ -0,0 +1,90 @@ +package com.teamscale.tia.client + +import com.teamscale.report.testwise.model.ETestExecutionResult +import com.teamscale.report.testwise.model.TestInfo +import com.teamscale.report.testwise.model.TestwiseCoverageReport +import com.teamscale.test.commons.SystemTestUtils +import com.teamscale.test.commons.SystemTestUtils.coverage +import com.teamscale.test.commons.TeamscaleMockServer +import com.teamscale.tia.client.TestRun.TestResultWithMessage +import okhttp3.HttpUrl.Companion.toHttpUrl +import org.assertj.core.api.Assertions +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.iterable.ThrowingExtractor +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertAll +import org.junit.jupiter.api.function.Executable +import systemundertest.SystemUnderTest +import testframework.CustomTestFramework +import java.util.function.Function + +/** + * Runs the custom test framework from src/main with our agent attached (the agent is configured in this project's + * build.gradle). The custom test framework contains an integration via the tia-client against the + * [TeamscaleMockServer]. Asserts that the resulting report looks as expected. + * + * + * This ensures that our tia-client library doesn't break unexpectedly. + */ +class TiaClientSystemTest { + @BeforeEach + fun startFakeTeamscaleServer() { + if (teamscaleMockServer == null) { + teamscaleMockServer = TeamscaleMockServer(SystemTestUtils.TEAMSCALE_PORT).acceptingReportUploads() + .withImpactedTests("testFoo", "testBar") + } + teamscaleMockServer?.reset() + } + + @Test + @Throws(Exception::class) + fun systemTest() { + val customTestFramework = CustomTestFramework(SystemTestUtils.AGENT_PORT) + customTestFramework.runTestsWithTia() + + val report = teamscaleMockServer!!.getOnlyTestwiseCoverageReport("part") + assertThat(report.tests).hasSize(2) + assertAll({ + assertThat(report.tests) + .extracting { it.uniformPath } + .containsExactlyInAnyOrder("testBar", "testFoo") + assertThat(report.tests) + .extracting { it.result } + .containsExactlyInAnyOrder(ETestExecutionResult.FAILURE, ETestExecutionResult.PASSED) + assertThat(report.tests) + .extracting { it.coverage } + .containsExactlyInAnyOrder("SystemUnderTest.kt:4,9", "SystemUnderTest.kt:4,6") + }) + } + + @Test + @Throws(Exception::class) + fun systemTestWithSpecialCharacter() { + val agent = TiaAgent(false, "http://localhost:${SystemTestUtils.AGENT_PORT}".toHttpUrl()) + val testRun = agent.startTestRunWithoutTestSelection() + + val uniformPath = "my/strange;te st[(-+path!@#$%^&*(name" + val runningTest = testRun.startTest(uniformPath) + SystemUnderTest().foo() + runningTest.endTest(TestResultWithMessage(ETestExecutionResult.PASSED, "")) + + testRun.endTestRun(true) + + val report = teamscaleMockServer!!.getOnlyTestwiseCoverageReport("part") + assertThat(report.tests).hasSize(1) + assertThat(report.tests).element(0) + .extracting { it.uniformPath }.isEqualTo(uniformPath) + } + + companion object { + private var teamscaleMockServer: TeamscaleMockServer? = null + + @JvmStatic + @AfterAll + fun stopFakeTeamscaleServer() { + teamscaleMockServer!!.shutdown() + } + } +} diff --git a/system-tests/tia-client-tia-mode-http/build.gradle.kts b/system-tests/tia-client-tia-mode-http/build.gradle.kts index 05800f873..1ee7e3e26 100644 --- a/system-tests/tia-client-tia-mode-http/build.gradle.kts +++ b/system-tests/tia-client-tia-mode-http/build.gradle.kts @@ -1,4 +1,5 @@ plugins { + com.teamscale.`kotlin-convention` com.teamscale.`system-test-convention` } diff --git a/system-tests/tia-client-tia-mode-http/src/main/java/systemundertest/SystemUnderTest.java b/system-tests/tia-client-tia-mode-http/src/main/java/systemundertest/SystemUnderTest.java deleted file mode 100644 index 2adc6d0e6..000000000 --- a/system-tests/tia-client-tia-mode-http/src/main/java/systemundertest/SystemUnderTest.java +++ /dev/null @@ -1,15 +0,0 @@ -package systemundertest; - -/** Fake system under test that is exercised by the fake {@link testframework.CustomTestFramework}. */ -public class SystemUnderTest { - - /** Returns 5. */ - public int foo() { - return 5; - } - - /** Returns 6. */ - public int bar() { - return 6; - } -} diff --git a/system-tests/tia-client-tia-mode-http/src/main/java/testframework/CustomTestFramework.java b/system-tests/tia-client-tia-mode-http/src/main/java/testframework/CustomTestFramework.java deleted file mode 100644 index 066da68ef..000000000 --- a/system-tests/tia-client-tia-mode-http/src/main/java/testframework/CustomTestFramework.java +++ /dev/null @@ -1,60 +0,0 @@ -package testframework; - -import com.teamscale.report.testwise.model.ETestExecutionResult; -import com.teamscale.report.testwise.model.TestInfo; -import com.teamscale.tia.client.AgentHttpRequestFailedException; -import com.teamscale.tia.client.RunningTest; -import com.teamscale.tia.client.TestRun; -import com.teamscale.tia.client.TiaAgent; -import okhttp3.HttpUrl; -import systemundertest.SystemUnderTest; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** Simulates a custom test framework that doesn't rely on JUnit. */ -public class CustomTestFramework { - - private final Map allTests = new HashMap<>(); - public final List testInfos = new ArrayList<>(); - private final int agentPort; - - public CustomTestFramework(int agentPort) { - this.agentPort = agentPort; - allTests.put("testFoo", () -> { - if (new SystemUnderTest().foo() != 5) { - throw new AssertionError("Incorrect return value for foo"); - } - }); - allTests.put("testBar", () -> { - if (new SystemUnderTest().bar() != 7) { - throw new AssertionError("Incorrect return value for bar"); - } - }); - } - - /** Talks to the TIA agent and runs all impacted tests. Also uploads a coverage report at the end. */ - public void runTestsWithTia() throws AgentHttpRequestFailedException { - TiaAgent agent = new TiaAgent(false, HttpUrl.get("http://localhost:" + agentPort)); - TestRun testRun = agent.startTestRunWithoutTestSelection(); - - for (String uniformPath : allTests.keySet()) { - Runnable runnable = allTests.get(uniformPath); - RunningTest runningTest = testRun.startTest(uniformPath); - - TestInfo testInfo; - try { - runnable.run(); - testInfo = runningTest.endTestAndRetrieveCoverage( - new TestRun.TestResultWithMessage(ETestExecutionResult.PASSED, "")); - } catch (Throwable t) { - testInfo = runningTest.endTestAndRetrieveCoverage( - new TestRun.TestResultWithMessage(ETestExecutionResult.FAILURE, t.getMessage())); - } - testInfos.add(testInfo); - } - } - -} diff --git a/system-tests/tia-client-tia-mode-http/src/main/kotlin/systemundertest/SystemUnderTest.kt b/system-tests/tia-client-tia-mode-http/src/main/kotlin/systemundertest/SystemUnderTest.kt new file mode 100644 index 000000000..4c7b1c8c2 --- /dev/null +++ b/system-tests/tia-client-tia-mode-http/src/main/kotlin/systemundertest/SystemUnderTest.kt @@ -0,0 +1,10 @@ +package systemundertest + +/** Fake system under test that is exercised by the fake [testframework.CustomTestFramework]. */ +class SystemUnderTest { + /** Returns 5. */ + fun foo() = 5 + + /** Returns 6. */ + fun bar() = 6 +} diff --git a/system-tests/tia-client-tia-mode-http/src/main/kotlin/testframework/CustomTestFramework.kt b/system-tests/tia-client-tia-mode-http/src/main/kotlin/testframework/CustomTestFramework.kt new file mode 100644 index 000000000..194c72971 --- /dev/null +++ b/system-tests/tia-client-tia-mode-http/src/main/kotlin/testframework/CustomTestFramework.kt @@ -0,0 +1,54 @@ +package testframework + +import com.teamscale.report.testwise.model.ETestExecutionResult +import com.teamscale.report.testwise.model.TestInfo +import com.teamscale.tia.client.AgentHttpRequestFailedException +import com.teamscale.tia.client.TestRun.TestResultWithMessage +import com.teamscale.tia.client.TiaAgent +import okhttp3.HttpUrl +import okhttp3.HttpUrl.Companion.toHttpUrl +import systemundertest.SystemUnderTest + +/** Simulates a custom test framework that doesn't rely on JUnit. */ +class CustomTestFramework(private val agentPort: Int) { + private val allTests = mutableMapOf Unit>() + val testInfos = mutableListOf() + + init { + allTests["testFoo"] = { + if (SystemUnderTest().foo() != 5) { + throw AssertionError("Incorrect return value for foo") + } + } + allTests["testBar"] = { + if (SystemUnderTest().bar() != 7) { + throw AssertionError("Incorrect return value for bar") + } + } + } + + /** Talks to the TIA agent and runs all impacted tests. Also uploads a coverage report at the end. */ + @Throws(AgentHttpRequestFailedException::class) + fun runTestsWithTia() { + val agent = TiaAgent(false, "http://localhost:$agentPort".toHttpUrl()) + val testRun = agent.startTestRunWithoutTestSelection() + + allTests.keys.forEach { uniformPath -> + val runnable = allTests[uniformPath] + val runningTest = testRun.startTest(uniformPath) + + var testInfo: TestInfo + try { + runnable?.invoke() + testInfo = runningTest.endTestAndRetrieveCoverage( + TestResultWithMessage(ETestExecutionResult.PASSED, "") + ) + } catch (t: Throwable) { + testInfo = runningTest.endTestAndRetrieveCoverage( + TestResultWithMessage(ETestExecutionResult.FAILURE, t.message) + ) + } + testInfos.add(testInfo) + } + } +} diff --git a/system-tests/tia-client-tia-mode-http/src/test/java/com/teamscale/tia/client/TiaClientTiaModeHttpSystemTest.java b/system-tests/tia-client-tia-mode-http/src/test/java/com/teamscale/tia/client/TiaClientTiaModeHttpSystemTest.java deleted file mode 100644 index 06bf54ed8..000000000 --- a/system-tests/tia-client-tia-mode-http/src/test/java/com/teamscale/tia/client/TiaClientTiaModeHttpSystemTest.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.teamscale.tia.client; - -import com.teamscale.report.testwise.model.TestInfo; -import com.teamscale.test.commons.SystemTestUtils; -import org.junit.jupiter.api.Test; -import testframework.CustomTestFramework; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Runs the {@link CustomTestFramework} from src/main with our agent attached in tia-mode=http (the agent is configured - * in this project's build.gradle). The custom test framework contains an integration with the tia-client. Asserts that - * the resulting {@link TestInfo}s look as expected. - *

- * This ensures that our tia-client library doesn't break unexpectedly. - */ -public class TiaClientTiaModeHttpSystemTest { - - @Test - public void systemTest() throws Exception { - CustomTestFramework customTestFramework = new CustomTestFramework(SystemTestUtils.AGENT_PORT); - customTestFramework.runTestsWithTia(); - - assertThat(customTestFramework.testInfos.stream().map(SystemTestUtils::getCoverageString)) - .containsExactlyInAnyOrder("SystemUnderTest.java:4,13", "SystemUnderTest.java:4,8"); - } - -} diff --git a/system-tests/tia-client-tia-mode-http/src/test/kotlin/com/teamscale/tia/client/TiaClientTiaModeHttpSystemTest.kt b/system-tests/tia-client-tia-mode-http/src/test/kotlin/com/teamscale/tia/client/TiaClientTiaModeHttpSystemTest.kt new file mode 100644 index 000000000..824b812b7 --- /dev/null +++ b/system-tests/tia-client-tia-mode-http/src/test/kotlin/com/teamscale/tia/client/TiaClientTiaModeHttpSystemTest.kt @@ -0,0 +1,28 @@ +package com.teamscale.tia.client + +import com.teamscale.test.commons.SystemTestUtils +import com.teamscale.test.commons.SystemTestUtils.coverage +import org.assertj.core.api.Assertions +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import testframework.CustomTestFramework + +/** + * Runs the [CustomTestFramework] from src/main with our agent attached in tia-mode=http (the agent is configured + * in this project's build.gradle). The custom test framework contains an integration with the tia-client. Asserts that + * the resulting [TestInfo]s look as expected. + * + * + * This ensures that our tia-client library doesn't break unexpectedly. + */ +class TiaClientTiaModeHttpSystemTest { + @Test + @Throws(Exception::class) + fun systemTest() { + val customTestFramework = CustomTestFramework(SystemTestUtils.AGENT_PORT) + customTestFramework.runTestsWithTia() + + assertThat(customTestFramework.testInfos).extracting { it.coverage } + .containsExactlyInAnyOrder("SystemUnderTest.kt:4,6", "SystemUnderTest.kt:4,9") + } +} diff --git a/system-tests/tia-maven/build.gradle.kts b/system-tests/tia-maven/build.gradle.kts index 60c105c0d..9ca8c0b58 100644 --- a/system-tests/tia-maven/build.gradle.kts +++ b/system-tests/tia-maven/build.gradle.kts @@ -1,4 +1,5 @@ plugins { + com.teamscale.`kotlin-convention` com.teamscale.`system-test-convention` com.teamscale.coverage } diff --git a/system-tests/tia-maven/src/test/java/com/teamscale/tia/TiaMavenDumpToFileSystemTest.java b/system-tests/tia-maven/src/test/java/com/teamscale/tia/TiaMavenDumpToFileSystemTest.java deleted file mode 100644 index 20dd9cfee..000000000 --- a/system-tests/tia-maven/src/test/java/com/teamscale/tia/TiaMavenDumpToFileSystemTest.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.teamscale.tia; - -import com.google.common.collect.Iterables; -import com.teamscale.client.JsonUtils; -import com.teamscale.report.testwise.model.ETestExecutionResult; -import com.teamscale.report.testwise.model.TestwiseCoverageReport; -import com.teamscale.test.commons.SystemTestUtils; -import com.teamscale.test.commons.TeamscaleMockServer; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertAll; - -/** - * Runs several Maven projects' Surefire tests that have the agent attached and one of our JUnit run listeners enabled. - * Checks that this produces a correct coverage report. - */ -public class TiaMavenDumpToFileSystemTest { - - private static final String MAVEN_PROJECT_NAME = "maven-dump-local-project"; - - - private static TeamscaleMockServer teamscaleMockServer = null; - - @BeforeEach - public void startFakeTeamscaleServer() throws Exception { - if (teamscaleMockServer == null) { - teamscaleMockServer = new TeamscaleMockServer(SystemTestUtils.TEAMSCALE_PORT).disallowingStateChanges(); - } - } - - @AfterEach - public void stopFakeTeamscaleServer() { - teamscaleMockServer.shutdown(); - } - - @Test - public void testMavenTia() throws Exception { - SystemTestUtils.runMavenTests(MAVEN_PROJECT_NAME); - - TestwiseCoverageReport unitTestReport = parseDumpedCoverageReport("tia"); - assertThat(unitTestReport.tests).hasSize(2); - assertThat(unitTestReport.partial).isFalse(); - assertAll(() -> { - assertThat(unitTestReport.tests).extracting(test -> test.uniformPath) - .containsExactlyInAnyOrder("bar/UnitTest/utBla()", "bar/UnitTest/utFoo()"); - assertThat(unitTestReport.tests).extracting(test -> test.result) - .containsExactlyInAnyOrder(ETestExecutionResult.PASSED, ETestExecutionResult.PASSED); - assertThat(unitTestReport.tests).extracting(SystemTestUtils::getCoverageString) - .containsExactly("SUT.java:3,6-7", "SUT.java:3,10-11"); - }); - - TestwiseCoverageReport integrationTestReport = parseDumpedCoverageReport("tia-integration"); - assertThat(integrationTestReport.tests).hasSize(2); - assertAll(() -> { - assertThat(integrationTestReport.tests).extracting(test -> test.uniformPath) - .containsExactlyInAnyOrder("bar/IntegIT/itBla()", "bar/IntegIT/itFoo()"); - assertThat(integrationTestReport.tests).extracting(test -> test.result) - .containsExactlyInAnyOrder(ETestExecutionResult.PASSED, ETestExecutionResult.PASSED); - assertThat(integrationTestReport.tests).extracting(SystemTestUtils::getCoverageString) - .containsExactly("SUT.java:3,6-7", "SUT.java:3,10-11"); - }); - } - - private TestwiseCoverageReport parseDumpedCoverageReport(String folderName) throws IOException { - List files = SystemTestUtils.getReportFileNames(MAVEN_PROJECT_NAME, folderName); - return JsonUtils.deserialize(new String(Files.readAllBytes(Iterables.getOnlyElement(files))), - TestwiseCoverageReport.class); - } - -} diff --git a/system-tests/tia-maven/src/test/java/com/teamscale/tia/TiaMavenMultipleJobsTest.java b/system-tests/tia-maven/src/test/java/com/teamscale/tia/TiaMavenMultipleJobsTest.java deleted file mode 100644 index bc98d2d5a..000000000 --- a/system-tests/tia-maven/src/test/java/com/teamscale/tia/TiaMavenMultipleJobsTest.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.teamscale.tia; - -import com.teamscale.test.commons.SystemTestUtils; -import org.conqat.lib.commons.filesystem.FileSystemUtils; -import org.junit.jupiter.api.Test; - -import java.nio.file.Path; -import java.nio.file.Paths; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Test class to check if multiple maven plugins can be started with dynamic port allocation. - */ -public class TiaMavenMultipleJobsTest { - - /** - * Starts multiple Maven processes and checks that the ports are dynamically set and the servers are correctly - * started. - */ - @Test - public void testMavenTia() throws Exception { - String workingDirectory = "maven-dump-local-project"; - - // Clean once before testing parallel execution and make sure that the cleaning - // process is finished before testing. - SystemTestUtils.runMaven(workingDirectory, "clean"); - - // run three verify processes in parallel without waiting - for (int i = 0; i < 3; i++) { - SystemTestUtils.buildMavenProcess(workingDirectory, "verify").start(); - } - - // and one more that we wait for to terminate - SystemTestUtils.runMaven(workingDirectory, "verify"); - - Path configFile = Paths.get(workingDirectory, "target", "tia", "agent.log"); - String configContent = FileSystemUtils.readFile(configFile.toFile()); - assertThat(configContent).isNotEmpty().doesNotContain("Could not start http server on port"); - } -} diff --git a/system-tests/tia-maven/src/test/java/com/teamscale/tia/TiaMavenSystemTest.java b/system-tests/tia-maven/src/test/java/com/teamscale/tia/TiaMavenSystemTest.java deleted file mode 100644 index c784d467b..000000000 --- a/system-tests/tia-maven/src/test/java/com/teamscale/tia/TiaMavenSystemTest.java +++ /dev/null @@ -1,160 +0,0 @@ -package com.teamscale.tia; - -import com.teamscale.client.JsonUtils; -import com.teamscale.report.testwise.model.ETestExecutionResult; -import com.teamscale.report.testwise.model.TestwiseCoverageReport; -import com.teamscale.test.commons.Session; -import com.teamscale.test.commons.SystemTestUtils; -import com.teamscale.test.commons.TeamscaleMockServer; -import org.conqat.lib.commons.filesystem.FileSystemUtils; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Paths; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Runs several Maven projects' Surefire tests that have the agent attached, and one of our JUnit run listeners enabled. - * Checks that this produces a correct coverage report. - */ -public class TiaMavenSystemTest { - - private static TeamscaleMockServer teamscaleMockServer = null; - - @BeforeEach - public void startFakeTeamscaleServer() throws Exception { - teamscaleMockServer = new TeamscaleMockServer(SystemTestUtils.TEAMSCALE_PORT) - .withAuthentication("build", "6lJKEvNHeTxGPhMAi4D84DWqzoSFL1p4") - .acceptingReportUploads() - .withImpactedTests("org/example/UnitTest/utBla()", - "org/example/UnitTest/utFoo()", - "org/example/UnitB1Test/utBlub()", - "org/example/UnitB1Test/utGoo()", - "org/example/UnitB2Test/utBlub()", - "org/example/UnitB2Test/utGoo()", - "org/example/IntegrationIT/itBla()", - "org/example/IntegrationIT/itFoo()", - "org/example/IntegrationB1IT/itBlub()", - "org/example/IntegrationB1IT/itGoo()", - "org/example/IntegrationB2IT/itBlub()", - "org/example/IntegrationB2IT/itGoo()"); - } - - @AfterEach - public void stopFakeTeamscaleServer() { - teamscaleMockServer.shutdown(); - } - - @Test - public void testMavenTia() throws Exception { - SystemTestUtils.runMavenTests("maven-project", "-Dtia"); - - assertThat(teamscaleMockServer.allAvailableTests).extracting("partition") - .containsOnly("Unit Tests", "Integration Tests"); - - assertThat(teamscaleMockServer.getSessions()).hasSize(2); - Session unitTestSession = teamscaleMockServer.getSession("Unit Tests"); - Session integrationTestSession = teamscaleMockServer.getSession("Integration Tests"); - - assertThat(integrationTestSession.getCommit()).matches("abcd1337:myRepoId, .*"); - assertThat(teamscaleMockServer.impactedTestCommits.get(0)).matches("abcd1337:myRepoId, .*"); - assertThat(teamscaleMockServer.impactedTestCommits.get(1)).matches("abcd1337:myRepoId, .*"); - - TestwiseCoverageReport unitTestReport = unitTestSession.getOnlyTestwiseCoverageReport(); - assertThat(unitTestReport.partial).isTrue(); - checkExpectedUnitTestCoverage(unitTestReport); - - TestwiseCoverageReport integrationTestReport = integrationTestSession.getOnlyTestwiseCoverageReport(); - checkExpectedIntegrationTestCoverage(integrationTestReport); - } - - @Test - public void testPreferBranchAndTimestampOverRevisionWhenProvidedManually() throws IOException { - SystemTestUtils.runMavenTests("maven-project", "-DteamscaleRevision=abcd1337", - "-DteamscaleTimestamp=master:HEAD", "-Dtia"); - - assertThat(teamscaleMockServer.impactedTestCommits.get(0)).matches("abcd1337:myRepoId, null"); - assertThat(teamscaleMockServer.impactedTestCommits.get(1)).matches("abcd1337:myRepoId, null"); - assertThat(teamscaleMockServer.getSession("Unit Tests").getCommit()).matches("abcd1337:myRepoId, null"); - } - - @Test - public void testBaselineRevisionIsPreferred() throws IOException { - SystemTestUtils.runMavenTests("maven-project", "-DbaselineRevision=rev1", "-DbaselineCommit=master:1234", - "-Dtia"); - - assertThat(teamscaleMockServer.baselines).containsOnly("rev1, null"); - } - - @Test - public void testBaselineCommitIsUsed() throws IOException { - SystemTestUtils.runMavenTests("maven-project", "-DbaselineCommit=master:1234", "-Dtia"); - - assertThat(teamscaleMockServer.baselines).containsOnly("null, master:1234"); - } - - /** - * Starts a maven process with the reuseForks flag set to "false". Checks if the coverage can be converted to a - * testwise coverage report afterward. - */ - @Test - public void testMavenTiaWithoutReuseForks() throws Exception { - SystemTestUtils.runMavenTests("maven-project", "-Dtia", "-DreuseForks=false"); - File workingDirectory = new File("maven-project"); - File testwiseCoverage = new File( - Paths.get(workingDirectory.getAbsolutePath(), "coverage", "target", "tia", "reports", - "testwise-coverage-1.json") - .toUri()); - TestwiseCoverageReport testwiseCoverageReport = JsonUtils - .deserialize(FileSystemUtils.readFile(testwiseCoverage), TestwiseCoverageReport.class); - checkExpectedUnitTestCoverage(testwiseCoverageReport); - } - - private static void checkExpectedUnitTestCoverage(TestwiseCoverageReport testwiseCoverageReport) { - assertThat(testwiseCoverageReport).isNotNull(); - assertThat(testwiseCoverageReport.tests).extracting(test -> test.uniformPath).containsExactlyInAnyOrder( - "org/example/UnitTest/utBla()", - "org/example/UnitTest/utFoo()", - "org/example/UnitB1Test/utBlub()", - "org/example/UnitB1Test/utGoo()", - "org/example/UnitB2Test/utBlub()", - "org/example/UnitB2Test/utGoo()" - ); - assertThat(testwiseCoverageReport.tests).extracting(test -> test.result) - .allMatch(result -> result == ETestExecutionResult.PASSED); - assertThat(testwiseCoverageReport.tests).extracting(SystemTestUtils::getCoverageString).containsExactly( - "SUTA.java:7,10-11;UnitTest.java:10-11", - "SUTA.java:7,14-15;UnitTest.java:15-16", - "SUTB1.java:7,14-15;UnitB1Test.java:10-11", - "SUTB1.java:7,10-11;UnitB1Test.java:15-16", - // Ensure that sources from the project itself (SUT2), but also other dependent projects is considered (SUT1) - "SUTB1.java:7,14-15;SUTB2.java:7,14-15;UnitB2Test.java:10-12", - "SUTB1.java:7,10-11;SUTB2.java:7,10-11;UnitB2Test.java:16-18" - ); - } - - private static void checkExpectedIntegrationTestCoverage(TestwiseCoverageReport integrationTestReport) { - assertThat(integrationTestReport.tests).extracting(test -> test.uniformPath) - .containsExactlyInAnyOrder( - "org/example/IntegrationIT/itBla()", - "org/example/IntegrationIT/itFoo()", - "org/example/IntegrationB1IT/itBlub()", - "org/example/IntegrationB1IT/itGoo()", - "org/example/IntegrationB2IT/itBlub()", - "org/example/IntegrationB2IT/itGoo()" - ); - assertThat(integrationTestReport.tests).extracting(test -> test.result) - .allMatch(result -> result == ETestExecutionResult.PASSED); - assertThat(integrationTestReport.tests).extracting(SystemTestUtils::getCoverageString) - .containsExactly("IntegrationIT.java:10-11;SUTA.java:7,10-11", - "IntegrationIT.java:15-16;SUTA.java:7,14-15", - "IntegrationB1IT.java:10-11;SUTB1.java:7,14-15", - "IntegrationB1IT.java:15-16;SUTB1.java:7,10-11", - "IntegrationB2IT.java:10-11;SUTB2.java:7,14-15", - "IntegrationB2IT.java:15-16;SUTB2.java:7,10-11"); - } -} diff --git a/system-tests/tia-maven/src/test/kotlin/com/teamscale/tia/TiaMavenDumpToFileSystemTest.kt b/system-tests/tia-maven/src/test/kotlin/com/teamscale/tia/TiaMavenDumpToFileSystemTest.kt new file mode 100644 index 000000000..f456fbcd2 --- /dev/null +++ b/system-tests/tia-maven/src/test/kotlin/com/teamscale/tia/TiaMavenDumpToFileSystemTest.kt @@ -0,0 +1,90 @@ +package com.teamscale.tia + +import com.google.common.collect.Iterables +import com.teamscale.client.JsonUtils +import com.teamscale.report.testwise.model.ETestExecutionResult +import com.teamscale.report.testwise.model.TestInfo +import com.teamscale.report.testwise.model.TestwiseCoverageReport +import com.teamscale.test.commons.SystemTestUtils +import com.teamscale.test.commons.SystemTestUtils.coverage +import com.teamscale.test.commons.SystemTestUtils.getReportFileNames +import com.teamscale.test.commons.SystemTestUtils.runMavenTests +import com.teamscale.test.commons.TeamscaleMockServer +import org.assertj.core.api.Assertions +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.iterable.ThrowingExtractor +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Assertions.assertAll +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertAll +import org.junit.jupiter.api.function.Executable +import java.io.IOException +import java.nio.file.Files +import java.nio.file.Path + +/** + * Runs several Maven projects' Surefire tests that have the agent attached and one of our JUnit run listeners enabled. + * Checks that this produces a correct coverage report. + */ +class TiaMavenDumpToFileSystemTest { + private lateinit var teamscaleMockServer: TeamscaleMockServer + + @BeforeEach + @Throws(Exception::class) + fun startFakeTeamscaleServer() { + teamscaleMockServer = TeamscaleMockServer(SystemTestUtils.TEAMSCALE_PORT).disallowingStateChanges() + } + + @AfterEach + fun stopFakeTeamscaleServer() { + teamscaleMockServer.shutdown() + } + + @Test + @Throws(Exception::class) + fun testMavenTia() { + runMavenTests(MAVEN_PROJECT_NAME) + + val unitTestReport = parseDumpedCoverageReport("tia") + assertThat(unitTestReport.tests).hasSize(2) + assertThat(unitTestReport.partial).isFalse() + assertAll({ + assertThat(unitTestReport.tests) + .extracting { it.uniformPath } + .containsExactlyInAnyOrder("bar/UnitTest/utBla()", "bar/UnitTest/utFoo()") + assertThat(unitTestReport.tests) + .extracting { it.result } + .containsExactlyInAnyOrder(ETestExecutionResult.PASSED, ETestExecutionResult.PASSED) + assertThat(unitTestReport.tests) + .extracting { it.coverage } + .containsExactly("SUT.java:3,6-7", "SUT.java:3,10-11") + }) + + val integrationTestReport = parseDumpedCoverageReport("tia-integration") + assertThat(integrationTestReport.tests).hasSize(2) + assertAll({ + assertThat(integrationTestReport.tests) + .extracting { it.uniformPath } + .containsExactlyInAnyOrder("bar/IntegIT/itBla()", "bar/IntegIT/itFoo()") + assertThat(integrationTestReport.tests) + .extracting { it.result } + .containsExactlyInAnyOrder(ETestExecutionResult.PASSED, ETestExecutionResult.PASSED) + assertThat(integrationTestReport.tests) + .extracting { it.coverage } + .containsExactly("SUT.java:3,6-7", "SUT.java:3,10-11") + }) + } + + @Throws(IOException::class) + private fun parseDumpedCoverageReport(folderName: String): TestwiseCoverageReport { + val files = getReportFileNames(MAVEN_PROJECT_NAME, folderName) + return JsonUtils.deserialize( + String(Files.readAllBytes(Iterables.getOnlyElement(files))) + ) + } + + companion object { + private const val MAVEN_PROJECT_NAME = "maven-dump-local-project" + } +} diff --git a/system-tests/tia-maven/src/test/kotlin/com/teamscale/tia/TiaMavenMultipleJobsTest.kt b/system-tests/tia-maven/src/test/kotlin/com/teamscale/tia/TiaMavenMultipleJobsTest.kt new file mode 100644 index 000000000..96d439eed --- /dev/null +++ b/system-tests/tia-maven/src/test/kotlin/com/teamscale/tia/TiaMavenMultipleJobsTest.kt @@ -0,0 +1,39 @@ +package com.teamscale.tia + +import com.teamscale.test.commons.SystemTestUtils.buildMavenProcess +import com.teamscale.test.commons.SystemTestUtils.runMaven +import org.assertj.core.api.Assertions +import org.conqat.lib.commons.filesystem.FileSystemUtils +import org.junit.jupiter.api.Test +import java.nio.file.Paths + +/** + * Test class to check if multiple maven plugins can be started with dynamic port allocation. + */ +class TiaMavenMultipleJobsTest { + /** + * Starts multiple Maven processes and checks that the ports are dynamically set and the servers are correctly + * started. + */ + @Test + @Throws(Exception::class) + fun testMavenTia() { + val workingDirectory = "maven-dump-local-project" + + // Clean once before testing parallel execution and make sure that the cleaning + // process is finished before testing. + runMaven(workingDirectory, "clean") + + // run three verify processes in parallel without waiting + repeat(3) { + buildMavenProcess(workingDirectory, "verify").start() + } + + // and one more that we wait for to terminate + runMaven(workingDirectory, "verify") + + val configFile = Paths.get(workingDirectory, "target", "tia", "agent.log") + val configContent = configFile.toFile().readText() + Assertions.assertThat(configContent).isNotEmpty().doesNotContain("Could not start http server on port") + } +} diff --git a/system-tests/tia-maven/src/test/kotlin/com/teamscale/tia/TiaMavenSystemTest.kt b/system-tests/tia-maven/src/test/kotlin/com/teamscale/tia/TiaMavenSystemTest.kt new file mode 100644 index 000000000..ed27477ab --- /dev/null +++ b/system-tests/tia-maven/src/test/kotlin/com/teamscale/tia/TiaMavenSystemTest.kt @@ -0,0 +1,178 @@ +package com.teamscale.tia + +import com.teamscale.client.JsonUtils +import com.teamscale.report.testwise.model.ETestExecutionResult +import com.teamscale.report.testwise.model.TestwiseCoverageReport +import com.teamscale.test.commons.SystemTestUtils +import com.teamscale.test.commons.SystemTestUtils.coverage +import com.teamscale.test.commons.SystemTestUtils.runMavenTests +import com.teamscale.test.commons.TeamscaleMockServer +import org.assertj.core.api.Assertions.assertThat +import org.conqat.lib.commons.filesystem.FileSystemUtils +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import java.io.File +import java.io.IOException +import java.nio.file.Paths + +/** + * Runs several Maven projects' Surefire tests that have the agent attached, and one of our JUnit run listeners enabled. + * Checks that this produces a correct coverage report. + */ +class TiaMavenSystemTest { + private lateinit var teamscaleMockServer: TeamscaleMockServer + + @BeforeEach + @Throws(Exception::class) + fun startFakeTeamscaleServer() { + teamscaleMockServer = TeamscaleMockServer(SystemTestUtils.TEAMSCALE_PORT) + .withAuthentication("build", "6lJKEvNHeTxGPhMAi4D84DWqzoSFL1p4") + .acceptingReportUploads() + .withImpactedTests( + "org/example/UnitTest/utBla()", + "org/example/UnitTest/utFoo()", + "org/example/UnitB1Test/utBlub()", + "org/example/UnitB1Test/utGoo()", + "org/example/UnitB2Test/utBlub()", + "org/example/UnitB2Test/utGoo()", + "org/example/IntegrationIT/itBla()", + "org/example/IntegrationIT/itFoo()", + "org/example/IntegrationB1IT/itBlub()", + "org/example/IntegrationB1IT/itGoo()", + "org/example/IntegrationB2IT/itBlub()", + "org/example/IntegrationB2IT/itGoo()" + ) + } + + @AfterEach + fun stopFakeTeamscaleServer() { + teamscaleMockServer.shutdown() + } + + @Test + @Throws(Exception::class) + fun testMavenTia() { + runMavenTests("maven-project", "-Dtia") + + assertThat(teamscaleMockServer.allAvailableTests).extracting("partition") + .containsOnly("Unit Tests", "Integration Tests") + + assertThat(teamscaleMockServer.getSessions()).hasSize(2) + val unitTestSession = teamscaleMockServer.getSession("Unit Tests") + val integrationTestSession = teamscaleMockServer.getSession("Integration Tests") + + assertThat(integrationTestSession.getCommit()).matches("abcd1337:myRepoId, .*") + assertThat(teamscaleMockServer.impactedTestCommits[0]).matches("abcd1337:myRepoId, .*") + assertThat(teamscaleMockServer.impactedTestCommits[1]).matches("abcd1337:myRepoId, .*") + + val unitTestReport = unitTestSession.onlyTestwiseCoverageReport + assertThat(unitTestReport.partial).isTrue() + checkExpectedUnitTestCoverage(unitTestReport) + checkExpectedIntegrationTestCoverage(integrationTestSession.onlyTestwiseCoverageReport) + } + + @Test + @Throws(IOException::class) + fun testPreferBranchAndTimestampOverRevisionWhenProvidedManually() { + runMavenTests( + "maven-project", "-DteamscaleRevision=abcd1337", + "-DteamscaleTimestamp=master:HEAD", "-Dtia" + ) + + assertThat(teamscaleMockServer.impactedTestCommits[0]).matches("abcd1337:myRepoId, null") + assertThat(teamscaleMockServer.impactedTestCommits[1]).matches("abcd1337:myRepoId, null") + assertThat(teamscaleMockServer.getSession("Unit Tests").getCommit()) + .matches("abcd1337:myRepoId, null") + } + + @Test + @Throws(IOException::class) + fun testBaselineRevisionIsPreferred() { + runMavenTests( + "maven-project", "-DbaselineRevision=rev1", "-DbaselineCommit=master:1234", "-Dtia" + ) + + assertThat(teamscaleMockServer.baselines).containsOnly("rev1, null") + } + + @Test + @Throws(IOException::class) + fun testBaselineCommitIsUsed() { + runMavenTests("maven-project", "-DbaselineCommit=master:1234", "-Dtia") + + assertThat(teamscaleMockServer.baselines).containsOnly("null, master:1234") + } + + /** + * Starts a maven process with the reuseForks flag set to "false". Checks if the coverage can be converted to a + * testwise coverage report afterward. + */ + @Test + @Throws(Exception::class) + fun testMavenTiaWithoutReuseForks() { + runMavenTests("maven-project", "-Dtia", "-DreuseForks=false") + val workingDirectory = File("maven-project") + val testwiseCoverage = File( + Paths.get(workingDirectory.absolutePath, "coverage", "target", "tia", "reports", "testwise-coverage-1.json").toUri() + ) + val testwiseCoverageReport = JsonUtils.deserialize( + FileSystemUtils.readFile(testwiseCoverage) + ) + checkExpectedUnitTestCoverage(testwiseCoverageReport) + } + + companion object { + private fun checkExpectedUnitTestCoverage(testwiseCoverageReport: TestwiseCoverageReport?) { + assertThat(testwiseCoverageReport).isNotNull() + assertThat(testwiseCoverageReport!!.tests) + .extracting { it.uniformPath } + .containsExactlyInAnyOrder( + "org/example/UnitTest/utBla()", + "org/example/UnitTest/utFoo()", + "org/example/UnitB1Test/utBlub()", + "org/example/UnitB1Test/utGoo()", + "org/example/UnitB2Test/utBlub()", + "org/example/UnitB2Test/utGoo()" + ) + assertThat(testwiseCoverageReport.tests) + .extracting { it.result } + .allMatch { it == ETestExecutionResult.PASSED } + assertThat(testwiseCoverageReport.tests) + .extracting {it.coverage}.containsExactly( + "SUTA.java:7,10-11;UnitTest.java:10-11", + "SUTA.java:7,14-15;UnitTest.java:15-16", + "SUTB1.java:7,14-15;UnitB1Test.java:10-11", + "SUTB1.java:7,10-11;UnitB1Test.java:15-16", // Ensure that sources from the project itself (SUT2), but also other dependent projects is considered (SUT1) + "SUTB1.java:7,14-15;SUTB2.java:7,14-15;UnitB2Test.java:10-12", + "SUTB1.java:7,10-11;SUTB2.java:7,10-11;UnitB2Test.java:16-18" + ) + } + + private fun checkExpectedIntegrationTestCoverage(integrationTestReport: TestwiseCoverageReport) { + assertThat(integrationTestReport.tests) + .extracting { it.uniformPath } + .containsExactlyInAnyOrder( + "org/example/IntegrationIT/itBla()", + "org/example/IntegrationIT/itFoo()", + "org/example/IntegrationB1IT/itBlub()", + "org/example/IntegrationB1IT/itGoo()", + "org/example/IntegrationB2IT/itBlub()", + "org/example/IntegrationB2IT/itGoo()" + ) + assertThat(integrationTestReport.tests) + .extracting { it.result } + .allMatch { it == ETestExecutionResult.PASSED } + assertThat(integrationTestReport.tests) + .extracting { it.coverage } + .containsExactly( + "IntegrationIT.java:10-11;SUTA.java:7,10-11", + "IntegrationIT.java:15-16;SUTA.java:7,14-15", + "IntegrationB1IT.java:10-11;SUTB1.java:7,14-15", + "IntegrationB1IT.java:15-16;SUTB1.java:7,10-11", + "IntegrationB2IT.java:10-11;SUTB2.java:7,14-15", + "IntegrationB2IT.java:15-16;SUTB2.java:7,10-11" + ) + } + } +} diff --git a/teamscale-client/src/main/kotlin/com/teamscale/client/JsonUtils.kt b/teamscale-client/src/main/kotlin/com/teamscale/client/JsonUtils.kt index 67a269f4e..fc504b623 100644 --- a/teamscale-client/src/main/kotlin/com/teamscale/client/JsonUtils.kt +++ b/teamscale-client/src/main/kotlin/com/teamscale/client/JsonUtils.kt @@ -40,6 +40,14 @@ object JsonUtils { fun deserialize(json: String, clazz: Class): T = OBJECT_MAPPER.readValue(json, clazz) + /** + * Deserializes a JSON string into an object of the given class. + */ + @Throws(JsonProcessingException::class) + @JvmStatic + inline fun deserialize(json: String): T = + OBJECT_MAPPER.readValue(json, T::class.java) + /** * Deserializes the contents of the given file into an object of the given class. */ @@ -52,9 +60,9 @@ object JsonUtils { */ @Throws(JsonProcessingException::class) @JvmStatic - fun deserializeList(json: String, elementClass: Class): List = + inline fun deserializeList(json: String): List = OBJECT_MAPPER.readValue( - json, OBJECT_MAPPER.typeFactory.constructCollectionLikeType(MutableList::class.java, elementClass) + json, OBJECT_MAPPER.typeFactory.constructCollectionLikeType(MutableList::class.java, T::class.java) ) /** @@ -62,8 +70,8 @@ object JsonUtils { */ @JvmStatic @Throws(JsonProcessingException::class) - fun serialize(value: Any): String = - OBJECT_MAPPER.writeValueAsString(value) + fun Any.serializeToJson(): String = + OBJECT_MAPPER.writeValueAsString(this) /** * Serializes an object to a file with pretty printing enabled. diff --git a/teamscale-gradle-plugin/src/test/kotlin/com/teamscale/CommitHandlingTest.kt b/teamscale-gradle-plugin/src/test/kotlin/com/teamscale/CommitHandlingTest.kt index 8fdaa5e88..2e7a65464 100644 --- a/teamscale-gradle-plugin/src/test/kotlin/com/teamscale/CommitHandlingTest.kt +++ b/teamscale-gradle-plugin/src/test/kotlin/com/teamscale/CommitHandlingTest.kt @@ -27,7 +27,7 @@ class CommitHandlingTest : TeamscalePluginTestBase() { "-DrunAllTests", "unitTestReportUpload" ) - assertThat(teamscaleMockServer.onlySession.commit).contains("null:myRepoId, master:1544512967526") + assertThat(teamscaleMockServer.onlySession.getCommit()).contains("null:myRepoId, master:1544512967526") } @Test @@ -40,7 +40,7 @@ class CommitHandlingTest : TeamscalePluginTestBase() { "-DrunAllTests", "unitTestReportUpload" ) - assertThat(teamscaleMockServer.onlySession.commit).contains("abcd1337:myRepoId, null") + assertThat(teamscaleMockServer.onlySession.getCommit()).contains("abcd1337:myRepoId, null") } @Test diff --git a/teamscale-gradle-plugin/src/test/kotlin/com/teamscale/TeamscalePluginTestwiseCoverageTest.kt b/teamscale-gradle-plugin/src/test/kotlin/com/teamscale/TeamscalePluginTestwiseCoverageTest.kt index d5daf4acc..3b916a244 100644 --- a/teamscale-gradle-plugin/src/test/kotlin/com/teamscale/TeamscalePluginTestwiseCoverageTest.kt +++ b/teamscale-gradle-plugin/src/test/kotlin/com/teamscale/TeamscalePluginTestwiseCoverageTest.kt @@ -97,7 +97,7 @@ class TeamscalePluginTestwiseCoverageTest : TeamscalePluginTestBase() { } private fun assertFullCoverage(source: String) { - val testwiseCoverageReport = JsonUtils.deserialize(source, TestwiseCoverageReport::class.java) + val testwiseCoverageReport = JsonUtils.deserialize(source) assertThat(testwiseCoverageReport) .hasPartial(false) .containsExecutionResult("com/example/project/IgnoredJUnit4Test/systemTest", ETestExecutionResult.SKIPPED) @@ -121,7 +121,7 @@ class TeamscalePluginTestwiseCoverageTest : TeamscalePluginTestBase() { } private fun assertPartialCoverage(source: String) { - val testwiseCoverageReport = JsonUtils.deserialize(source, TestwiseCoverageReport::class.java) + val testwiseCoverageReport = JsonUtils.deserialize(source) assertThat(testwiseCoverageReport) .hasPartial(true) .containsExecutionResult("com/example/project/JUnit4Test/systemTest", ETestExecutionResult.PASSED) diff --git a/tia-client/src/main/kotlin/com/teamscale/tia/client/CommandLineInterface.kt b/tia-client/src/main/kotlin/com/teamscale/tia/client/CommandLineInterface.kt index 29a270f6f..139b0ceb1 100644 --- a/tia-client/src/main/kotlin/com/teamscale/tia/client/CommandLineInterface.kt +++ b/tia-client/src/main/kotlin/com/teamscale/tia/client/CommandLineInterface.kt @@ -2,7 +2,7 @@ package com.teamscale.tia.client import com.teamscale.client.ClusteredTestDetails import com.teamscale.client.JsonUtils.deserializeList -import com.teamscale.client.JsonUtils.serialize +import com.teamscale.client.JsonUtils.serializeToJson import com.teamscale.client.StringUtils.isEmpty import com.teamscale.report.testwise.model.ETestExecutionResult import com.teamscale.report.testwise.model.TestExecution @@ -108,7 +108,7 @@ class CommandLineInterface(arguments: Array) { ) { api.testRunStarted(includeNonImpacted, baseline, baselineRevision, availableTests) }?.let { - println(serialize(it)) + println(it.serializeToJson()) } } @@ -117,10 +117,7 @@ class CommandLineInterface(arguments: Array) { val json = readStdin() var availableTests = emptyList() if (!isEmpty(json)) { - availableTests = deserializeList( - json, - ClusteredTestDetails::class.java - ) + availableTests = deserializeList(json) } return availableTests } diff --git a/tia-client/src/main/kotlin/com/teamscale/tia/client/RunningTest.kt b/tia-client/src/main/kotlin/com/teamscale/tia/client/RunningTest.kt index 444883be7..c6983f3cb 100644 --- a/tia-client/src/main/kotlin/com/teamscale/tia/client/RunningTest.kt +++ b/tia-client/src/main/kotlin/com/teamscale/tia/client/RunningTest.kt @@ -102,7 +102,7 @@ class RunningTest(private val uniformPath: String, private val api: ITestwiseCov } try { - return deserialize(json, TestInfo::class.java) + return deserialize(json) } catch (e: IOException) { throw AgentHttpRequestFailedException( "Unable to parse the JSON returned by the agent. Maybe you have" +