From 90714add616d8217474b6aa4086fd533a9c0570e Mon Sep 17 00:00:00 2001 From: Frotty Date: Mon, 25 May 2026 14:05:27 +0200 Subject: [PATCH] add -compactOutput --- .../de/peeeq/wurstio/CompilationProcess.java | 10 +- .../src/main/java/de/peeeq/wurstio/Main.java | 47 ++++++-- .../languageserver/requests/RunTests.java | 107 +++++++++++++----- .../java/de/peeeq/wurstscript/RunArgs.java | 6 + .../wurstscript/attributes/CompileError.java | 5 + .../wurstscript/gui/WurstGuiCliImpl.java | 12 ++ .../tests/wurstscript/utils/UtilsTest.java | 15 +++ 7 files changed, 162 insertions(+), 40 deletions(-) diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/CompilationProcess.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/CompilationProcess.java index 7f135d599..eb70be15f 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/CompilationProcess.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/CompilationProcess.java @@ -157,8 +157,10 @@ private void runTests(ImTranslator translator, WurstCompilerJassImpl compiler, i PrintStream out = System.out; // tests gui.sendProgress("Running tests"); - System.out.println("Running tests"); - RunTests runTests = new RunTests(Optional.empty(), 0, 0, Optional.empty(), testTimeout, testFilter) { + if (!runArgs.isCompactOutput()) { + System.out.println("Running tests"); + } + RunTests runTests = new RunTests(Optional.empty(), 0, 0, Optional.empty(), testTimeout, testFilter, runArgs.isCompactOutput()) { @Override protected void print(String message) { out.print(message); @@ -176,6 +178,8 @@ protected void print(String message) { } } - System.out.println("Finished running tests"); + if (!runArgs.isCompactOutput()) { + System.out.println("Finished running tests"); + } } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/Main.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/Main.java index 5d28f2c16..710fe0b19 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/Main.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/Main.java @@ -31,7 +31,9 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.Optional; import static de.peeeq.wurstio.languageserver.ProjectConfigBuilder.FILE_NAME; @@ -99,7 +101,7 @@ public static void main(String[] args) { // use the error reporting with GUI ErrorReporting.instance = new ErrorReportingIO(); } else { - gui = new WurstGuiCliImpl(); + gui = new WurstGuiCliImpl(runArgs.isCompactOutput()); } if (runArgs.showLastErrors()) { @@ -183,23 +185,50 @@ public static void main(String[] args) { if (gui != null) { gui.sendFinished(); if (!runArgs.isGui()) { - if (gui.getErrorCount() > 0) { - // print error messages - for (CompileError err : gui.getErrorList()) { + if (runArgs.isCompactOutput()) { + printCompactMessages(gui); + } else { + if (gui.getErrorCount() > 0) { + // print error messages + for (CompileError err : gui.getErrorList()) { + System.out.println(err); + } + // signal that there was an error when compiling + System.exit(1); + } + // print warnings: + for (CompileError err : gui.getWarningList()) { System.out.println(err); } - // signal that there was an error when compiling - System.exit(1); } - // print warnings: - for (CompileError err : gui.getWarningList()) { - System.out.println(err); + if (gui.getErrorCount() > 0) { + System.exit(1); } } } } } + private static void printCompactMessages(WurstGui gui) { + if (gui.getErrorCount() > 0) { + System.out.println("Errors: " + gui.getErrorCount()); + for (CompileError err : gui.getErrorList()) { + System.out.println(err.toCompactString()); + } + } + if (!gui.getWarningList().isEmpty()) { + Map warningsByFile = new LinkedHashMap<>(); + for (CompileError err : gui.getWarningList()) { + String file = new File(err.getSource().getFile()).getName(); + warningsByFile.put(file, warningsByFile.getOrDefault(file, 0) + 1); + } + System.out.println("Warnings: " + gui.getWarningList().size()); + for (Map.Entry entry : warningsByFile.entrySet()) { + System.out.println("Warning " + entry.getKey() + ": " + entry.getValue()); + } + } + } + private static void logStartup(String[] args) { // VM Arguments RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean(); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/RunTests.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/RunTests.java index 652e18c97..7c6738f56 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/RunTests.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/RunTests.java @@ -47,6 +47,7 @@ public class RunTests extends UserRequest { private final Optional testName; private final int timeoutSeconds; private final Optional testFilter; + private final boolean compactOutput; private final List successTests = Lists.newArrayList(); private final List failTests = Lists.newArrayList(); @@ -98,12 +99,17 @@ public RunTests(Optional filename, int line, int column, Optional filename, int line, int column, Optional testName, int timeoutSeconds, Optional testFilter) { + this(filename, line, column, testName, timeoutSeconds, testFilter, false); + } + + public RunTests(Optional filename, int line, int column, Optional testName, int timeoutSeconds, Optional testFilter, boolean compactOutput) { this.filename = filename.map(WFile::create); this.line = line; this.column = column; this.testName = testName; this.timeoutSeconds = timeoutSeconds; this.testFilter = testFilter; + this.compactOutput = compactOutput; } @@ -175,7 +181,7 @@ public TestResult runTests(ImTranslator translator, ImProg imProg, Optional 0) { for (CompileError compileError : gui.getErrorList()) { - println(compileError.toString()); + println(compactOutput ? compileError.toCompactString() : compileError.toString()); } println("There were some problem while running compiletime expressions and functions."); return new TestResult(0, 1); @@ -196,7 +202,7 @@ public TestResult runTests(ImTranslator translator, ImProg imProg, Optional 0) { StringBuilder sb = new StringBuilder(); + int appendedErrors = 0; for (CompileError error : gui.getErrorList()) { - sb.append(error).append("\n"); - println(error.getMessage()); + if (compactOutput) { + if (appendedErrors >= 3) { + continue; + } + if (sb.length() > 0) { + sb.append("; "); + } + sb.append(error.toCompactString()); + appendedErrors++; + } else { + sb.append(error).append("\n"); + } + if (!compactOutput) { + println(error.getMessage()); + } + } + if (compactOutput && gui.getErrorCount() > appendedErrors) { + sb.append("; +").append(gui.getErrorCount() - appendedErrors).append(" more"); } gui.clearErrors(); TestFailure failure = new TestFailure(f, interpreter.getStackFrames(), sb.toString()); failTests.add(failure); } else { successTests.add(f); - println("\tOK!"); + if (!compactOutput) { + println("\tOK!"); + } } } catch (TestSuccessException e) { successTests.add(f); - println("\tOK!"); + if (!compactOutput) { + println("\tOK!"); + } } catch (TestFailException e) { TestFailure failure = new TestFailure(f, interpreter.getStackFrames(), e.getMessage()); failTests.add(failure); - println("\tFAILED assertion:"); - println("\t" + failure.getMessageWithStackFrame()); + if (!compactOutput) { + println("\tFAILED assertion:"); + println("\t" + failure.getMessageWithStackFrame()); + } } catch (TestTimeOutException e) { failTests.add(new TestFailure(f, interpreter.getStackFrames(), e.getMessage())); - println("\tFAILED - TIMEOUT (This test did not complete in " + timeoutSeconds + " seconds, it might contain an endless loop)"); - println(interpreter.getStackFrames().toString()); + if (!compactOutput) { + println("\tFAILED - TIMEOUT (This test did not complete in " + timeoutSeconds + " seconds, it might contain an endless loop)"); + println(interpreter.getStackFrames().toString()); + } } catch (InterpreterException e) { TestFailure failure = new TestFailure(f, interpreter.getStackFrames(), e.getMessage()); failTests.add(failure); - println("\t" + failure.getMessageWithStackFrame()); + if (!compactOutput) { + println("\t" + failure.getMessageWithStackFrame()); + } } catch (Throwable e) { failTests.add(new TestFailure(f, interpreter.getStackFrames(), e.toString())); - println("\tFAILED with exception: " + e.getClass() + " " + e.getLocalizedMessage()); - println(interpreter.getStackFrames().toString()); - println("Here are some compiler internals, that might help Wurst developers to debug this issue:"); - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); - e.printStackTrace(pw); - String sStackTrace = sw.toString(); - println("\t" + e.getLocalizedMessage()); - println("\t" + sStackTrace); + if (!compactOutput) { + println("\tFAILED with exception: " + e.getClass() + " " + e.getLocalizedMessage()); + println(interpreter.getStackFrames().toString()); + println("Here are some compiler internals, that might help Wurst developers to debug this issue:"); + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + e.printStackTrace(pw); + String sStackTrace = sw.toString(); + println("\t" + e.getLocalizedMessage()); + println("\t" + sStackTrace); + } } } } } // Scheduler is automatically shut down here - println("Tests succeeded: " + successTests.size() + "/" + (successTests.size() + failTests.size())); - if (failTests.size() == 0) { - println(">> All tests have passed successfully!"); + if (compactOutput) { + println("Tests: " + successTests.size() + "/" + (successTests.size() + failTests.size()) + " passed"); + for (TestFailure failure : failTests) { + println("FAILED " + qualifiedTestName(failure.getFunction())); + } } else { - println(">> " + failTests.size() + " Tests have failed!"); + println("Tests succeeded: " + successTests.size() + "/" + (successTests.size() + failTests.size())); + if (failTests.size() == 0) { + println(">> All tests have passed successfully!"); + } else { + println(">> " + failTests.size() + " Tests have failed!"); + } } if (gui.getErrorCount() > 0) { println("There were some errors reported while running the tests."); for (CompileError error : gui.getErrorList()) { - println(error.toString()); + println(compactOutput ? error.toCompactString() : error.toString()); } } @@ -319,14 +363,16 @@ private void redirectInterpreterOutput(ProgramState globalState) { @Override public void write(int b) throws IOException { - if (b > 0) { + if (!compactOutput && b > 0) { println("" + (char) b); } } @Override public void write(byte[] b, int off, int len) throws IOException { - println(new String(b, off, len)); + if (!compactOutput) { + println(new String(b, off, len)); + } } @@ -334,6 +380,11 @@ public void write(byte[] b, int off, int len) throws IOException { globalState.setOutStream(new PrintStream(os)); } + private String qualifiedTestName(ImFunction f) { + String packageName = f.attrTrace().attrNearestPackage().tryGetNameDef().getName(); + return packageName + "." + f.getName(); + } + protected void println(String message) { print(message); print(System.lineSeparator()); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/RunArgs.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/RunArgs.java index 610c2f47b..3717c05ab 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/RunArgs.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/RunArgs.java @@ -49,6 +49,7 @@ public class RunArgs { private final RunOption optionShowVersion; private final RunOption optionPrettyPrint; private final RunOption optionMeasureTimes; + private final RunOption optionCompactOutput; private final RunOption optionHotStartmap; private final RunOption optionHotReload; private final RunOption optionTestTimeout; @@ -104,6 +105,7 @@ public RunArgs(String... args) { + "some programming errors like null-pointer-dereferences or accessing of destroyed objects can no longer be detected. " + "It is strongly recommended to not use this option, but it can give some performance benefits."); optionMeasureTimes = addOption("measure", "Measure how long each step of the translation process takes."); + optionCompactOutput = addOption("compactOutput", "Print compact CLI output for automated agents."); // tools optionAbout = addOption("-about", "Show the 'about' window."); optionFixInstall = addOption("-fixInstallation", "Checks your wc3 installation and applies compatibility fixes"); @@ -357,6 +359,10 @@ public boolean isMeasureTimes() { return optionMeasureTimes.isSet; } + public boolean isCompactOutput() { + return optionCompactOutput.isSet; + } + public boolean isHotStartmap() { return optionHotStartmap.isSet; } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/CompileError.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/CompileError.java index 15236afb0..fe0ac77a3 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/CompileError.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/CompileError.java @@ -72,6 +72,11 @@ public String toString() { message; } + public String toCompactString() { + File file = new File(source.getFile()); + return errorType + " " + file.getName() + ":" + source.getLine() + ": " + message.replace('\r', ' ').replace('\n', ' '); + } + public ErrorType getErrorType() { return errorType; } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/gui/WurstGuiCliImpl.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/gui/WurstGuiCliImpl.java index dc552661e..8ae417a70 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/gui/WurstGuiCliImpl.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/gui/WurstGuiCliImpl.java @@ -7,6 +7,15 @@ */ public class WurstGuiCliImpl extends WurstGui { + private final boolean compactOutput; + + public WurstGuiCliImpl() { + this(false); + } + + public WurstGuiCliImpl(boolean compactOutput) { + this.compactOutput = compactOutput; + } @Override public void sendError(CompileError err) { @@ -19,6 +28,9 @@ public void sendProgress(String msg) { @Override public void sendFinished() { + if (compactOutput) { + return; + } System.out.println("compilation finished (errors: " + getErrorCount() + ", warnings: " + getWarningList().size() + ")"); } diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/utils/UtilsTest.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/utils/UtilsTest.java index e299176c7..b50f9ea73 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/utils/UtilsTest.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/utils/UtilsTest.java @@ -3,6 +3,9 @@ import de.peeeq.wurstscript.utils.TopsortCycleException; import de.peeeq.wurstscript.utils.Utils; +import de.peeeq.wurstscript.RunArgs; +import de.peeeq.wurstscript.attributes.CompileError; +import de.peeeq.wurstscript.parser.WPos; import org.testng.Assert; import org.testng.annotations.Test; @@ -12,6 +15,18 @@ public class UtilsTest { + @Test + public void compactOutputFlag() { + Assert.assertTrue(new RunArgs("-compactOutput").isCompactOutput()); + } + + @Test + public void compactCompileErrorIsSingleLine() { + CompileError error = new CompileError((WPos) null, "first line\nsecond line"); + Assert.assertFalse(error.toCompactString().contains("\n")); + Assert.assertTrue(error.toCompactString().contains("first line second line")); + } + @Test public void array() { int[] ar1 = {1, 2, 3};