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

C#, JavaScript, Go runtime tests performance improvements #3365

Merged
merged 7 commits into from
Nov 29, 2021
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,10 @@
<GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute>
</PropertyGroup>

<ItemGroup>
<Reference Include="Antlr4.Runtime">
<HintPath>Antlr4.Runtime.Standard.dll</HintPath>
</Reference>
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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() {
Expand Down Expand Up @@ -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);
Expand All @@ -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);
}
Expand Down Expand Up @@ -271,27 +302,11 @@ 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 {
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();
Expand All @@ -302,37 +317,18 @@ 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();
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 writtenOutput;
process.exitValue();
String stdoutString = stdoutVacuum.toString();
String stderrString = stderrVacuum.toString();
setParseErrors(stderrString);
return stdoutString;
} catch (Exception e) {
System.err.println("can't exec recognizer");
e.printStackTrace(System.err);
}
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,
Expand All @@ -341,46 +337,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" +
" <lexerName> lex = new <lexerName>(input, output, errorOutput);\n" +
" CommonTokenStream tokens = new CommonTokenStream(lex);\n" +
" <createParser>\n" +
" parser.BuildParseTree = true;\n" +
" ParserRuleContext tree = parser.<parserStartRuleName>();\n" +
" ParseTreeWalker.Default.Walk(new TreeShapeListener(), tree);\n" +
" }\n" +
" <lexerName> lex = new <lexerName>(input);\n" +
" CommonTokenStream tokens = new CommonTokenStream(lex);\n" +
" <createParser>\n" +
" parser.BuildParseTree = true;\n" +
" ParserRuleContext tree = parser.<parserStartRuleName>();\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(" <parserName> parser = new <parserName>(tokens, output, errorOutput);\n");
ST createParserST = new ST("<parserName> parser = new <parserName>(tokens);\n");
if (debug) {
createParserST =
new ST(
" <parserName> parser = new <parserName>(tokens, output, errorOutput);\n" +
"<parserName> parser = new <parserName>(tokens);\n" +
" parser.AddErrorListener(new DiagnosticErrorListener());\n");
}
outputFileST.add("createParser", createParserST);
Expand All @@ -399,19 +391,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" +
" <lexerName> lex = new <lexerName>(input, output, errorOutput);\n" +
" <lexerName> lex = new <lexerName>(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" +
"}"
);

Expand Down
Loading