Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix issues with changes to test resources #17198

Merged
merged 1 commit into from
May 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions bom/application/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1465,6 +1465,13 @@
<artifactId>quarkus-vertx-http-deployment</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-vertx-http-deployment</artifactId>
<version>${project.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-vertx-web</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,27 @@

import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.HotDeploymentWatchedFileBuildItem;
import io.quarkus.deployment.builditem.LaunchModeBuildItem;
import io.quarkus.deployment.builditem.ServiceStartBuildItem;
import io.quarkus.dev.testing.TestWatchedFiles;

public class HotDeploymentConfigFileBuildStep {

@BuildStep
ServiceStartBuildItem setupConfigFileHotDeployment(List<HotDeploymentWatchedFileBuildItem> files) {
ServiceStartBuildItem setupConfigFileHotDeployment(List<HotDeploymentWatchedFileBuildItem> files,
LaunchModeBuildItem launchModeBuildItem) {
// TODO: this should really be an output of the RuntimeRunner
RuntimeUpdatesProcessor processor = RuntimeUpdatesProcessor.INSTANCE;
if (processor != null) {
if (processor != null || launchModeBuildItem.isAuxiliaryApplication()) {
Map<String, Boolean> watchedFilePaths = files.stream()
.collect(Collectors.toMap(HotDeploymentWatchedFileBuildItem::getLocation,
HotDeploymentWatchedFileBuildItem::isRestartNeeded,
(isRestartNeeded1, isRestartNeeded2) -> isRestartNeeded1 || isRestartNeeded2));
processor.setWatchedFilePaths(watchedFilePaths);
if (launchModeBuildItem.isAuxiliaryApplication()) {
TestWatchedFiles.setWatchedFilePaths(watchedFilePaths);
} else {
processor.setWatchedFilePaths(watchedFilePaths, false);
}
}
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
Expand Down Expand Up @@ -81,9 +82,6 @@ public class RuntimeUpdatesProcessor implements HotReplacementContext, Closeable
private final DevModeType devModeType;
volatile Throwable compileProblem;

// file path -> isRestartNeeded
private volatile Map<String, Boolean> watchedFilePaths = Collections.emptyMap();

private volatile Predicate<ClassInfo> disableInstrumentationForClassPredicate = new AlwaysFalsePredicate<>();
private volatile Predicate<Index> disableInstrumentationForIndexPredicate = new AlwaysFalsePredicate<>();

Expand All @@ -95,12 +93,6 @@ public class RuntimeUpdatesProcessor implements HotReplacementContext, Closeable
private final TimestampSet test = new TimestampSet();
final Map<Path, Long> sourceFileTimestamps = new ConcurrentHashMap<>();

/**
* Resources that appear in both src and target, these will be removed if the src resource subsequently disappears.
* This map contains the paths in the target dir, one for each module, otherwise on a second module we will delete files
* from the first one
*/
private final Map<String, Set<Path>> correspondingResources = new ConcurrentHashMap<>();
private final List<Runnable> preScanSteps = new CopyOnWriteArrayList<>();
private final List<Consumer<Set<String>>> noRestartChangesConsumers = new CopyOnWriteArrayList<>();
private final List<HotReplacementSetup> hotReplacementSetup = new ArrayList<>();
Expand All @@ -115,6 +107,13 @@ public class RuntimeUpdatesProcessor implements HotReplacementContext, Closeable
*/
private static volatile IndexView lastStartIndex;

/**
* Resources that appear in both src and target, these will be removed if the src resource subsequently disappears.
* This map contains the paths in the target dir, one for each module, otherwise on a second module we will delete files
* from the first one
*/
private final Map<DevModeContext.CompilationUnit, Set<Path>> correspondingResources = new ConcurrentHashMap<>();

private final TestSupport testSupport;
private volatile boolean firstTestScanComplete;
private volatile Boolean instrumentationEnabled;
Expand Down Expand Up @@ -248,9 +247,11 @@ private void periodicTestCompile() {
try {
ClassScanResult changedTestClassResult = compileTestClasses();
ClassScanResult changedApp = checkForChangedClasses(compiler, DevModeContext.ModuleInfo::getMain, false, test);
Set<String> filesChanged = checkForFileChange(DevModeContext.ModuleInfo::getMain, test);
boolean configFileRestartNeeded = filesChanged.stream().map(watchedFilePaths::get)
Set<String> filesChanges = new HashSet<>(checkForFileChange(s -> s.getTest().get(), test));
filesChanges.addAll(checkForFileChange(DevModeContext.ModuleInfo::getMain, test));
boolean configFileRestartNeeded = filesChanges.stream().map(test.watchedFilePaths::get)
.anyMatch(Boolean.TRUE::equals);

ClassScanResult merged = ClassScanResult.merge(changedTestClassResult, changedApp);
if (configFileRestartNeeded) {
if (compileProblem != null) {
Expand Down Expand Up @@ -369,7 +370,8 @@ public boolean doScan(boolean userInitiated, boolean force) throws IOException {
main);
Set<String> filesChanged = checkForFileChange(DevModeContext.ModuleInfo::getMain, main);

boolean configFileRestartNeeded = filesChanged.stream().map(watchedFilePaths::get).anyMatch(Boolean.TRUE::equals);
boolean configFileRestartNeeded = filesChanged.stream().map(main.watchedFilePaths::get)
.anyMatch(Boolean.TRUE::equals);
boolean instrumentationChange = false;
if (ClassChangeAgent.getInstrumentation() != null && lastStartIndex != null && !configFileRestartNeeded
&& devModeType != DevModeType.REMOTE_LOCAL_SIDE) {
Expand Down Expand Up @@ -561,7 +563,7 @@ ClassScanResult checkForChangedClasses(QuarkusCompiler compiler,
changedSourceFiles = sourcesStream
.parallel()
.filter(p -> matchingHandledExtension(p).isPresent()
&& sourceFileWasRecentModified(p, ignoreFirstScanChanges, timestampSet))
&& sourceFileWasRecentModified(p, ignoreFirstScanChanges))
.map(Path::toFile)
//Needing a concurrent Set, not many standard options:
.collect(Collectors.toCollection(ConcurrentSkipListSet::new));
Expand Down Expand Up @@ -695,7 +697,7 @@ Set<String> checkForFileChange(Function<DevModeContext.ModuleInfo, DevModeContex
TimestampSet timestampSet) {
Set<String> ret = new HashSet<>();
for (DevModeContext.ModuleInfo module : context.getAllModules()) {
final Set<Path> moduleResources = correspondingResources.computeIfAbsent(module.getName(),
final Set<Path> moduleResources = correspondingResources.computeIfAbsent(cuf.apply(module),
m -> Collections.newSetFromMap(new ConcurrentHashMap<>()));
boolean doCopy = true;
String rootPath = cuf.apply(module).getResourcePath();
Expand Down Expand Up @@ -759,13 +761,15 @@ Set<String> checkForFileChange(Function<DevModeContext.ModuleInfo, DevModeContex
}
}

for (String path : watchedFilePaths.keySet()) {
for (String path : timestampSet.watchedFilePaths.keySet()) {
Path file = root.resolve(path);
if (file.toFile().exists()) {
try {
long value = Files.getLastModifiedTime(file).toMillis();
Long existing = timestampSet.watchedFileTimestamps.get(file);
if (value > existing) {
//existing can be null when running tests
//as there is both normal and test resources, but only one set of watched timestampts
if (existing != null && value > existing) {
ret.add(path);
log.infof("File change detected: %s", file);
if (doCopy && !Files.isDirectory(file)) {
Expand Down Expand Up @@ -795,8 +799,7 @@ Set<String> checkForFileChange(Function<DevModeContext.ModuleInfo, DevModeContex
return ret;
}

private boolean sourceFileWasRecentModified(final Path sourcePath, boolean ignoreFirstScanChanges,
TimestampSet timestampSet) {
private boolean sourceFileWasRecentModified(final Path sourcePath, boolean ignoreFirstScanChanges) {
return checkIfFileModified(sourcePath, sourceFileTimestamps, ignoreFirstScanChanges);
}

Expand Down Expand Up @@ -850,48 +853,55 @@ public RuntimeUpdatesProcessor setDisableInstrumentationForIndexPredicate(
return this;
}

public RuntimeUpdatesProcessor setWatchedFilePaths(Map<String, Boolean> watchedFilePaths) {
boolean includeTest = test.watchedFileTimestamps.isEmpty();
this.watchedFilePaths = watchedFilePaths;
main.watchedFileTimestamps.clear();
public RuntimeUpdatesProcessor setWatchedFilePaths(Map<String, Boolean> watchedFilePaths, boolean isTest) {
if (isTest) {
setWatchedFilePathsInternal(watchedFilePaths, test, s -> Arrays.asList(s.getTest().get(), s.getMain()));
} else {
main.watchedFileTimestamps.clear();
setWatchedFilePathsInternal(watchedFilePaths, main, s -> Collections.singletonList(s.getMain()));
}
return this;
}

private RuntimeUpdatesProcessor setWatchedFilePathsInternal(Map<String, Boolean> watchedFilePaths,
TimestampSet timestamps, Function<DevModeContext.ModuleInfo, List<DevModeContext.CompilationUnit>> cuf) {
timestamps.watchedFilePaths = watchedFilePaths;
Map<String, Boolean> extraWatchedFilePaths = new HashMap<>();
for (DevModeContext.ModuleInfo module : context.getAllModules()) {
String rootPath = module.getMain().getResourcePath();
List<DevModeContext.CompilationUnit> compilationUnits = cuf.apply(module);
for (DevModeContext.CompilationUnit unit : compilationUnits) {
String rootPath = unit.getResourcePath();

if (rootPath == null) {
rootPath = module.getMain().getClassesPath();
}
if (rootPath == null) {
continue;
}
Path root = Paths.get(rootPath);
for (String path : watchedFilePaths.keySet()) {
Path config = root.resolve(path);
if (config.toFile().exists()) {
try {
FileTime lastModifiedTime = Files.getLastModifiedTime(config);
main.watchedFileTimestamps.put(config, lastModifiedTime.toMillis());
if (includeTest) {
test.watchedFileTimestamps.put(config, lastModifiedTime.toMillis());
if (rootPath == null) {
rootPath = unit.getClassesPath();
}
if (rootPath == null) {
continue;
}
Path root = Paths.get(rootPath);
for (String path : watchedFilePaths.keySet()) {
Path config = root.resolve(path);
if (config.toFile().exists()) {
try {
FileTime lastModifiedTime = Files.getLastModifiedTime(config);
timestamps.watchedFileTimestamps.put(config, lastModifiedTime.toMillis());
} catch (IOException e) {
throw new UncheckedIOException(e);
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
} else {
main.watchedFileTimestamps.put(config, 0L);
Map<Path, Long> extraWatchedFileTimestamps = expandGlobPattern(root, config);
main.watchedFileTimestamps.putAll(extraWatchedFileTimestamps);
for (Path extraPath : extraWatchedFileTimestamps.keySet()) {
extraWatchedFilePaths.put(root.relativize(extraPath).toString(), this.watchedFilePaths.get(path));
}
if (includeTest) {
test.watchedFileTimestamps.put(config, 0L);
} else {
timestamps.watchedFileTimestamps.put(config, 0L);
Map<Path, Long> extraWatchedFileTimestamps = expandGlobPattern(root, config);
timestamps.watchedFileTimestamps.putAll(extraWatchedFileTimestamps);
for (Path extraPath : extraWatchedFileTimestamps.keySet()) {
extraWatchedFilePaths.put(root.relativize(extraPath).toString(),
timestamps.watchedFilePaths.get(path));
}
timestamps.watchedFileTimestamps.putAll(extraWatchedFileTimestamps);
}
main.watchedFileTimestamps.putAll(extraWatchedFileTimestamps);
}
}
}
this.watchedFilePaths.putAll(extraWatchedFilePaths);
timestamps.watchedFilePaths.putAll(extraWatchedFilePaths);
return this;
}

Expand Down Expand Up @@ -970,11 +980,17 @@ static class TimestampSet {
final Map<Path, Long> watchedFileTimestamps = new ConcurrentHashMap<>();
final Map<Path, Long> classFileChangeTimeStamps = new ConcurrentHashMap<>();
final Map<Path, Path> classFilePathToSourceFilePath = new ConcurrentHashMap<>();
// file path -> isRestartNeeded
private volatile Map<String, Boolean> watchedFilePaths = Collections.emptyMap();

public void merge(TimestampSet other) {
watchedFileTimestamps.putAll(other.watchedFileTimestamps);
classFileChangeTimeStamps.putAll(other.classFileChangeTimeStamps);
classFilePathToSourceFilePath.putAll(other.classFilePathToSourceFilePath);
Map<String, Boolean> newVal = new HashMap<>(watchedFilePaths);
newVal.putAll(other.watchedFilePaths);
watchedFilePaths = newVal;

}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import io.quarkus.bootstrap.app.CuratedApplication;
import io.quarkus.deployment.dev.ClassScanResult;
import io.quarkus.deployment.dev.DevModeContext;
import io.quarkus.deployment.dev.RuntimeUpdatesProcessor;
import io.quarkus.dev.testing.TestWatchedFiles;
import io.quarkus.runtime.configuration.HyphenateEnumConverter;

public class TestRunner {
Expand Down Expand Up @@ -243,6 +245,10 @@ public void runComplete(TestRunResults results) {
synchronized (this) {
runner = null;
}
Map<String, Boolean> watched = TestWatchedFiles.retrieveWatchedFilePaths();
if (watched != null) {
RuntimeUpdatesProcessor.INSTANCE.setWatchedFilePaths(watched, true);
}
if (disabled) {
return;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package io.quarkus.dev.testing;

import java.util.Map;

/**
* provides a way for a test run to tell the external application about watched paths.
*
* This could be a test specific application.properties or import.sql for example
*/
public class TestWatchedFiles {

private static volatile Map<String, Boolean> watchedFilePaths;

public static Map<String, Boolean> retrieveWatchedFilePaths() {
Map<String, Boolean> watchedFilePaths = TestWatchedFiles.watchedFilePaths;
TestWatchedFiles.watchedFilePaths = null;
return watchedFilePaths;
}

public static void setWatchedFilePaths(Map<String, Boolean> watchedFilePaths) {
TestWatchedFiles.watchedFilePaths = watchedFilePaths;
}
}
1 change: 1 addition & 0 deletions devtools/maven/src/main/java/io/quarkus/maven/DevMojo.java
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,7 @@ private void addProject(MavenDevModeLauncher.Builder builder, LocalProject local
.setTestSourcePaths(testSourcePaths)
.setTestClassesPath(testClassesPath)
.setTestResourcePath(testResourcePath)
.setTestResourcesOutputPath(testClassesPath)
.build();

if (root) {
Expand Down
16 changes: 16 additions & 0 deletions extensions/hibernate-orm/deployment/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@
<artifactId>quarkus-jdbc-h2-deployment</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
Expand All @@ -82,6 +87,17 @@
<artifactId>quarkus-smallrye-metrics-deployment</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-vertx-http-deployment</artifactId>
<scope>test</scope>
<type>test-jar</type>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -861,11 +861,9 @@ private static void producePersistenceUnitDescriptorFromConfig(
"Unable to find file referenced in '" + HIBERNATE_ORM_CONFIG_PREFIX + "sql-load-script="
+ persistenceUnitConfig.sqlLoadScript.get() + "'. Remove property or add file to your path.");
}
if (launchMode == LaunchMode.DEVELOPMENT) {
// in dev mode we want to make sure that we watch for changes to file even if it doesn't currently exist
// as a user could still add it after performing the initial configuration
hotDeploymentWatchedFiles.produce(new HotDeploymentWatchedFileBuildItem(importFile.get()));
}
// in dev mode we want to make sure that we watch for changes to file even if it doesn't currently exist
// as a user could still add it after performing the initial configuration
hotDeploymentWatchedFiles.produce(new HotDeploymentWatchedFileBuildItem(importFile.get()));
} else {
//Disable implicit loading of the default import script (import.sql)
descriptor.getProperties().setProperty(AvailableSettings.HBM2DDL_IMPORT_FILES, "");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.quarkus.hibernate.orm;

import static org.hamcrest.Matchers.is;

import org.junit.jupiter.api.Test;

import io.quarkus.test.junit.QuarkusTest;
import io.restassured.RestAssured;

/**
* Test for continuous testing with hibernate
*/
@QuarkusTest
public class HibernateET {

@Test
public void testImport() {
RestAssured.when().get("/my-entity/1").then().body(is("MyEntity:TEST ENTITY"));
}

}
Loading