diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java index c57e77c0dbbfb4..62edd7ce0f8da2 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java @@ -375,6 +375,17 @@ public boolean doScan(boolean userInitiated, boolean force) throws IOException { boolean configFileRestartNeeded = filesChanged.stream().map(main.watchedFilePaths::get) .anyMatch(Boolean.TRUE::equals); boolean instrumentationChange = false; + + List changedFilesForRestart = new ArrayList<>(); + if (configFileRestartNeeded) { + changedFilesForRestart + .addAll(filesChanged.stream().filter(fn -> Boolean.TRUE.equals(main.watchedFilePaths.get(fn))) + .map(Paths::get).collect(Collectors.toList())); + } + changedFilesForRestart.addAll(changedClassResults.getChangedClasses()); + changedFilesForRestart.addAll(changedClassResults.getAddedClasses()); + changedFilesForRestart.addAll(changedClassResults.getDeletedClasses()); + if (ClassChangeAgent.getInstrumentation() != null && lastStartIndex != null && !configFileRestartNeeded && devModeType != DevModeType.REMOTE_LOCAL_SIDE) { //attempt to do an instrumentation based reload @@ -428,6 +439,9 @@ public boolean doScan(boolean userInitiated, boolean force) throws IOException { boolean restartNeeded = !instrumentationChange && (changedClassResults.isChanged() || (IsolatedDevModeMain.deploymentProblem != null && userInitiated) || configFileRestartNeeded); if (restartNeeded) { + String changeString = changedFilesForRestart.stream().map(Path::getFileName).map(Object::toString) + .collect(Collectors.joining(", ")) + "."; + log.infof("Restarting quarkus due to changes in " + changeString); restartCallback.accept(filesChanged, changedClassResults); long timeNanoSeconds = System.nanoTime() - startNanoseconds; log.infof("Live reload total time: %ss ", Timing.convertToBigDecimalSeconds(timeNanoSeconds)); @@ -574,8 +588,6 @@ && sourceFileWasRecentModified(p, ignoreFirstScanChanges)) } if (!changedSourceFiles.isEmpty()) { classScanResult.compilationHappened = true; - log.info("Changed source files detected, recompiling " - + changedSourceFiles.stream().map(File::getName).collect(Collectors.joining(", "))); //so this is pretty yuck, but on a lot of systems a write is actually a truncate + write //its possible we see the truncated file timestamp, then the write updates the timestamp //which will then re-trigger continuous testing/live reload diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestConsoleHandler.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestConsoleHandler.java index 275c219707e554..02f7a036c4732d 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestConsoleHandler.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestConsoleHandler.java @@ -7,10 +7,14 @@ import static io.quarkus.deployment.dev.testing.TestConsoleHandler.MessageFormat.helpOption; import static io.quarkus.deployment.dev.testing.TestConsoleHandler.MessageFormat.statusFooter; import static io.quarkus.deployment.dev.testing.TestConsoleHandler.MessageFormat.statusHeader; -import static io.quarkus.deployment.dev.testing.TestConsoleHandler.MessageFormat.toggleStatus; import java.io.IOException; +import java.nio.file.Path; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.LinkedHashSet; import java.util.Map; +import java.util.Set; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Consumer; @@ -19,6 +23,7 @@ import org.junit.platform.launcher.TestIdentifier; import io.quarkus.bootstrap.classloading.QuarkusClassLoader; +import io.quarkus.deployment.dev.ClassScanResult; import io.quarkus.deployment.dev.RuntimeUpdatesProcessor; import io.quarkus.dev.console.InputHandler; import io.quarkus.dev.console.QuarkusConsole; @@ -165,16 +170,37 @@ public void testComplete(TestResult result) { @Override public void runComplete(TestRunResults results) { firstRun = false; - if (results.getCurrentTotalCount() == 0) { + SimpleDateFormat df = new SimpleDateFormat("kk:mm:ss"); + + String end = " Tests completed at " + df.format(new Date()); + if (results.getTrigger() != null) { + ClassScanResult trigger = results.getTrigger(); + Set paths = new LinkedHashSet<>(); + paths.addAll(trigger.getChangedClasses()); + paths.addAll(trigger.getAddedClasses()); + paths.addAll(trigger.getDeletedClasses()); + if (paths.size() == 1) { + end = end + " due to changes to " + paths.iterator().next().getFileName() + "."; + } else if (paths.size() > 1) { + end = end + " due to changes to " + paths.iterator().next().getFileName() + " and " + (paths.size() - 1) + + " other files."; + } else { + //should never happen + end = end + "."; + } + } else { + end = end + "."; + } + if (results.getTotalCount() == 0) { lastStatus = YELLOW + "No tests found" + RESET; } else if (results.getFailedCount() == 0 && results.getPassedCount() == 0) { lastStatus = String.format(YELLOW + "All %d tests were skipped" + RESET, results.getSkippedCount()); } else if (results.getCurrentFailing().isEmpty()) { lastStatus = String.format( - GREEN + "All %d tests are passing (%d skipped), %d tests were run in %dms." + RESET, + GREEN + "All %d tests are passing (%d skipped), %d tests were run in %dms." + end + RESET, results.getPassedCount(), results.getSkippedCount(), - results.getCurrentTotalCount(), results.getTotalTime()); + results.getCurrentTotalCount() - results.getSkippedCount(), results.getTotalTime()); } else { //TODO: this should not use the logger, it should print a nicer status log.error(statusHeader("TEST REPORT #" + results.getId())); @@ -189,7 +215,7 @@ public void runComplete(TestRunResults results) { statusFooter(RED + results.getCurrentFailedCount() + " TESTS FAILED")); lastStatus = String.format( RED + "%d tests failed" + RESET + " (" + GREEN + "%d passing" + RESET + ", " + YELLOW + "%d skipped" - + RESET + "), %d tests were run in %dms." + RESET, + + RESET + "), %d tests were run in %dms." + end + RESET, results.getCurrentFailedCount(), results.getPassedCount(), results.getSkippedCount(), results.getCurrentTotalCount(), results.getTotalTime()); } @@ -200,10 +226,7 @@ public void runComplete(TestRunResults results) { @Override public void noTests(TestRunResults results) { - firstRun = false; - lastStatus = "No tests to run"; - promptHandler.setStatus(lastStatus); - promptHandler.setPrompt(RUNNING_PROMPT); + runComplete(results); } @Override