Skip to content

Commit

Permalink
Merge pull request #3365 from KvanTTT/csharp-runtime-tests-performance
Browse files Browse the repository at this point in the history
C#, JavaScript, Go runtime tests performance improvements
  • Loading branch information
ericvergnaud authored Nov 29, 2021
2 parents 1493859 + d49facf commit e811330
Show file tree
Hide file tree
Showing 5 changed files with 280 additions and 329 deletions.
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

0 comments on commit e811330

Please sign in to comment.