Skip to content

Commit

Permalink
Add the ability to select the test type to run
Browse files Browse the repository at this point in the history
Also makes sure unit tests run before @QuarkusTest
based tests, and fixes a dev mode startup time
regression.
  • Loading branch information
stuartwdouglas committed Apr 26, 2021
1 parent ed1e899 commit 2558fe4
Show file tree
Hide file tree
Showing 10 changed files with 160 additions and 15 deletions.
Expand Up @@ -6,7 +6,7 @@
import org.aesh.readline.tty.terminal.TerminalConnection;
import org.aesh.terminal.Connection;

import io.quarkus.deployment.TestConfig;
import io.quarkus.deployment.dev.testing.TestConfig;
import io.quarkus.dev.console.BasicConsole;
import io.quarkus.dev.console.QuarkusConsole;

Expand Down
Expand Up @@ -107,6 +107,7 @@ public class JunitTestRunner {
private final Pattern exclude;
private final boolean displayInConsole;
private final boolean failingTestsOnly;
private final TestType testType;

private volatile boolean testsRunning = false;
private volatile boolean aborted;
Expand All @@ -127,6 +128,7 @@ public JunitTestRunner(Builder builder) {
this.exclude = builder.exclude;
this.displayInConsole = builder.displayInConsole;
this.failingTestsOnly = builder.failingTestsOnly;
this.testType = builder.testType;
}

public void runTests() {
Expand Down Expand Up @@ -472,15 +474,16 @@ private DiscoveryResult discoverTestClasses(DevModeContext devModeContext) {
unitTestClasses.add(name);
}

List<Class<?>> qtClasses = new ArrayList<>();
List<Class<?>> itClasses = new ArrayList<>();
List<Class<?>> utClasses = new ArrayList<>();
for (String i : quarkusTestClasses) {
try {
qtClasses.add(Thread.currentThread().getContextClassLoader().loadClass(i));
itClasses.add(Thread.currentThread().getContextClassLoader().loadClass(i));
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
qtClasses.sort(Comparator.comparing(new Function<Class<?>, String>() {
itClasses.sort(Comparator.comparing(new Function<Class<?>, String>() {
@Override
public String apply(Class<?> aClass) {
ClassInfo def = index.getClassByName(DotName.createSimple(aClass.getName()));
Expand Down Expand Up @@ -516,14 +519,26 @@ public String apply(Class<?> aClass) {
cl = testApplication.createRuntimeClassLoader(Collections.emptyMap(), transformedClasses);
for (String i : unitTestClasses) {
try {
qtClasses.add(cl.loadClass(i));
utClasses.add(cl.loadClass(i));
} catch (ClassNotFoundException exception) {
throw new RuntimeException(exception);
}
}

}
return new DiscoveryResult(cl, qtClasses);
if (testType == TestType.ALL) {
//run unit style tests first
//before the quarkus tests have started
//which stops quarkus interfering with WireMock
List<Class<?>> ret = new ArrayList<>(utClasses.size() + itClasses.size());
ret.addAll(utClasses);
ret.addAll(itClasses);
return new DiscoveryResult(cl, ret);
} else if (testType == TestType.UNIT) {
return new DiscoveryResult(cl, utClasses);
} else {
return new DiscoveryResult(cl, itClasses);
}
}

private static Set<DotName> collectTestAnnotations(Index index) {
Expand Down Expand Up @@ -602,6 +617,7 @@ public boolean test(String logRecord) {
}

static class Builder {
private TestType testType = TestType.ALL;
private TestState testState;
private long runId = -1;
private DevModeContext devModeContext;
Expand All @@ -622,6 +638,11 @@ public Builder setRunId(long runId) {
return this;
}

public Builder setTestType(TestType testType) {
this.testType = testType;
return this;
}

public Builder setDevModeContext(DevModeContext devModeContext) {
this.devModeContext = devModeContext;
return this;
Expand Down
@@ -1,4 +1,4 @@
package io.quarkus.deployment;
package io.quarkus.deployment.dev.testing;

import java.time.Duration;
import java.util.List;
Expand Down Expand Up @@ -134,6 +134,17 @@ public class TestConfig {
@ConfigItem(defaultValue = "10m")
Duration hangDetectionTimeout;

/**
* The type of test to run, this can be either:
*
* quarkus-test: Only runs {@code @QuarkusTest} annotated test classes
* unit: Only runs classes that are not annotated with {@code @QuarkusTest}
* all: Runs both, running the unit tests first
*
*/
@ConfigItem(defaultValue = "all")
TestType testType;

@ConfigGroup
public static class Profile {

Expand All @@ -157,6 +168,5 @@ public enum Mode {
PAUSED,
ENABLED,
DISABLED

}
}
Expand Up @@ -25,6 +25,7 @@
import io.quarkus.bootstrap.app.CuratedApplication;
import io.quarkus.deployment.dev.ClassScanResult;
import io.quarkus.deployment.dev.DevModeContext;
import io.quarkus.runtime.configuration.HyphenateEnumConverter;

public class TestRunner {

Expand Down Expand Up @@ -55,6 +56,7 @@ public class TestRunner {
String appPropertiesExcludeTags;
String appPropertiesIncludePattern;
String appPropertiesExcludePattern;
String appPropertiesTestType;

public TestRunner(TestSupport testSupport, DevModeContext devModeContext, CuratedApplication testApplication) {
this.testSupport = testSupport;
Expand Down Expand Up @@ -85,9 +87,6 @@ private void runTests(ClassScanResult classScanResult, boolean reRunFailures) {
if (compileProblem != null) {
return;
}
if (testApplication == null) {
return;
}
if (disabled) {
return;
}
Expand Down Expand Up @@ -207,6 +206,7 @@ private void runInternal(ClassScanResult classScanResult, boolean reRunFailures)
.setExcludeTags(testSupport.excludeTags)
.setInclude(testSupport.include)
.setExclude(testSupport.exclude)
.setTestType(testSupport.testType)
.setFailingTestsOnly(testSupport.failingTestsOnly);
if (reRunFailures) {
Set<UniqueId> ids = new HashSet<>();
Expand Down Expand Up @@ -277,6 +277,7 @@ private void handleApplicationPropertiesChange() {
String excludeTags = p.getProperty("quarkus.test.exclude-tags");
String includePattern = p.getProperty("quarkus.test.include-pattern");
String excludePattern = p.getProperty("quarkus.test.exclude-pattern");
String testType = p.getProperty("quarkus.test.test-type");
if (!firstRun) {
if (!Objects.equals(includeTags, appPropertiesIncludeTags)) {
if (includeTags == null) {
Expand Down Expand Up @@ -308,11 +309,26 @@ private void handleApplicationPropertiesChange() {
testSupport.exclude = Pattern.compile(excludePattern);
}
}
if (!Objects.equals(excludePattern, appPropertiesExcludePattern)) {
if (excludePattern == null) {
testSupport.exclude = null;
} else {
testSupport.exclude = Pattern.compile(excludePattern);
}
}
if (!Objects.equals(testType, appPropertiesTestType)) {
if (testType == null) {
testSupport.testType = TestType.ALL;
} else {
testSupport.testType = new HyphenateEnumConverter<>(TestType.class).convert(testType);
}
}
}
appPropertiesIncludeTags = includeTags;
appPropertiesExcludeTags = excludeTags;
appPropertiesIncludePattern = includePattern;
appPropertiesExcludePattern = excludePattern;
appPropertiesTestType = testType;
break;
}
}
Expand Down
Expand Up @@ -39,6 +39,7 @@ public class TestSupport implements TestController {
volatile boolean displayTestOutput;
volatile Boolean explicitDisplayTestOutput;
volatile boolean failingTestsOnly;
volatile TestType testType = TestType.ALL;

public TestSupport(CuratedApplication curatedApplication, List<CompilationProvider> compilationProviders,
DevModeContext context) {
Expand Down Expand Up @@ -142,7 +143,6 @@ public synchronized void stop() {
}
}
if (testRunner != null) {

testRunner.disable();
}
}
Expand Down Expand Up @@ -216,6 +216,11 @@ public TestSupport setConfiguredDisplayTestOutput(boolean displayTestOutput) {
return this;
}

public TestSupport setTestType(TestType testType) {
this.testType = testType;
return this;
}

@Override
public TestState currentState() {
return testState;
Expand Down
Expand Up @@ -14,7 +14,6 @@
import io.quarkus.deployment.IsDevelopment;
import io.quarkus.deployment.IsNormal;
import io.quarkus.deployment.IsTest;
import io.quarkus.deployment.TestConfig;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.Produce;
Expand Down Expand Up @@ -82,11 +81,11 @@ ServiceStartBuildItem startTesting(TestConfig config, LiveReloadBuildItem liveRe
testSupport.setPatterns(config.includePattern.orElse(null),
config.excludePattern.orElse(null));
testSupport.setConfiguredDisplayTestOutput(config.displayTestOutput);
testSupport.setTestType(config.testType);
if (!liveReloadBuildItem.isLiveReload()) {
if (config.continuousTesting == TestConfig.Mode.ENABLED) {
testSupport.start();
} else if (config.continuousTesting == TestConfig.Mode.PAUSED) {
testSupport.init();
testSupport.stop();
}
}
Expand Down
@@ -0,0 +1,7 @@
package io.quarkus.deployment.dev.testing;

public enum TestType {
UNIT,
QUARKUS_TEST,
ALL
}
@@ -0,0 +1,43 @@
package io.quarkus.vertx.http.testrunner;

import java.util.function.Supplier;

import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusDevModeTest;
import io.quarkus.vertx.http.deployment.devmode.tests.TestStatus;

public class QuarkusTestTypeTestCase {

@RegisterExtension
static QuarkusDevModeTest test = new QuarkusDevModeTest()
.setArchiveProducer(new Supplier<JavaArchive>() {
@Override
public JavaArchive get() {
return ShrinkWrap.create(JavaArchive.class).addClass(HelloResource.class)
.add(new StringAsset(TestRunnerTestUtils.appProperties("quarkus.test.test-type=quarkus-test")),
"application.properties");
}
})
.setTestArchiveProducer(new Supplier<JavaArchive>() {
@Override
public JavaArchive get() {
return ShrinkWrap.create(JavaArchive.class).addClasses(SimpleET.class, UnitET.class);
}
});

@Test
public void testQuarkusTestMode() throws InterruptedException {
TestStatus ts = TestRunnerTestUtils.waitForFirstRunToComplete();
Assertions.assertEquals(1L, ts.getLastRun());
Assertions.assertEquals(1L, ts.getTestsFailed());
Assertions.assertEquals(1L, ts.getTestsPassed());
Assertions.assertEquals(0L, ts.getTestsSkipped());
Assertions.assertEquals(-1L, ts.getRunning());
}
}
Expand Up @@ -40,7 +40,7 @@ public Boolean call() throws Exception {
}

public static String appProperties(String... props) {
return "quarkus.test.continuous-testing=enabled\nquarkus.test.console=false\nquarkus.test.display-test-output=true\n"
return "quarkus.test.continuous-testing=enabled\nquarkus.test.display-test-output=true\nquarkus.test.basic-console=true\n"
+ String.join("\n", Arrays.asList(props));
}
}
@@ -0,0 +1,44 @@
package io.quarkus.vertx.http.testrunner;

import java.util.function.Supplier;

import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusDevModeTest;
import io.quarkus.vertx.http.deployment.devmode.tests.TestStatus;

public class UnitTestTypeTestCase {

@RegisterExtension
static QuarkusDevModeTest test = new QuarkusDevModeTest()
.setArchiveProducer(new Supplier<JavaArchive>() {
@Override
public JavaArchive get() {
return ShrinkWrap.create(JavaArchive.class).addClass(HelloResource.class)
.add(new StringAsset(TestRunnerTestUtils.appProperties("quarkus.test.test-type=unit")),
"application.properties");
}
})
.setTestArchiveProducer(new Supplier<JavaArchive>() {
@Override
public JavaArchive get() {
return ShrinkWrap.create(JavaArchive.class).addClasses(SimpleET.class, UnitET.class);
}
});

@Test
public void testUnitMode() throws InterruptedException {
TestStatus ts = TestRunnerTestUtils.waitForFirstRunToComplete();
Assertions.assertEquals(1L, ts.getLastRun());
Assertions.assertEquals(1L, ts.getTestsFailed());
Assertions.assertEquals(0L, ts.getTestsPassed());
Assertions.assertEquals(0L, ts.getTestsSkipped());
Assertions.assertEquals(-1L, ts.getRunning());

}
}

0 comments on commit 2558fe4

Please sign in to comment.