Skip to content

Commit

Permalink
Unification of all test-framework-related options
Browse files Browse the repository at this point in the history
Since all of them are now gathered in one place
it would become easier to monitor and document them
Required for potential automation of #369
  • Loading branch information
fedinskiy committed Jan 29, 2025
1 parent df1a86a commit 36f0cc1
Show file tree
Hide file tree
Showing 36 changed files with 290 additions and 148 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package io.quarkus.qe.debug;

import static io.quarkus.test.configuration.Configuration.Property.RUN_TESTS;
import static io.quarkus.test.debug.SureFireDebugProvider.APP_IS_READ_PRESS_ENTER_TO_EXIT;
import static io.quarkus.test.debug.SureFireDebugProvider.RUN_TESTS;
import static io.quarkus.test.debug.SureFireDebugProvider.TEST_RUN_SUCCESS;
import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;
Expand Down Expand Up @@ -35,7 +35,7 @@ public void smokeTest() throws Exception {
final RunningInvoker invoker = new RunningInvoker(testDir, false);
invoker.setMavenExecutable(new File("../../mvnw").getAbsoluteFile());
final MavenProcessInvocationResult result = invoker.execute(Arrays.asList("clean", "verify", "-Dts.debug",
"-D" + RUN_TESTS),
"-D" + RUN_TESTS.getName()),
Collections.emptyMap());

// wait till app is ready for debugging and press enter
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.quarkus.test.bootstrap;

import static io.quarkus.test.configuration.Configuration.Property.CLI_CMD;
import static io.quarkus.test.services.quarkus.model.QuarkusProperties.createDisableBuildAnalyticsProperty;
import static org.junit.jupiter.api.Assertions.assertTrue;

Expand Down Expand Up @@ -30,7 +31,7 @@ public class QuarkusCliClient {
private static final String QUARKUS_UPSTREAM_VERSION = "999-SNAPSHOT";
private static final String BUILD = "build";
private static final String DEV = "dev";
private static final PropertyLookup COMMAND = new PropertyLookup("ts.quarkus.cli.cmd", "quarkus");
private static final PropertyLookup COMMAND = new PropertyLookup(CLI_CMD.getName(), "quarkus");
private static final Path TARGET = Paths.get("target");

private final ScenarioContext context;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package io.quarkus.test.services.containers;

import static io.quarkus.test.bootstrap.BaseService.SERVICE_STARTUP_TIMEOUT;
import static io.quarkus.test.bootstrap.BaseService.SERVICE_STARTUP_TIMEOUT_DEFAULT;
import static io.quarkus.test.utils.PropertiesUtils.RESOURCE_PREFIX;
import static io.quarkus.test.utils.PropertiesUtils.RESOURCE_WITH_DESTINATION_PREFIX;
Expand All @@ -25,7 +24,7 @@
import io.quarkus.test.bootstrap.ManagedResource;
import io.quarkus.test.bootstrap.Protocol;
import io.quarkus.test.bootstrap.ServiceContext;
import io.quarkus.test.configuration.PropertyLookup;
import io.quarkus.test.configuration.Configuration;
import io.quarkus.test.logging.Log;
import io.quarkus.test.logging.LoggingHandler;
import io.quarkus.test.logging.TestContainersLoggingHandler;
Expand All @@ -36,9 +35,7 @@
public abstract class DockerContainerManagedResource implements ManagedResource {

public static final String DOCKER_INNER_CONTAINER = DockerContainerManagedResource.class.getName() + "_inner";
private static final String DELETE_IMAGE_ON_STOP_PROPERTY = "container.delete.image.on.stop";
private static final String TARGET = "target";
private static final PropertyLookup CONTAINER_STARTUP_ATTEMPTS = new PropertyLookup("container-startup-attempts", "1");

private final ServiceContext context;

Expand All @@ -64,10 +61,11 @@ public void start() {
DockerUtils.pullImageById(image);
}

innerContainer.withStartupTimeout(context.getOwner().getConfiguration()
.getAsDuration(SERVICE_STARTUP_TIMEOUT, SERVICE_STARTUP_TIMEOUT_DEFAULT));
Configuration configuration = context.getOwner().getConfiguration();
innerContainer.withStartupTimeout(configuration
.getAsDuration(Configuration.Property.SERVICE_STARTUP_TIMEOUT, SERVICE_STARTUP_TIMEOUT_DEFAULT));
innerContainer.withEnv(resolveProperties());
innerContainer.withStartupAttempts(CONTAINER_STARTUP_ATTEMPTS.getAsInteger());
innerContainer.withStartupAttempts(configuration.getAsInteger(Configuration.Property.CONTAINER_STARTUP_ATTEMPTS, 1));

loggingHandler = new TestContainersLoggingHandler(context.getOwner(), innerContainer);
loggingHandler.startWatching();
Expand All @@ -78,7 +76,7 @@ public void start() {
}

private boolean isDockerImageDeletedOnStop() {
return context.getOwner().getConfiguration().isTrue(DELETE_IMAGE_ON_STOP_PROPERTY);
return context.getOwner().getConfiguration().isTrue(Configuration.Property.DELETE_IMAGE_ON_STOP_PROPERTY);
}

protected abstract GenericContainer<?> initContainer();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy;

import io.quarkus.test.configuration.Configuration;
import io.quarkus.test.logging.Log;
import io.quarkus.test.utils.DockerUtils;

public class GenericDockerContainerManagedResource extends DockerContainerManagedResource {

private static final String PRIVILEGED_MODE = "container.privileged-mode";
private static final String REUSABLE_MODE = "container.reusable";
private final ContainerManagedResourceBuilder model;

protected GenericDockerContainerManagedResource(ContainerManagedResourceBuilder model) {
Expand Down Expand Up @@ -68,10 +67,10 @@ public void stop() {
}

protected boolean isReusable() {
return model.getContext().getOwner().getConfiguration().isTrue(REUSABLE_MODE);
return model.getContext().getOwner().getConfiguration().isTrue(Configuration.Property.REUSABLE_MODE);
}

private boolean isPrivileged() {
return model.getContext().getOwner().getConfiguration().isTrue(PRIVILEGED_MODE);
return model.getContext().getOwner().getConfiguration().isTrue(Configuration.Property.PRIVILEGED_MODE);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,7 @@

public class BaseService<T extends Service> implements Service {

public static final String SERVICE_STARTUP_TIMEOUT = "startup.timeout";
public static final Duration SERVICE_STARTUP_TIMEOUT_DEFAULT = Duration.ofMinutes(5);
public static final String DELETE_FOLDER_ON_EXIT = "delete.folder.on.exit";

private static final String SERVICE_STARTUP_CHECK_POLL_INTERVAL = "startup.check-poll-interval";
private static final Duration SERVICE_STARTUP_CHECK_POLL_INTERVAL_DEFAULT = Duration.ofSeconds(2);

protected ServiceContext context;
Expand Down Expand Up @@ -272,7 +268,7 @@ public void stop() {
public void close() {
if (!context.getScenarioContext().isDebug()) {
stop();
if (getConfiguration().isTrue(DELETE_FOLDER_ON_EXIT)) {
if (getConfiguration().isTrue(Configuration.Property.DELETE_FOLDER_ON_EXIT)) {
try {
FileUtils.deletePath(getServiceFolder());
} catch (Exception ex) {
Expand Down Expand Up @@ -376,9 +372,10 @@ private void waitUntilServiceIsStarted() {
}
try {
Duration startupCheckInterval = getConfiguration()
.getAsDuration(SERVICE_STARTUP_CHECK_POLL_INTERVAL, SERVICE_STARTUP_CHECK_POLL_INTERVAL_DEFAULT);
.getAsDuration(Configuration.Property.SERVICE_STARTUP_CHECK_POLL_INTERVAL,
SERVICE_STARTUP_CHECK_POLL_INTERVAL_DEFAULT);
Duration startupTimeout = getConfiguration()
.getAsDuration(SERVICE_STARTUP_TIMEOUT, SERVICE_STARTUP_TIMEOUT_DEFAULT);
.getAsDuration(Configuration.Property.SERVICE_STARTUP_TIMEOUT, SERVICE_STARTUP_TIMEOUT_DEFAULT);
untilIsTrue(this::isRunningOrFailed, AwaitilitySettings
.using(startupCheckInterval, startupTimeout)
.doNotIgnoreExceptions()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,36 @@

import java.io.InputStream;
import java.time.Duration;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.Properties;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import jakarta.annotation.Nullable;

import org.apache.commons.lang3.StringUtils;

public final class Configuration {

private static final String GLOBAL_PROPERTIES = System.getProperty("ts.test.resources.file.location", "global.properties");
private static final String TEST_PROPERTIES = "test.properties";
private static final String PREFIX_TEMPLATE = "ts.%s.";
private static final String PREFIX = "ts.";
private static final String PREFIX_TEMPLATE = PREFIX + "%s.";
private static final String GLOBAL_SCOPE = "global";

private final Map<String, String> properties;
private final EnumMap<Property, String> properties;

private Configuration(Map<String, String> properties) {
private Configuration(EnumMap<Property, String> properties) {
this.properties = properties;
}

public List<String> getAsList(String property) {
public List<String> getAsList(Property property) {
String value = get(property);
if (StringUtils.isEmpty(value)) {
return Collections.emptyList();
Expand All @@ -35,7 +40,7 @@ public List<String> getAsList(String property) {
return Stream.of(value.split(",")).collect(Collectors.toList());
}

public Duration getAsDuration(String property, Duration defaultValue) {
public Duration getAsDuration(Property property, Duration defaultValue) {
String value = get(property);
if (StringUtils.isEmpty(value)) {
return defaultValue;
Expand All @@ -48,7 +53,7 @@ public Duration getAsDuration(String property, Duration defaultValue) {
return Duration.parse(value);
}

public Double getAsDouble(String property, double defaultValue) {
public Double getAsDouble(Property property, double defaultValue) {
String value = get(property);
if (StringUtils.isEmpty(value)) {
return defaultValue;
Expand All @@ -57,24 +62,33 @@ public Double getAsDouble(String property, double defaultValue) {
return Double.parseDouble(value);
}

public String get(String property) {
public int getAsInteger(Property property, int defaultValue) {
String value = get(property);
if (StringUtils.isEmpty(value)) {
return defaultValue;
}

return Integer.parseInt(value);
}

public String get(Property property) {
return properties.get(property);
}

public String getOrDefault(String property, String defaultValue) {
public String getOrDefault(Property property, String defaultValue) {
return properties.getOrDefault(property, defaultValue);
}

public boolean isTrue(String property) {
public boolean isTrue(Property property) {
return is(property, Boolean.TRUE.toString());
}

public boolean is(String property, String expected) {
public boolean is(Property property, String expected) {
return StringUtils.equalsIgnoreCase(properties.get(property), expected);
}

public static Configuration load() {
Map<String, String> properties = new HashMap<>();
EnumMap<Property, String> properties = new EnumMap<>(Property.class);
// Lowest priority: properties from global.properties and scope `global`
properties.putAll(loadPropertiesFrom(GLOBAL_PROPERTIES, GLOBAL_SCOPE));
// Then, properties from system properties and scope `global`
Expand All @@ -97,32 +111,131 @@ public static Configuration load(String... serviceNames) {
return configuration;
}

private static Map<String, String> loadPropertiesFromSystemProperties(String scope) {
private static EnumMap<Property, String> loadPropertiesFromSystemProperties(String scope) {
return loadPropertiesFrom(System.getProperties(), scope);
}

private static Map<String, String> loadPropertiesFrom(String propertiesFile, String scope) {
private static EnumMap<Property, String> loadPropertiesFrom(String propertiesFile, String scope) {
try (InputStream input = Configuration.class.getClassLoader().getResourceAsStream(propertiesFile)) {
Properties prop = new Properties();
prop.load(input);
return loadPropertiesFrom(prop, scope);
} catch (Exception ignored) {
// There is no properties file: this is not mandatory.
} catch (Exception exception) {
if (exception instanceof NullPointerException && exception.getMessage().equals("inStream parameter is null")) {
System.err.println("No properties file: " + propertiesFile);
} else {
throw new IllegalStateException(exception);
}
}

return Collections.emptyMap();
return new EnumMap<>(Property.class);
}

private static Map<String, String> loadPropertiesFrom(Properties prop, String scope) {
Map<String, String> properties = new HashMap<>();
private static EnumMap<Property, String> loadPropertiesFrom(Properties prop, String scope) {
EnumMap<Property, String> properties = new EnumMap<>(Property.class);

String prefix = String.format(PREFIX_TEMPLATE, scope);
for (Entry<Object, Object> entry : prop.entrySet()) {
String key = (String) entry.getKey();
if (StringUtils.startsWith(key, prefix)) {
properties.put(key.replace(prefix, StringUtils.EMPTY), (String) entry.getValue());
String property = key.replace(prefix, StringUtils.EMPTY);
Property parsed = Property.getByName(property)
.orElseThrow(() -> new NoSuchElementException("Unknown property: " + property + " from " + key));
properties.put(parsed, (String) entry.getValue());
}
}

return properties;
}

public enum Property {
RESOURCES_FILE_LOCATION("resources.file.location"),
SERVICE_STARTUP_TIMEOUT("startup.timeout"),
DELETE_FOLDER_ON_EXIT("delete.folder.on.exit"),
SERVICE_STARTUP_CHECK_POLL_INTERVAL("startup.check-poll-interval"),
TIMEOUT_FACTOR_PROPERTY("factor.timeout"),
KUBERNETES_DEPLOYMENT_SERVICE_PROPERTY("kubernetes.service"),
KUBERNETES_DEPLOYMENT_TEMPLATE_PROPERTY("kubernetes.template"),
KUBERNETES_USE_INTERNAL_SERVICE_AS_URL_PROPERTY("kubernetes.use-internal-service-as-url"),
KUBERNETES_DELETE_AFTERWARDS("kubernetes.delete.namespace.after.all"),
KUBERNETES_EPHEMERAL_NAMESPACES("kubernetes.ephemeral.namespaces.enabled"),
OPENSHIFT_DEPLOYMENT_SERVICE_PROPERTY("openshift.service"),
OPENSHIFT_DEPLOYMENT_TEMPLATE_PROPERTY("openshift.template"),
OPENSHIFT_USE_INTERNAL_SERVICE_AS_URL_PROPERTY("openshift.use-internal-service-as-url"),
OPENSHIFT_DELETE_AFTERWARDS("openshift.delete.namespace.after.all"),
OPENSHIFT_PRINT_ON_ERROR("openshift.print.info.on.error"),
OPENSHIFT_EPHEMERAL_NAMESPACES("openshift.ephemeral.namespaces.enabled"),

DELETE_IMAGE_ON_STOP_PROPERTY("container.delete.image.on.stop"),
IMAGE_STREAM_TIMEOUT("imagestream.install.timeout"),
OPERATOR_INSTALL_TIMEOUT("operator.install.timeout"),

CREATE_SERVICE_BY_DEFAULT("generated-service.enabled"),
PROPAGATE_PROPERTIES_STRATEGY("maven.propagate-properties-strategy"),
PROPAGATE_PROPERTIES_STRATEGY_ALL_EXCLUSIONS("maven.propagate-properties-strategy.all.exclude"),
PRIVILEGED_MODE("container.privileged-mode"),
REUSABLE_MODE("container.reusable"),
EXPECTED_OUTPUT("quarkus.expected.log"),
PORT_RANGE_MIN("port.range.min"),
PORT_RANGE_MAX("port.range.max"),
PORT_RESOLUTION_STRATEGY("port.resolution.strategy"),

METRICS_EXTENSION_ENABLED_PROPERTY("metrics.enabled"),
METRICS_PUSH_AFTER_EACH_TEST("metrics.push-after-each-test"),
METRICS_EXPORT_PROMETHEUS_PROPERTY("metrics.export.prometheus.endpoint"),
JAEGER_HTTP_ENDPOINT_SYSTEM_PROPERTY("tracing.jaeger.endpoint"),

LOG_ENABLE("log.enable"),
LOG_LEVEL_NAME("log.level"),
LOG_FORMAT("log.format"),
LOG_FILE_OUTPUT("log.file.output"),
LOG_NOCOLOR("log.nocolor"),
CONTAINER_STARTUP_ATTEMPTS("container-startup-attempts"),
JAEGER_TRACE_URL_PROPERTY("jaeger.trace.url"),
KAFKA_REGISTRY_URL_PROPERTY("kafka.registry.url"),
KAFKA_SSL_PROPERTIES("kafka.ssl.properties"),
SKIP_BEFORE_AND_AFTER("debug.skip-before-and-after-methods"),
RUN_TESTS("debug.run-tests"),
SUSPEND("debug.suspend"),
DOCKER_DETECTION("docker-detection-enabled"),
JAVA_ENABLE_PREVIEW("enable-java-preview"),
IGNORE_KNOWN_ISSUE("global.ignore-known-issue"),
CLI_CMD("quarkus.cli.cmd"),
CONTAINER_REGISTRY_URL("container.registry-url"),
CONTAINER_PREFIX("docker-container-prefix"),
S2I_MAVEN_REMOTE_REPOSITORY("s2i.maven.remote.repository"),
S2I_REPLACE_CA_CERTS("s2i.java.replace-ca-certs"),
S2I_BASE_NATIVE_IMAGE("s2i.openshift.base-native-image");

private final String name;

Property(String name) {
this.name = name;
}

public String getName(@Nullable String scope) {
String scopePart = "";
if (scope != null) {
scopePart = scope + ".";
}
return PREFIX + scopePart + name;
}

public String getName() {
return getName(null);
}

public static Optional<Property> getByName(String requested) {
return Arrays.stream(Property.values())
.filter(property -> property.name.equals(requested))
.findAny();
}

static Property byName(String requested) {
return getByName(requested).orElseThrow(() -> new NoSuchElementException("Unknown property: " + requested));
}

public static boolean isKnownProperty(String toCheck) {
return Arrays.stream(Property.values()).map(property -> property.name).anyMatch(name -> name.equals(toCheck));
}
}
}
Loading

0 comments on commit 36f0cc1

Please sign in to comment.