From 2c2784aa34449442a50d837decce9828d4c73b9f Mon Sep 17 00:00:00 2001 From: Stuart Douglas Date: Mon, 21 Jun 2021 11:04:39 +1000 Subject: [PATCH] Continuous testing fixes - Add extra 'compile error' display, so test compile errors don't overwrite results. - Add extra 'test results' display, so current results are always visible. - Fix multiple bugs with pausing/resuming tests - Change yellow to blue, as ANSI yellow is very close to ANSI green in some terminals. --- .../deployment/dev/console/AeshConsole.java | 100 ++++++++++-------- .../dev/testing/TestConsoleHandler.java | 53 ++++++---- .../deployment/dev/testing/TestListener.java | 4 + .../deployment/dev/testing/TestRunner.java | 7 +- .../deployment/dev/testing/TestSupport.java | 2 +- .../io/quarkus/dev/console/BasicConsole.java | 20 ++++ .../io/quarkus/dev/console/InputHandler.java | 4 + .../quarkus/dev/console/QuarkusConsole.java | 28 ++++- 8 files changed, 145 insertions(+), 73 deletions(-) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/console/AeshConsole.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/console/AeshConsole.java index 561aab966edea..8e5304be7be9f 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/console/AeshConsole.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/console/AeshConsole.java @@ -19,8 +19,7 @@ public class AeshConsole extends QuarkusConsole { private Size size; private Attributes attributes; - private String statusMessage; - private String promptMessage; + private String[] messages = new String[0]; private int totalStatusLines = 0; private int lastWriteCursorX; /** @@ -60,27 +59,6 @@ public void run() { }, "Console Shutdown Hoot")); } - private AeshConsole setStatusMessage(String statusMessage) { - synchronized (this) { - StringBuilder buffer = new StringBuilder(); - clearStatusMessages(buffer); - int newLines = countLines(statusMessage) + countLines(promptMessage); - if (statusMessage == null) { - if (promptMessage != null) { - newLines += 2; - } - } else if (promptMessage == null) { - newLines += 2; - } else { - newLines += 3; - } - this.statusMessage = statusMessage; - updatePromptOnChange(buffer, newLines); - } - deadlockSafeWrite(); - return this; - } - private void updatePromptOnChange(StringBuilder buffer, int newLines) { if (newLines > totalStatusLines) { StringBuilder nb = new StringBuilder(); @@ -106,29 +84,41 @@ public AeshInputHolder createHolder(InputHandler inputHandler) { } private AeshConsole setPromptMessage(String promptMessage) { + if (!inputSupport) { + return this; + } + setMessage(0, promptMessage); + return this; + } + + private AeshConsole setMessage(int position, String message) { synchronized (this) { - if (!inputSupport) { - return this; + if (messages.length <= position) { + String[] old = messages; + messages = new String[position + 1]; + System.arraycopy(old, 0, this.messages, 0, old.length); } + messages[position] = message; + int newLines = countTotalStatusLines(); StringBuilder buffer = new StringBuilder(); clearStatusMessages(buffer); - int newLines = countLines(statusMessage) + countLines(promptMessage); - if (statusMessage == null) { - if (promptMessage != null) { - newLines += 2; - } - } else if (promptMessage == null) { - newLines += 2; - } else { - newLines += 3; - } - this.promptMessage = promptMessage; updatePromptOnChange(buffer, newLines); } deadlockSafeWrite(); return this; } + private int countTotalStatusLines() { + int total = 0; + for (String i : messages) { + if (i != null) { + total++; + total += countLines(i); + } + } + return total == 0 ? total : total + 1; + } + private void end(Connection conn) { conn.setAttributes(attributes); StringBuilder sb = new StringBuilder(); @@ -227,15 +217,21 @@ private void printStatusAndPrompt(StringBuilder buffer) { bottomBlankSpace = 0; } buffer.append("\n--\n"); - if (statusMessage != null) { - buffer.append(statusMessage); - if (promptMessage != null) { - buffer.append("\n"); + for (int i = messages.length - 1; i >= 0; --i) { + String msg = messages[i]; + if (msg != null) { + buffer.append(msg); + if (i > 0) { + //if there is any more messages to print we add a newline + for (int j = 0; j < i; ++j) { + if (messages[j] != null) { + buffer.append("\n"); + break; + } + } + } } } - if (promptMessage != null) { - buffer.append(promptMessage); - } } private void clearStatusMessages(StringBuilder buffer) { @@ -352,13 +348,27 @@ protected AeshInputHolder(InputHandler handler) { @Override protected void setPromptMessage(String prompt) { - AeshConsole.this.setPromptMessage(prompt); + if (!inputSupport) { + return; + } + setMessage(0, prompt); + } + + @Override + protected void setResultsMessage(String results) { + setMessage(1, results); + } + + @Override + protected void setCompileErrorMessage(String results) { + setMessage(3, results); } @Override protected void setStatusMessage(String status) { - AeshConsole.this.setStatusMessage(status); + setMessage(2, status); } + } } 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 8b38b9de93b66..207a02082db64 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 @@ -1,9 +1,9 @@ package io.quarkus.deployment.dev.testing; +import static io.quarkus.deployment.dev.testing.TestConsoleHandler.MessageFormat.BLUE; import static io.quarkus.deployment.dev.testing.TestConsoleHandler.MessageFormat.GREEN; import static io.quarkus.deployment.dev.testing.TestConsoleHandler.MessageFormat.RED; import static io.quarkus.deployment.dev.testing.TestConsoleHandler.MessageFormat.RESET; -import static io.quarkus.deployment.dev.testing.TestConsoleHandler.MessageFormat.YELLOW; 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; @@ -34,10 +34,12 @@ public class TestConsoleHandler implements TestListener { private static final Logger log = Logger.getLogger("io.quarkus.test"); - public static final String PAUSED_PROMPT = YELLOW + "Tests paused, press [r] to resume, [h] for more options>" + RESET; - public static final String FIRST_RUN_PROMPT = YELLOW + "Running Tests for the first time" + RESET; - public static final String RUNNING_PROMPT = "Press [r] to re-run, [v] to view full results, [p] to pause, [h] for more options>"; - public static final String ABORTED_PROMPT = "Test run aborted."; + public static final String PAUSED_PROMPT = "Tests paused, press [" + BLUE + "r" + RESET + "] to resume, [" + BLUE + "h" + + RESET + "] for more options>" + RESET; + public static final String FIRST_RUN_PROMPT = BLUE + "Running Tests for the first time" + RESET; + public static final String RUNNING_PROMPT = "Press [" + BLUE + "r" + RESET + "] to re-run, [" + BLUE + + "v" + RESET + "] to view full results, [" + BLUE + "p" + RESET + "] to pause, [" + BLUE + + "h" + RESET + "] for more options>"; final DevModeType devModeType; @@ -46,7 +48,7 @@ public class TestConsoleHandler implements TestListener { boolean currentlyFailing = false; volatile InputHandler.ConsoleStatus promptHandler; volatile TestController testController; - private String lastStatus; + private String lastResults; public TestConsoleHandler(DevModeType devModeType) { this.devModeType = devModeType; @@ -93,7 +95,7 @@ public void run() { } else { if (disabled) { if (k == 'r') { - promptHandler.setStatus(YELLOW + "Starting tests" + RESET); + promptHandler.setStatus(BLUE + "Starting tests" + RESET); TestSupport.instance().get().start(); } } else if (!firstRun) { @@ -158,10 +160,12 @@ public void testsEnabled() { disabled = false; if (firstRun) { promptHandler.setStatus(null); + promptHandler.setResults(null); promptHandler.setPrompt(FIRST_RUN_PROMPT); } else { promptHandler.setPrompt(RUNNING_PROMPT); - promptHandler.setStatus(lastStatus); + promptHandler.setResults(lastResults); + promptHandler.setStatus(null); } } @@ -170,11 +174,17 @@ public void testsDisabled() { disabled = true; promptHandler.setPrompt(PAUSED_PROMPT); promptHandler.setStatus(null); + promptHandler.setResults(null); } @Override public void testCompileFailed(String message) { - promptHandler.setStatus(message); + promptHandler.setCompileError(message); + } + + @Override + public void testCompileSucceeded() { + promptHandler.setCompileError(null); } @Override @@ -226,15 +236,15 @@ public void runComplete(TestRunResults results) { end = end + "."; } if (results.getTotalCount() == 0) { - lastStatus = YELLOW + "No tests found" + RESET; + lastResults = BLUE + "No tests found" + RESET; } else if (results.getFailedCount() == 0 && results.getPassedCount() == 0) { - lastStatus = String.format(YELLOW + "All %d tests were skipped" + RESET, results.getSkippedCount()); + lastResults = String.format(BLUE + "All %d tests were skipped" + RESET, results.getSkippedCount()); } else if (results.getCurrentFailing().isEmpty()) { if (currentlyFailing) { log.info(GREEN + "All tests are now passing" + RESET); } currentlyFailing = false; - lastStatus = String.format( + lastResults = String.format( GREEN + "All %d tests are passing (%d skipped), %d tests were run in %dms." + end + RESET, results.getPassedCount(), results.getSkippedCount(), @@ -252,15 +262,18 @@ public void runComplete(TestRunResults results) { } log.error( 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." + end + RESET, + lastResults = String.format( + RED + "%d tests failed" + RESET + " (" + GREEN + "%d passing" + RESET + ", " + BLUE + "%d skipped" + + RESET + ")" + RED + ", %d tests were run in %dms." + end + RESET, results.getCurrentFailedCount(), results.getPassedCount(), results.getSkippedCount(), results.getCurrentTotalCount(), results.getTotalTime()); } //this will re-print when using the basic console - promptHandler.setPrompt(RUNNING_PROMPT); - promptHandler.setStatus(lastStatus); + if (!disabled) { + promptHandler.setPrompt(RUNNING_PROMPT); + promptHandler.setResults(lastResults); + promptHandler.setStatus(null); + } } @Override @@ -270,8 +283,6 @@ public void noTests(TestRunResults results) { @Override public void runAborted() { - promptHandler.setStatus(ABORTED_PROMPT); - promptHandler.setPrompt(RUNNING_PROMPT); firstRun = false; } @@ -296,7 +307,7 @@ static class MessageFormat { public static final String RED = "\u001B[91m"; public static final String GREEN = "\u001b[32m"; - public static final String YELLOW = "\u001b[33m"; + public static final String BLUE = "\u001b[34m"; public static final String RESET = "\u001b[0m"; private MessageFormat() { @@ -315,7 +326,7 @@ public static String toggleStatus(boolean enabled) { } public static String helpOption(String key, String description) { - return "[" + GREEN + key + RESET + "] - " + description; + return "[" + BLUE + key + RESET + "] - " + description; } public static String helpOption(String key, String description, boolean enabled) { diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestListener.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestListener.java index bfac67a31c795..f8f8540654a3f 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestListener.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestListener.java @@ -34,4 +34,8 @@ default void setInstrumentationBasedReload(boolean ibr) { default void testCompileFailed(String message) { } + + default void testCompileSucceeded() { + + } } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestRunner.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestRunner.java index 3a8deabcdb244..d3b5ffb01f182 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestRunner.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestRunner.java @@ -20,7 +20,6 @@ import org.junit.platform.engine.TestDescriptor; import org.junit.platform.engine.UniqueId; import org.junit.platform.launcher.PostDiscoveryFilter; -import org.opentest4j.TestAbortedException; import io.quarkus.bootstrap.app.CuratedApplication; import io.quarkus.deployment.dev.ClassScanResult; @@ -348,9 +347,6 @@ public void waitTillResumed() { throw new RuntimeException(e); } } - if (disabled) { - throw new TestAbortedException("Tests are disabled"); - } } } @@ -366,6 +362,9 @@ public void testCompileFailed(Throwable e) { public synchronized void testCompileSucceeded() { compileProblem = null; + for (TestListener i : testSupport.testListeners) { + i.testCompileSucceeded(); + } } public boolean isRunning() { diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestSupport.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestSupport.java index 84c5b9cca19d2..436150fe1b9c9 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestSupport.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestSupport.java @@ -98,10 +98,10 @@ public void start() { if (context.getApplicationRoot().getTest().isPresent()) { started = true; init(); + testRunner.enable(); for (TestListener i : testListeners) { i.testsEnabled(); } - testRunner.enable(); } } catch (Exception e) { log.error("Failed to create compiler, runtime compilation will be unavailable", e); diff --git a/core/devmode-spi/src/main/java/io/quarkus/dev/console/BasicConsole.java b/core/devmode-spi/src/main/java/io/quarkus/dev/console/BasicConsole.java index 893d97dc77c5b..b9fb3feff3464 100644 --- a/core/devmode-spi/src/main/java/io/quarkus/dev/console/BasicConsole.java +++ b/core/devmode-spi/src/main/java/io/quarkus/dev/console/BasicConsole.java @@ -71,6 +71,26 @@ protected void setPromptMessage(String prompt) { } } + @Override + protected void setResultsMessage(String results) { + DISABLE_FILTER.set(true); + try { + statusLogger.info(results); + } finally { + DISABLE_FILTER.set(false); + } + } + + @Override + protected void setCompileErrorMessage(String results) { + DISABLE_FILTER.set(true); + try { + statusLogger.info(results); + } finally { + DISABLE_FILTER.set(false); + } + } + @Override protected void setStatusMessage(String status) { if (status == null) { diff --git a/core/devmode-spi/src/main/java/io/quarkus/dev/console/InputHandler.java b/core/devmode-spi/src/main/java/io/quarkus/dev/console/InputHandler.java index 191ae5cb72b46..e9d88a61f84f3 100644 --- a/core/devmode-spi/src/main/java/io/quarkus/dev/console/InputHandler.java +++ b/core/devmode-spi/src/main/java/io/quarkus/dev/console/InputHandler.java @@ -10,5 +10,9 @@ interface ConsoleStatus { void setPrompt(String prompt); void setStatus(String status); + + void setResults(String results); + + void setCompileError(String compileError); } } diff --git a/core/devmode-spi/src/main/java/io/quarkus/dev/console/QuarkusConsole.java b/core/devmode-spi/src/main/java/io/quarkus/dev/console/QuarkusConsole.java index eb22b834fa06d..93d07fd806546 100644 --- a/core/devmode-spi/src/main/java/io/quarkus/dev/console/QuarkusConsole.java +++ b/core/devmode-spi/src/main/java/io/quarkus/dev/console/QuarkusConsole.java @@ -119,6 +119,8 @@ protected static abstract class InputHolder implements InputHandler.ConsoleStatu volatile boolean enabled; String prompt; String status; + String results; + String compileError; protected InputHolder(InputHandler handler) { this.handler = handler; @@ -129,6 +131,8 @@ public InputHolder setEnabled(boolean enabled) { if (enabled) { setStatus(status); setPrompt(prompt); + setResults(results); + setCompileError(compileError); } return this; } @@ -141,8 +145,6 @@ public void setPrompt(String prompt) { } } - protected abstract void setPromptMessage(String prompt); - @Override public void setStatus(String status) { this.status = status; @@ -151,6 +153,28 @@ public void setStatus(String status) { } } + @Override + public void setResults(String results) { + this.results = results; + if (enabled) { + setResultsMessage(results); + } + } + + @Override + public void setCompileError(String compileError) { + this.compileError = compileError; + if (enabled) { + setCompileErrorMessage(compileError); + } + } + protected abstract void setStatusMessage(String status); + + protected abstract void setPromptMessage(String prompt); + + protected abstract void setResultsMessage(String results); + + protected abstract void setCompileErrorMessage(String results); } }