From d3cd38123047275c4a5c28d27893652931d2d3c8 Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Sat, 27 Nov 2021 20:58:53 +0300 Subject: [PATCH 1/7] Improve C# runtime tests performance: compile C# runtime once per session and use .dll in test projects --- .../test/runtime/BaseRuntimeTestSupport.java | 10 +- .../v4/test/runtime/csharp/Antlr4.Test.csproj | 6 + .../test/runtime/csharp/BaseCSharpTest.java | 121 ++++++++++-------- 3 files changed, 82 insertions(+), 55 deletions(-) diff --git a/runtime-testsuite/test/org/antlr/v4/test/runtime/BaseRuntimeTestSupport.java b/runtime-testsuite/test/org/antlr/v4/test/runtime/BaseRuntimeTestSupport.java index 8942ede343..4792057899 100644 --- a/runtime-testsuite/test/org/antlr/v4/test/runtime/BaseRuntimeTestSupport.java +++ b/runtime-testsuite/test/org/antlr/v4/test/runtime/BaseRuntimeTestSupport.java @@ -16,9 +16,7 @@ import java.io.File; import java.io.IOException; import java.nio.file.Files; -import java.nio.file.LinkOption; -import java.nio.file.Path; -import java.util.Locale; +import java.util.*; import java.util.logging.Logger; import static org.junit.Assert.assertEquals; @@ -42,6 +40,12 @@ public abstract class BaseRuntimeTestSupport implements RuntimeTestSupport { /** Errors found while running antlr */ private StringBuilder antlrToolErrors; + public static String cachingDirectory; + + static { + cachingDirectory = new File(System.getProperty("java.io.tmpdir"), "ANTLR-runtime-testsuite-cache").getAbsolutePath(); + } + @org.junit.Rule public final TestRule testWatcher = new TestWatcher() { diff --git a/runtime-testsuite/test/org/antlr/v4/test/runtime/csharp/Antlr4.Test.csproj b/runtime-testsuite/test/org/antlr/v4/test/runtime/csharp/Antlr4.Test.csproj index b6436700f4..061e2ff69e 100644 --- a/runtime-testsuite/test/org/antlr/v4/test/runtime/csharp/Antlr4.Test.csproj +++ b/runtime-testsuite/test/org/antlr/v4/test/runtime/csharp/Antlr4.Test.csproj @@ -15,4 +15,10 @@ false + + + Antlr4.Runtime.Standard.dll + + + diff --git a/runtime-testsuite/test/org/antlr/v4/test/runtime/csharp/BaseCSharpTest.java b/runtime-testsuite/test/org/antlr/v4/test/runtime/csharp/BaseCSharpTest.java index e1c713eb76..e7d5da80da 100644 --- a/runtime-testsuite/test/org/antlr/v4/test/runtime/csharp/BaseCSharpTest.java +++ b/runtime-testsuite/test/org/antlr/v4/test/runtime/csharp/BaseCSharpTest.java @@ -9,13 +9,10 @@ import org.antlr.v4.test.runtime.*; import org.stringtemplate.v4.ST; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; +import java.io.*; import java.net.URL; -import java.nio.file.Path; +import java.nio.charset.StandardCharsets; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -27,11 +24,14 @@ import static org.antlr.v4.test.runtime.BaseRuntimeTest.antlrOnString; import static org.antlr.v4.test.runtime.BaseRuntimeTest.writeFile; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; public class BaseCSharpTest extends BaseRuntimeTestSupport implements RuntimeTestSupport { + private static boolean isRuntimeInitialized = false; + private final static String cSharpAntlrRuntimeDllName = "Antlr4.Runtime.Standard.dll"; + private final static String testProjectFileName = "Antlr4.Test.csproj"; + private final static boolean isDebug = false; + private static String cSharpTestProjectContent; @Override protected String getPropertyPrefix() { @@ -184,44 +184,21 @@ public boolean compile() { } private String locateExec() { - return new File(getTempTestDir(), "bin/Release/netcoreapp3.1/Test.dll").getAbsolutePath(); + return new File(getTempTestDir(), "bin/" + getConfig() + "/netcoreapp3.1/Test.dll").getAbsolutePath(); } public boolean buildProject() { try { + assertTrue(initializeRuntime()); + // save auxiliary files - String pack = BaseCSharpTest.class.getPackage().getName().replace(".", "/") + "/"; - saveResourceAsFile(pack + "Antlr4.Test.csproj", new File(getTempTestDir(), "Antlr4.Test.csproj")); - - // find runtime package - final ClassLoader loader = Thread.currentThread().getContextClassLoader(); - final URL runtimeProj = loader.getResource("CSharp/src/Antlr4.csproj"); - if (runtimeProj == null) { - throw new RuntimeException("C# runtime project file not found!"); + try (PrintWriter out = new PrintWriter(new File(getTempTestDir(), testProjectFileName))) { + out.print(cSharpTestProjectContent); } - File runtimeProjFile = new File(runtimeProj.getFile()); - String runtimeProjPath = runtimeProjFile.getPath(); - - // add Runtime project reference - String[] args = new String[]{ - "dotnet", - "add", - "Antlr4.Test.csproj", - "reference", - runtimeProjPath - }; - boolean success = runProcess(args, getTempDirPath()); - assertTrue(success); // build test - args = new String[]{ - "dotnet", - "build", - "Antlr4.Test.csproj", - "-c", - "Release" - }; - success = runProcess(args, getTempDirPath()); + String[] args = new String[] { "dotnet", "build", testProjectFileName, "-c", getConfig() }; + boolean success = runProcess(args, getTempDirPath()); assertTrue(success); } catch (Exception e) { e.printStackTrace(System.err); @@ -231,6 +208,60 @@ public boolean buildProject() { return true; } + private boolean initializeRuntime() { + // Compile runtime project once per tests session + if (isRuntimeInitialized) + return true; + + // find runtime package + final ClassLoader loader = Thread.currentThread().getContextClassLoader(); + final URL runtimeProj = loader.getResource("CSharp/src/Antlr4.csproj"); + if (runtimeProj == null) { + throw new RuntimeException("C# runtime project file not found!"); + } + File runtimeProjFile = new File(runtimeProj.getFile()); + String runtimeProjPath = runtimeProjFile.getPath(); + + RuntimeTestUtils.mkdir(cachingDirectory); + String[] args = new String[]{ + "dotnet", + "build", + runtimeProjPath, + "-c", + "Release", + "-o", + cachingDirectory + }; + + boolean success; + try + { + String cSharpTestProjectResourceName = BaseCSharpTest.class.getPackage().getName().replace(".", "/") + "/"; + InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(cSharpTestProjectResourceName + testProjectFileName); + int bufferSize = 1024; + char[] buffer = new char[bufferSize]; + StringBuilder out = new StringBuilder(); + Reader in = new InputStreamReader(inputStream, StandardCharsets.UTF_8); + for (int numRead; (numRead = in.read(buffer, 0, buffer.length)) > 0; ) { + out.append(buffer, 0, numRead); + } + cSharpTestProjectContent = out.toString().replace(cSharpAntlrRuntimeDllName, Paths.get(cachingDirectory, cSharpAntlrRuntimeDllName).toString()); + + success = runProcess(args, cachingDirectory); + } + catch (Exception e) { + e.printStackTrace(System.err); + return false; + } + + isRuntimeInitialized = true; + return success; + } + + private static String getConfig() { + return isDebug ? "Debug" : "Release"; + } + private boolean runProcess(String[] args, String path) throws Exception { return runProcess(args, path, 0); } @@ -271,20 +302,6 @@ private boolean runProcess(String[] args, String path, int retries) throws Excep return success; } - private void saveResourceAsFile(String resourceName, File file) throws IOException { - InputStream input = Thread.currentThread().getContextClassLoader().getResourceAsStream(resourceName); - if (input == null) { - System.err.println("Can't find " + resourceName + " as resource"); - throw new IOException("Missing resource:" + resourceName); - } - OutputStream output = new FileOutputStream(file.getAbsolutePath()); - while (input.available() > 0) { - output.write(input.read()); - } - output.close(); - input.close(); - } - public String execTest() { String exec = locateExec(); try { From 469515155a94715ff95c69192fe269aa798e7252 Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Sat, 27 Nov 2021 22:55:50 +0300 Subject: [PATCH 2/7] Get rid of dealing with files during C# runtime test running --- .../test/runtime/csharp/BaseCSharpTest.java | 80 +++++++------------ 1 file changed, 31 insertions(+), 49 deletions(-) diff --git a/runtime-testsuite/test/org/antlr/v4/test/runtime/csharp/BaseCSharpTest.java b/runtime-testsuite/test/org/antlr/v4/test/runtime/csharp/BaseCSharpTest.java index e7d5da80da..a17ceceb6a 100644 --- a/runtime-testsuite/test/org/antlr/v4/test/runtime/csharp/BaseCSharpTest.java +++ b/runtime-testsuite/test/org/antlr/v4/test/runtime/csharp/BaseCSharpTest.java @@ -306,9 +306,7 @@ public String execTest() { String exec = locateExec(); try { File tmpdirFile = new File(getTempDirPath()); - Path output = tmpdirFile.toPath().resolve("output"); - Path errorOutput = tmpdirFile.toPath().resolve("error-output"); - String[] args = getExecTestArgs(exec, output, errorOutput); + String[] args = new String[] { "dotnet", exec, new File(getTempTestDir(), "input").getAbsolutePath() }; ProcessBuilder pb = new ProcessBuilder(args); pb.directory(tmpdirFile); Process process = pb.start(); @@ -319,11 +317,10 @@ public String execTest() { process.waitFor(); stdoutVacuum.join(); stderrVacuum.join(); - String writtenOutput = TestOutputReading.read(output); - setParseErrors(TestOutputReading.read(errorOutput)); int exitValue = process.exitValue(); - String stdoutString = stdoutVacuum.toString().trim(); - String stderrString = stderrVacuum.toString().trim(); + String stdoutString = stdoutVacuum.toString(); + String stderrString = stderrVacuum.toString(); + setParseErrors(stderrString); if (exitValue != 0) { System.err.println("execTest command: " + Utils.join(args, " ")); System.err.println("execTest exitValue: " + exitValue); @@ -334,7 +331,7 @@ public String execTest() { if (!stderrString.isEmpty()) { System.err.println("execTest stderrVacuum: " + stderrString); } - return writtenOutput; + return stdoutString; } catch (Exception e) { System.err.println("can't exec recognizer"); e.printStackTrace(System.err); @@ -342,14 +339,6 @@ public String execTest() { return null; } - private String[] getExecTestArgs(String exec, Path output, Path errorOutput) { - return new String[]{ - "dotnet", exec, new File(getTempTestDir(), "input").getAbsolutePath(), - output.toAbsolutePath().toString(), - errorOutput.toAbsolutePath().toString() - }; - } - protected void writeParserTestFile(String parserName, String lexerName, String parserStartRuleName, @@ -358,46 +347,42 @@ protected void writeParserTestFile(String parserName, "using System;\n" + "using Antlr4.Runtime;\n" + "using Antlr4.Runtime.Tree;\n" + - "using System.IO;\n" + "using System.Text;\n" + "\n" + "public class Test {\n" + " public static void Main(string[] args) {\n" + + " Console.OutputEncoding = Encoding.UTF8;\n" + + " Console.InputEncoding = Encoding.UTF8;\n" + " var input = CharStreams.fromPath(args[0]);\n" + - " using (FileStream fsOut = new FileStream(args[1], FileMode.Create, FileAccess.Write))\n" + - " using (FileStream fsErr = new FileStream(args[2], FileMode.Create, FileAccess.Write))\n" + - " using (TextWriter output = new StreamWriter(fsOut),\n" + - " errorOutput = new StreamWriter(fsErr)) {\n" + - " lex = new (input, output, errorOutput);\n" + - " CommonTokenStream tokens = new CommonTokenStream(lex);\n" + - " \n" + - " parser.BuildParseTree = true;\n" + - " ParserRuleContext tree = parser.();\n" + - " ParseTreeWalker.Default.Walk(new TreeShapeListener(), tree);\n" + - " }\n" + + " lex = new (input);\n" + + " CommonTokenStream tokens = new CommonTokenStream(lex);\n" + + " \n" + + " parser.BuildParseTree = true;\n" + + " ParserRuleContext tree = parser.();\n" + + " ParseTreeWalker.Default.Walk(new TreeShapeListener(), tree);\n" + " }\n" + "}\n" + "\n" + "class TreeShapeListener : IParseTreeListener {\n" + - " public void VisitTerminal(ITerminalNode node) { }\n" + - " public void VisitErrorNode(IErrorNode node) { }\n" + - " public void ExitEveryRule(ParserRuleContext ctx) { }\n" + + " public void VisitTerminal(ITerminalNode node) { }\n" + + " public void VisitErrorNode(IErrorNode node) { }\n" + + " public void ExitEveryRule(ParserRuleContext ctx) { }\n" + "\n" + - " public void EnterEveryRule(ParserRuleContext ctx) {\n" + - " for (int i = 0; i \\< ctx.ChildCount; i++) {\n" + - " IParseTree parent = ctx.GetChild(i).Parent;\n" + - " if (!(parent is IRuleNode) || ((IRuleNode)parent).RuleContext != ctx) {\n" + - " throw new Exception(\"Invalid parse tree shape detected.\");\n" + - " }\n" + - " }\n" + - " }\n" + + " public void EnterEveryRule(ParserRuleContext ctx) {\n" + + " for (int i = 0; i \\< ctx.ChildCount; i++) {\n" + + " IParseTree parent = ctx.GetChild(i).Parent;\n" + + " if (!(parent is IRuleNode) || ((IRuleNode)parent).RuleContext != ctx) {\n" + + " throw new Exception(\"Invalid parse tree shape detected.\");\n" + + " }\n" + + " }\n" + + " }\n" + "}" ); - ST createParserST = new ST(" parser = new (tokens, output, errorOutput);\n"); + ST createParserST = new ST(" parser = new (tokens);\n"); if (debug) { createParserST = new ST( - " parser = new (tokens, output, errorOutput);\n" + + " parser = new (tokens);\n" + " parser.AddErrorListener(new DiagnosticErrorListener());\n"); } outputFileST.add("createParser", createParserST); @@ -416,19 +401,16 @@ protected void writeLexerTestFile(String lexerName, boolean showDFA) { "\n" + "public class Test {\n" + " public static void Main(string[] args) {\n" + + " Console.OutputEncoding = Encoding.UTF8;\n" + + " Console.InputEncoding = Encoding.UTF8;\n" + " var input = CharStreams.fromPath(args[0]);\n" + - " using (FileStream fsOut = new FileStream(args[1], FileMode.Create, FileAccess.Write))\n" + - " using (FileStream fsErr = new FileStream(args[2], FileMode.Create, FileAccess.Write))\n" + - " using (TextWriter output = new StreamWriter(fsOut),\n" + - " errorOutput = new StreamWriter(fsErr)) {\n" + - " lex = new (input, output, errorOutput);\n" + + " lex = new (input);\n" + " CommonTokenStream tokens = new CommonTokenStream(lex);\n" + " tokens.Fill();\n" + " foreach (object t in tokens.GetTokens())\n" + - " output.WriteLine(t);\n" + - (showDFA ? " output.Write(lex.Interpreter.GetDFA(Lexer.DEFAULT_MODE).ToLexerString());\n" : "") + + " Console.Out.WriteLine(t);\n" + + (showDFA ? " Console.Out.Write(lex.Interpreter.GetDFA(Lexer.DEFAULT_MODE).ToLexerString());\n" : "") + " }\n" + - "}\n" + "}" ); From 90093c8c4bd179378037de3996aa596b677ae70d Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Sun, 28 Nov 2021 00:36:23 +0300 Subject: [PATCH 3/7] C# runtime tests: don't write useless output to console --- .../antlr/v4/test/runtime/csharp/BaseCSharpTest.java | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/runtime-testsuite/test/org/antlr/v4/test/runtime/csharp/BaseCSharpTest.java b/runtime-testsuite/test/org/antlr/v4/test/runtime/csharp/BaseCSharpTest.java index a17ceceb6a..2a4a57c3a0 100644 --- a/runtime-testsuite/test/org/antlr/v4/test/runtime/csharp/BaseCSharpTest.java +++ b/runtime-testsuite/test/org/antlr/v4/test/runtime/csharp/BaseCSharpTest.java @@ -317,20 +317,10 @@ public String execTest() { process.waitFor(); stdoutVacuum.join(); stderrVacuum.join(); - int exitValue = process.exitValue(); + process.exitValue(); String stdoutString = stdoutVacuum.toString(); String stderrString = stderrVacuum.toString(); setParseErrors(stderrString); - if (exitValue != 0) { - System.err.println("execTest command: " + Utils.join(args, " ")); - System.err.println("execTest exitValue: " + exitValue); - } - if (!stdoutString.isEmpty()) { - System.err.println("execTest stdoutVacuum: " + stdoutString); - } - if (!stderrString.isEmpty()) { - System.err.println("execTest stderrVacuum: " + stderrString); - } return stdoutString; } catch (Exception e) { System.err.println("can't exec recognizer"); From eaed4571af332d7ac18dd5f69f9510407412668a Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Sun, 28 Nov 2021 15:53:06 +0300 Subject: [PATCH 4/7] JavaScript runtime tests: get rid of package installing using npm, use direct file path to runtime files It has significantly improved performance --- .../test/runtime/javascript/BaseNodeTest.java | 130 ++++++------------ 1 file changed, 39 insertions(+), 91 deletions(-) diff --git a/runtime-testsuite/test/org/antlr/v4/test/runtime/javascript/BaseNodeTest.java b/runtime-testsuite/test/org/antlr/v4/test/runtime/javascript/BaseNodeTest.java index b22e632d6c..c14d32b8dc 100644 --- a/runtime-testsuite/test/org/antlr/v4/test/runtime/javascript/BaseNodeTest.java +++ b/runtime-testsuite/test/org/antlr/v4/test/runtime/javascript/BaseNodeTest.java @@ -5,20 +5,36 @@ */ package org.antlr.v4.test.runtime.javascript; -import org.antlr.v4.runtime.misc.Utils; import org.antlr.v4.test.runtime.*; import org.stringtemplate.v4.ST; import java.io.File; import java.io.IOException; +import java.io.PrintWriter; import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.*; - import static org.antlr.v4.test.runtime.BaseRuntimeTest.antlrOnString; import static org.antlr.v4.test.runtime.BaseRuntimeTest.writeFile; import static org.junit.Assert.*; public class BaseNodeTest extends BaseRuntimeTestSupport implements RuntimeTestSupport { + private static String runtimeDir; + + static { + final ClassLoader loader = Thread.currentThread().getContextClassLoader(); + final URL runtimeSrc = loader.getResource("JavaScript"); + if ( runtimeSrc==null ) { + throw new RuntimeException("Cannot find JavaScript runtime"); + } + runtimeDir = runtimeSrc.getPath(); + if(isWindows()){ + runtimeDir = runtimeDir.replaceFirst("/", ""); + } + } @Override protected String getPropertyPrefix() { @@ -99,6 +115,21 @@ protected boolean rawGenerateAndBuildRecognizer(String grammarFileName, + "Visitor.js"); } } + + String newImportAntlrString = "import antlr4 from 'file://" + runtimeDir + "/src/antlr4/index.js'"; + for (String file : files) { + Path path = Paths.get(getTempDirPath(), file); + try { + String content = new String(Files.readAllBytes(path), StandardCharsets.UTF_8); + String newContent = content.replaceAll("import antlr4 from 'antlr4';", newImportAntlrString); + try (PrintWriter out = new PrintWriter(path.toString())) { + out.println(newContent); + } + } catch (IOException e) { + fail("File not found: " + path.toString()); + } + } + return true; // allIsWell: no compile } @@ -121,17 +152,9 @@ public String execRecognizer() { public String execModule(String fileName) { try { - String npmPath = locateNpm(); - if(!TestContext.isCI()) { - installRuntime(npmPath); - registerRuntime(npmPath); - } - String modulePath = new File(getTempTestDir(), fileName) - .getAbsolutePath(); - linkRuntime(npmPath); + String modulePath = new File(getTempTestDir(), fileName).getAbsolutePath(); String nodejsPath = locateNodeJS(); - String inputPath = new File(getTempTestDir(), "input") - .getAbsolutePath(); + String inputPath = new File(getTempTestDir(), "input").getAbsolutePath(); ProcessBuilder builder = new ProcessBuilder(nodejsPath, modulePath, inputPath); builder.environment().put("NODE_PATH", getTempDirPath()); @@ -165,60 +188,6 @@ public String execModule(String fileName) { } } - private void installRuntime(String npmPath) throws IOException, InterruptedException { - String runtimePath = locateRuntime(); - ProcessBuilder builder = new ProcessBuilder(npmPath, "install"); - builder.directory(new File(runtimePath)); - builder.redirectError(new File(getTempTestDir(), "error.txt")); - builder.redirectOutput(new File(getTempTestDir(), "output.txt")); - Process process = builder.start(); - // TODO switch to jdk 8 - process.waitFor(); - // if(!process.waitFor(30L, TimeUnit.SECONDS)) - // process.destroyForcibly(); - int error = process.exitValue(); - if(error!=0) - throw new IOException("'npm install' failed"); - } - - private void registerRuntime(String npmPath) throws IOException, InterruptedException { - String runtimePath = locateRuntime(); - ProcessBuilder builder = new ProcessBuilder(npmPath, "link"); - builder.directory(new File(runtimePath)); - builder.redirectError(new File(getTempTestDir(), "error.txt")); - builder.redirectOutput(new File(getTempTestDir(), "output.txt")); - Process process = builder.start(); - // TODO switch to jdk 8 - process.waitFor(); - // if(!process.waitFor(30L, TimeUnit.SECONDS)) - // process.destroyForcibly(); - int error = process.exitValue(); - if(error!=0) - throw new IOException("'npm link' failed"); - } - - private void linkRuntime(String npmPath) throws IOException, InterruptedException { - List args = new ArrayList<>(); - if(TestContext.isCircleCI()) - args.add("sudo"); - args.addAll(Arrays.asList(npmPath, "link", "antlr4")); - ProcessBuilder builder = new ProcessBuilder(args.toArray(new String[0])); - builder.directory(getTempTestDir()); - File errorFile = new File(getTempTestDir(), "error.txt"); - builder.redirectError(errorFile); - builder.redirectOutput(new File(getTempTestDir(), "output.txt")); - Process process = builder.start(); - // TODO switch to jdk 8 - process.waitFor(); - // if(!process.waitFor(30L, TimeUnit.SECONDS)) - // process.destroyForcibly(); - int error = process.exitValue(); - if(error!=0) { - char[] errors = Utils.readFile(errorFile.getAbsolutePath()); - throw new IOException("'npm link antlr4' failed: " + new String(errors)); - } - } - private boolean canExecute(String tool) { try { ProcessBuilder builder = new ProcessBuilder(tool, "--version"); @@ -237,17 +206,6 @@ private boolean canExecute(String tool) { } } - private String locateNpm() { - // typically /usr/local/bin/npm - String prop = System.getProperty("antlr-javascript-npm"); - if ( prop!=null && prop.length()!=0 ) { - if(prop.contains(" ")) - prop = "\"" + prop + "\""; - return prop; - } - return "npm"; // everywhere - } - private String locateNodeJS() { // typically /usr/local/bin/node String prop = System.getProperty("antlr-javascript-nodejs"); @@ -262,23 +220,11 @@ private String locateNodeJS() { return "node"; // everywhere else } - private String locateRuntime() { - final ClassLoader loader = Thread.currentThread().getContextClassLoader(); - final URL runtimeSrc = loader.getResource("JavaScript"); - if ( runtimeSrc==null ) { - throw new RuntimeException("Cannot find JavaScript runtime"); - } - if(isWindows()){ - return runtimeSrc.getPath().replaceFirst("/", ""); - } - return runtimeSrc.getPath(); - } - protected void writeParserTestFile(String parserName, String lexerName, String listenerName, String visitorName, String parserStartRuleName, boolean debug) { ST outputFileST = new ST( - "import antlr4 from 'antlr4';\n" + "import antlr4 from 'file:///src/antlr4/index.js'\n" + "import from './.js';\n" + "import from './.js';\n" + "import from './.js';\n" @@ -324,12 +270,13 @@ protected void writeParserTestFile(String parserName, String lexerName, outputFileST.add("listenerName", listenerName); outputFileST.add("visitorName", visitorName); outputFileST.add("parserStartRuleName", parserStartRuleName); + outputFileST.add("runtimeDir", runtimeDir); writeFile(getTempDirPath(), "Test.js", outputFileST.render()); } protected void writeLexerTestFile(String lexerName, boolean showDFA) { ST outputFileST = new ST( - "import antlr4 from 'antlr4';\n" + "import antlr4 from 'file:///src/antlr4/index.js'\n" + "import from './.js';\n" + "\n" + "function main(argv) {\n" @@ -344,6 +291,7 @@ protected void writeLexerTestFile(String lexerName, boolean showDFA) { : "") + "}\n" + "\n" + "main(process.argv);\n" + "\n"); outputFileST.add("lexerName", lexerName); + outputFileST.add("runtimeDir", runtimeDir); writeFile(getTempDirPath(), "Test.js", outputFileST.render()); } From 67e34fbebb0326d06f04a745485b60843d1362fc Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Sun, 28 Nov 2021 18:40:32 +0300 Subject: [PATCH 5/7] Go runtime tests: get rid of useless copying of runtime files on every test run It has significantly improved performance --- .../antlr/v4/test/runtime/go/BaseGoTest.java | 175 ++++++++---------- 1 file changed, 74 insertions(+), 101 deletions(-) diff --git a/runtime-testsuite/test/org/antlr/v4/test/runtime/go/BaseGoTest.java b/runtime-testsuite/test/org/antlr/v4/test/runtime/go/BaseGoTest.java index 2e0b391528..5231260af4 100644 --- a/runtime-testsuite/test/org/antlr/v4/test/runtime/go/BaseGoTest.java +++ b/runtime-testsuite/test/org/antlr/v4/test/runtime/go/BaseGoTest.java @@ -7,24 +7,27 @@ import org.antlr.v4.test.runtime.*; +import org.junit.Assert; import org.stringtemplate.v4.ST; -import java.io.File; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.BufferedReader; -import java.io.BufferedWriter; +import java.io.*; import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import static junit.framework.TestCase.*; import static org.antlr.v4.test.runtime.BaseRuntimeTest.antlrOnString; import static org.antlr.v4.test.runtime.BaseRuntimeTest.writeFile; +import static org.junit.Assert.fail; public class BaseGoTest extends BaseRuntimeTestSupport implements RuntimeTestSupport { - + private final static String antlrTestPackageName = "antlr.test"; private static final String GO_RUNTIME_IMPORT_PATH = "github.com/antlr/antlr4/runtime/Go/antlr"; // TODO: Change this before merging with upstream + private static boolean isRuntimeInitialized = false; + private static String newGoPath; private File parserTempDir; // "parser" with tempDir @@ -36,37 +39,6 @@ protected String getPropertyPrefix() { public static void groupSetUp() throws Exception { } public static void groupTearDown() throws Exception { } - private void setupAntlrRuntime() throws Exception { - File packageDir = new File(getTempParserDir(), "antlr"); - if (!packageDir.mkdirs()) { - throw new Exception("Cannot make directory for runtime"); - } - File[] runtimeFiles = locateRuntime().listFiles(new GoFileFilter()); - if (runtimeFiles == null) { - throw new Exception("Go runtime file list is empty."); - } - for (File runtimeFile : runtimeFiles) { - File dest = new File(packageDir, runtimeFile.getName()); - - RuntimeTestUtils.copyFile(runtimeFile, dest); - } - } - - private void setupGoMod() throws Exception { - String goExecutable = locateGo(); - ProcessBuilder pb = new ProcessBuilder(goExecutable, "mod", "init", "antlr.org/test"); - pb.directory(getTempTestDir()); - pb.redirectErrorStream(true); - Process process = pb.start(); - StreamVacuum sucker = new StreamVacuum(process.getInputStream()); - sucker.start(); - int exit = process.waitFor(); - sucker.join(); - if (exit != 0) { - throw new Exception("Non-zero exit while setting up go module: " + sucker.toString()); - } - } - public void testSetUp() throws Exception { eraseParserTempDir(); super.testSetUp(); @@ -96,6 +68,7 @@ public String execLexer(String grammarFileName, String grammarStr, boolean success = rawGenerateAndBuildRecognizer(grammarFileName, grammarStr, null, lexerName, "-no-listener"); assertTrue(success); + replaceImportPath(); writeFile(getTempDirPath(), "input", input); writeLexerTestFile(lexerName, showDFA); return execModule("Test.go"); @@ -110,10 +83,27 @@ public String execParser(String grammarFileName, String grammarStr, boolean success = rawGenerateAndBuildRecognizer(grammarFileName, grammarStr, parserName, lexerName, "-visitor"); assertTrue(success); + replaceImportPath(); writeFile(getTempDirPath(), "input", input); rawBuildRecognizerTestFile(parserName, lexerName, listenerName, visitorName, startRuleName, showDiagnosticErrors); - return execRecognizer(); + return execModule("Test.go"); + } + + private void replaceImportPath() { + File[] files = getTempParserDir().listFiles(new GoFileFilter()); + for (File file : files) { + String fileName = file.toString(); + try { + String content = new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8); + String newContent = content.replaceAll(GO_RUNTIME_IMPORT_PATH, antlrTestPackageName); + try (PrintWriter out = new PrintWriter(fileName)) { + out.println(newContent); + } + } catch (IOException e) { + fail("Error during processing " + fileName); + } + } } /** Return true if all is well */ @@ -149,17 +139,16 @@ protected void rawBuildRecognizerTestFile(String parserName, } } - public String execRecognizer() { - return execModule("Test.go"); - } + private String execModule(String fileName) { + initializeRuntime(); - public String execModule(String fileName) { String goExecutable = locateGo(); String modulePath = new File(getTempTestDir(), fileName).getAbsolutePath(); String inputPath = new File(getTempTestDir(), "input").getAbsolutePath(); try { ProcessBuilder builder = new ProcessBuilder(goExecutable, "run", modulePath, inputPath); builder.directory(getTempTestDir()); + builder.environment().put("GOPATH", newGoPath); Process process = builder.start(); StreamVacuum stdoutVacuum = new StreamVacuum(process.getInputStream()); StreamVacuum stderrVacuum = new StreamVacuum(process.getErrorStream()); @@ -184,6 +173,44 @@ public String execModule(String fileName) { return null; } + private boolean initializeRuntime() { + if (isRuntimeInitialized) + return true; + + newGoPath = Paths.get(cachingDirectory, "Go").toString(); + String packageDir = Paths.get(newGoPath, "src", antlrTestPackageName).toString(); + RuntimeTestUtils.mkdir(packageDir); + File[] runtimeFiles = locateRuntime().listFiles(new GoFileFilter()); + if (runtimeFiles == null) { + Assert.fail("Go runtime file list is empty."); + } + + for (File runtimeFile : runtimeFiles) { + File dest = new File(packageDir, runtimeFile.getName()); + try { + RuntimeTestUtils.copyFile(runtimeFile, dest); + } catch (IOException e) { + e.printStackTrace(); + Assert.fail("Unable to copy runtime file " + runtimeFile); + } + } + + isRuntimeInitialized = true; + return isRuntimeInitialized; + } + + private static String locateGo() { + String propName = "antlr-go"; + String prop = System.getProperty(propName); + if (prop == null || prop.length() == 0) { + prop = locateTool("go"); + } + if (prop == null) { + throw new RuntimeException("Missing system property:" + propName); + } + return prop; + } + private static String locateTool(String tool) { ArrayList paths = new ArrayList(); // default cap is about right @@ -215,18 +242,6 @@ private static String locateTool(String tool) { return null; } - private static String locateGo() { - String propName = "antlr-go"; - String prop = System.getProperty(propName); - if (prop == null || prop.length() == 0) { - prop = locateTool("go"); - } - if (prop == null) { - throw new RuntimeException("Missing system property:" + propName); - } - return prop; - } - private static File locateRuntime() { final ClassLoader loader = Thread.currentThread().getContextClassLoader(); final URL runtimeSrc = loader.getResource("Go"); @@ -237,42 +252,17 @@ private static File locateRuntime() { if (!runtimeDir.exists()) { throw new RuntimeException("Cannot find Go ANTLR runtime"); } - return runtimeDir; + return new File(runtimeDir.getPath()); } - private void replaceImportPath() throws Exception { - File[] files = getTempParserDir().listFiles(new GoFileFilter()); - for (File file: files) { - File temp = File.createTempFile( - file.getName(), - ".bak", - file.getParentFile() - ); - BufferedReader br = new BufferedReader(new FileReader(file)); - BufferedWriter bw = new BufferedWriter(new FileWriter(temp)); - String s = ""; - - while ((s = br.readLine()) != null) { - bw.write(s.replace(GO_RUNTIME_IMPORT_PATH, "antlr.org/test/parser/antlr")); - bw.newLine(); - } - - br.close(); - bw.close(); - - file.delete(); - temp.renameTo(file); - } - } - protected void writeParserTestFile(String parserName, String lexerName, String listenerName, String visitorName, String parserStartRuleName, boolean debug) { ST outputFileST = new ST( "package main\n" + "import (\n" - + " \"antlr.org/test/parser\"\n" - + " \"antlr.org/test/parser/antlr\"\n" + + " \"./parser\"\n" + + " \"" + antlrTestPackageName + "\"\n" + " \"fmt\"\n" + " \"os\"\n" + ")\n" @@ -322,24 +312,15 @@ protected void writeParserTestFile(String parserName, String lexerName, outputFileST.add("listenerName", listenerName); outputFileST.add("visitorName", visitorName); outputFileST.add("parserStartRuleName", parserStartRuleName.substring(0, 1).toUpperCase() + parserStartRuleName.substring(1) ); - try { - setupGoMod(); - setupAntlrRuntime(); - replaceImportPath(); - } catch (Exception e) { - // - } writeFile(getTempDirPath(), "Test.go", outputFileST.render()); } - - protected void writeLexerTestFile(String lexerName, boolean showDFA) { ST outputFileST = new ST( "package main\n" + "import (\n" - + " \"antlr.org/test/parser\"\n" - + " \"antlr.org/test/parser/antlr\"\n" + + " \"./parser\"\n" + + " \"" + antlrTestPackageName + "\"\n" + " \"os\"\n" + " \"fmt\"\n" + ")\n" @@ -361,14 +342,6 @@ protected void writeLexerTestFile(String lexerName, boolean showDFA) { + "}\n" + "\n"); outputFileST.add("lexerName", lexerName); - try { - setupGoMod(); - setupAntlrRuntime(); - replaceImportPath(); - } catch (Exception e) { - // - } writeFile(getTempDirPath(), "Test.go", outputFileST.render()); } - } From 7a87cdcb3d13d3cb5942da29ae537d99e1cbabba Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Mon, 29 Nov 2021 00:49:36 +0300 Subject: [PATCH 6/7] Go runtime tests: fix working with the latest version of GO, use GOROOT redirection instead of GOPATH --- .../antlr/v4/test/runtime/go/BaseGoTest.java | 119 ++++++++++-------- 1 file changed, 69 insertions(+), 50 deletions(-) diff --git a/runtime-testsuite/test/org/antlr/v4/test/runtime/go/BaseGoTest.java b/runtime-testsuite/test/org/antlr/v4/test/runtime/go/BaseGoTest.java index 5231260af4..3f661e1007 100644 --- a/runtime-testsuite/test/org/antlr/v4/test/runtime/go/BaseGoTest.java +++ b/runtime-testsuite/test/org/antlr/v4/test/runtime/go/BaseGoTest.java @@ -13,10 +13,8 @@ import java.io.*; import java.net.URL; import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Arrays; +import java.nio.file.*; +import java.nio.file.attribute.BasicFileAttributes; import static junit.framework.TestCase.*; import static org.antlr.v4.test.runtime.BaseRuntimeTest.antlrOnString; @@ -24,10 +22,10 @@ import static org.junit.Assert.fail; public class BaseGoTest extends BaseRuntimeTestSupport implements RuntimeTestSupport { - private final static String antlrTestPackageName = "antlr.test"; + private final static String antlrTestPackageName = "antlr"; private static final String GO_RUNTIME_IMPORT_PATH = "github.com/antlr/antlr4/runtime/Go/antlr"; // TODO: Change this before merging with upstream private static boolean isRuntimeInitialized = false; - private static String newGoPath; + private static String newGoRootString; private File parserTempDir; // "parser" with tempDir @@ -142,13 +140,12 @@ protected void rawBuildRecognizerTestFile(String parserName, private String execModule(String fileName) { initializeRuntime(); - String goExecutable = locateGo(); String modulePath = new File(getTempTestDir(), fileName).getAbsolutePath(); String inputPath = new File(getTempTestDir(), "input").getAbsolutePath(); try { - ProcessBuilder builder = new ProcessBuilder(goExecutable, "run", modulePath, inputPath); + ProcessBuilder builder = new ProcessBuilder("go", "run", modulePath, inputPath); builder.directory(getTempTestDir()); - builder.environment().put("GOPATH", newGoPath); + builder.environment().put("GOROOT", newGoRootString); Process process = builder.start(); StreamVacuum stdoutVacuum = new StreamVacuum(process.getInputStream()); StreamVacuum stderrVacuum = new StreamVacuum(process.getErrorStream()); @@ -177,8 +174,17 @@ private boolean initializeRuntime() { if (isRuntimeInitialized) return true; - newGoPath = Paths.get(cachingDirectory, "Go").toString(); - String packageDir = Paths.get(newGoPath, "src", antlrTestPackageName).toString(); + String goRoot = getGoRootValue(); + Path newGoRoot = Paths.get(cachingDirectory, "Go"); + newGoRootString = newGoRoot.toString(); + try { + copyDirectory(Paths.get(goRoot), newGoRoot, StandardCopyOption.REPLACE_EXISTING); + } catch (IOException e) { + e.printStackTrace(); + Assert.fail("Unable to copy go system files"); + } + + String packageDir = Paths.get(newGoRootString, "src", antlrTestPackageName).toString(); RuntimeTestUtils.mkdir(packageDir); File[] runtimeFiles = locateRuntime().listFiles(new GoFileFilter()); if (runtimeFiles == null) { @@ -199,45 +205,37 @@ private boolean initializeRuntime() { return isRuntimeInitialized; } - private static String locateGo() { - String propName = "antlr-go"; - String prop = System.getProperty(propName); - if (prop == null || prop.length() == 0) { - prop = locateTool("go"); - } - if (prop == null) { - throw new RuntimeException("Missing system property:" + propName); - } - return prop; - } - - private static String locateTool(String tool) { - ArrayList paths = new ArrayList(); // default cap is about right - - // GOROOT should have priority if set - String goroot = System.getenv("GOROOT"); - if (goroot != null) { - paths.add(goroot + File.separatorChar + "bin"); - } - - String pathEnv = System.getenv("PATH"); - if (pathEnv != null) { - paths.addAll(Arrays.asList(pathEnv.split(File.pathSeparator))); - } - - // OS specific default locations of binary dist as last resort - paths.add("/usr/local/go/bin"); - paths.add("c:\\Go\\bin"); - - for (String path : paths) { - File candidate = new File(new File(path), tool); - if (candidate.exists()) { - return candidate.getPath(); + private void copyDirectory(final Path source, final Path target, final CopyOption... options) + throws IOException { + Files.walkFileTree(source, new SimpleFileVisitor() { + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) + throws IOException { + Files.createDirectories(target.resolve(source.relativize(dir))); + return FileVisitResult.CONTINUE; } - candidate = new File(new File(path), tool+".exe"); - if (candidate.exists()) { - return candidate.getPath(); + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) + throws IOException { + Files.copy(file, target.resolve(source.relativize(file)), options); + return FileVisitResult.CONTINUE; } + }); + } + + private static String getGoRootValue() { + try { + ProcessBuilder pb = new ProcessBuilder("go", "env", "GOROOT"); + Process process = pb.start(); + StreamVacuum stdoutVacuum = new StreamVacuum(process.getInputStream()); + stdoutVacuum.start(); + process.waitFor(); + stdoutVacuum.join(); + return stdoutVacuum.toString().trim(); + } catch (Exception e) { + e.printStackTrace(); + Assert.fail("Unable to execute go env"); } return null; } @@ -261,7 +259,7 @@ protected void writeParserTestFile(String parserName, String lexerName, ST outputFileST = new ST( "package main\n" + "import (\n" - + " \"./parser\"\n" + + " \"test/parser\"\n" + " \"" + antlrTestPackageName + "\"\n" + " \"fmt\"\n" + " \"os\"\n" @@ -312,6 +310,7 @@ protected void writeParserTestFile(String parserName, String lexerName, outputFileST.add("listenerName", listenerName); outputFileST.add("visitorName", visitorName); outputFileST.add("parserStartRuleName", parserStartRuleName.substring(0, 1).toUpperCase() + parserStartRuleName.substring(1) ); + setupGoMod(); writeFile(getTempDirPath(), "Test.go", outputFileST.render()); } @@ -319,7 +318,7 @@ protected void writeLexerTestFile(String lexerName, boolean showDFA) { ST outputFileST = new ST( "package main\n" + "import (\n" - + " \"./parser\"\n" + + " \"test/parser\"\n" + " \"" + antlrTestPackageName + "\"\n" + " \"os\"\n" + " \"fmt\"\n" @@ -342,6 +341,26 @@ protected void writeLexerTestFile(String lexerName, boolean showDFA) { + "}\n" + "\n"); outputFileST.add("lexerName", lexerName); + setupGoMod(); writeFile(getTempDirPath(), "Test.go", outputFileST.render()); } + + private void setupGoMod(){ + try { + ProcessBuilder pb = new ProcessBuilder("go", "mod", "init", "test"); + pb.directory(getTempTestDir()); + pb.redirectErrorStream(true); + Process process = pb.start(); + StreamVacuum sucker = new StreamVacuum(process.getInputStream()); + sucker.start(); + int exit = process.waitFor(); + sucker.join(); + if (exit != 0) { + throw new Exception("Non-zero exit while setting up go module: " + sucker); + } + } catch (Exception e) { + e.printStackTrace(); + Assert.fail("Unable to execute go mod"); + } + } } From d49facf3b22091b570d03f69a1263f14eb9e5a44 Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Mon, 29 Nov 2021 01:04:28 +0300 Subject: [PATCH 7/7] Go runtime tests: cache go.mod file, don't run go mod command every test --- .../antlr/v4/test/runtime/go/BaseGoTest.java | 54 +++++++++++-------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/runtime-testsuite/test/org/antlr/v4/test/runtime/go/BaseGoTest.java b/runtime-testsuite/test/org/antlr/v4/test/runtime/go/BaseGoTest.java index 3f661e1007..51be7cfddd 100644 --- a/runtime-testsuite/test/org/antlr/v4/test/runtime/go/BaseGoTest.java +++ b/runtime-testsuite/test/org/antlr/v4/test/runtime/go/BaseGoTest.java @@ -23,9 +23,11 @@ public class BaseGoTest extends BaseRuntimeTestSupport implements RuntimeTestSupport { private final static String antlrTestPackageName = "antlr"; + private static final String goModFileName = "go.mod"; private static final String GO_RUNTIME_IMPORT_PATH = "github.com/antlr/antlr4/runtime/Go/antlr"; // TODO: Change this before merging with upstream private static boolean isRuntimeInitialized = false; private static String newGoRootString; + private static String goModContent = null; private File parserTempDir; // "parser" with tempDir @@ -69,6 +71,7 @@ public String execLexer(String grammarFileName, String grammarStr, replaceImportPath(); writeFile(getTempDirPath(), "input", input); writeLexerTestFile(lexerName, showDFA); + writeGoModFile(); return execModule("Test.go"); } @@ -83,11 +86,41 @@ public String execParser(String grammarFileName, String grammarStr, assertTrue(success); replaceImportPath(); writeFile(getTempDirPath(), "input", input); + writeGoModFile(); rawBuildRecognizerTestFile(parserName, lexerName, listenerName, visitorName, startRuleName, showDiagnosticErrors); return execModule("Test.go"); } + private void writeGoModFile() { + if (goModContent == null) { + try { + ProcessBuilder pb = new ProcessBuilder("go", "mod", "init", "test"); + pb.directory(getTempTestDir()); + pb.redirectErrorStream(true); + Process process = pb.start(); + StreamVacuum sucker = new StreamVacuum(process.getInputStream()); + sucker.start(); + int exit = process.waitFor(); + sucker.join(); + if (exit != 0) { + throw new Exception("Non-zero exit while setting up go module: " + sucker); + } + goModContent = new String(Files.readAllBytes(Paths.get(getTempDirPath(), goModFileName)), StandardCharsets.UTF_8); + } catch (Exception e) { + e.printStackTrace(); + Assert.fail("Unable to execute go mod"); + } + } else { + try (PrintWriter out = new PrintWriter(Paths.get(getTempDirPath(), goModFileName).toString())) { + out.println(goModContent); + } catch (FileNotFoundException e) { + e.printStackTrace(); + Assert.fail("Unable to write " + goModFileName); + } + } + } + private void replaceImportPath() { File[] files = getTempParserDir().listFiles(new GoFileFilter()); for (File file : files) { @@ -310,7 +343,6 @@ protected void writeParserTestFile(String parserName, String lexerName, outputFileST.add("listenerName", listenerName); outputFileST.add("visitorName", visitorName); outputFileST.add("parserStartRuleName", parserStartRuleName.substring(0, 1).toUpperCase() + parserStartRuleName.substring(1) ); - setupGoMod(); writeFile(getTempDirPath(), "Test.go", outputFileST.render()); } @@ -341,26 +373,6 @@ protected void writeLexerTestFile(String lexerName, boolean showDFA) { + "}\n" + "\n"); outputFileST.add("lexerName", lexerName); - setupGoMod(); writeFile(getTempDirPath(), "Test.go", outputFileST.render()); } - - private void setupGoMod(){ - try { - ProcessBuilder pb = new ProcessBuilder("go", "mod", "init", "test"); - pb.directory(getTempTestDir()); - pb.redirectErrorStream(true); - Process process = pb.start(); - StreamVacuum sucker = new StreamVacuum(process.getInputStream()); - sucker.start(); - int exit = process.waitFor(); - sucker.join(); - if (exit != 0) { - throw new Exception("Non-zero exit while setting up go module: " + sucker); - } - } catch (Exception e) { - e.printStackTrace(); - Assert.fail("Unable to execute go mod"); - } - } }