From aeb1e3f9676705f3a05a54bbce175151825b0d7e Mon Sep 17 00:00:00 2001 From: Johannes Mey <johannes.mey@tu-dresden.de> Date: Thu, 4 Feb 2021 22:41:10 +0100 Subject: [PATCH] update test infrastructure --- build.gradle | 1 + src/main/jastadd/backend/AbstractGrammar.jadd | 24 ++-- .../org/jastadd/relast/compiler/Mustache.java | 30 ++++- .../relast/compiler/RelAstProcessor.java | 2 +- .../relast/tests/PreprocessorTest.java | 12 +- src/test/resources/Comments/config.yaml | 8 ++ .../resources/Comments/in/CommentsA.relast | 15 +++ src/test/resources/MinimalGrammar/config.yaml | 9 +- .../MinimalGrammar/in/CommentInFront.relast | 2 +- .../MinimalGrammar/in/Example.relast | 2 +- .../relast/tests/RelAstProcessorTestBase.java | 113 ++++++++++++------ .../relast/tests/config/Configuration.java | 41 ++++++- 12 files changed, 186 insertions(+), 73 deletions(-) create mode 100644 src/test/resources/Comments/config.yaml create mode 100644 src/test/resources/Comments/in/CommentsA.relast diff --git a/build.gradle b/build.gradle index c9f5a8d..03170b2 100644 --- a/build.gradle +++ b/build.gradle @@ -53,6 +53,7 @@ dependencies { testFixturesApi group: 'org.assertj', name: 'assertj-core', version: '3.18.0' testFixturesApi group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.12.0-rc1' testFixturesApi group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: '2.12.0-rc1' + testFixturesApi group: 'commons-io', name: 'commons-io', version: '2.8.0' } test { diff --git a/src/main/jastadd/backend/AbstractGrammar.jadd b/src/main/jastadd/backend/AbstractGrammar.jadd index da803c5..3683fe2 100644 --- a/src/main/jastadd/backend/AbstractGrammar.jadd +++ b/src/main/jastadd/backend/AbstractGrammar.jadd @@ -43,14 +43,16 @@ aspect BackendAbstractGrammar { if (getAbstract()) { b.append("abstract "); } - b.append(getName()).append(" "); + b.append(getName()); if (hasSuperType()) { - b.append(": ").append(getSuperType().getName()).append(" "); + b.append(" : ").append(getSuperType().getName()); } - b.append("::="); - for (Component component : getComponentList()) { - b.append(" "); - component.generateAbstractGrammar(b); + if (getNumComponent() > 0) { + b.append(" ::="); + for (Component component : getComponentList()) { + b.append(" "); + component.generateAbstractGrammar(b); + } } b.append(";"); super.generateAbstractGrammar(b); @@ -62,8 +64,7 @@ aspect BackendAbstractGrammar { if (getNTA()) { b.append("/"); } - - if (!getName().equals("")) { + if (!getName().equals("") && !getName().equals(getTypeDecl().getName())) { b.append(getName()).append(":"); } b.append(getTypeDecl().getName()); @@ -76,8 +77,7 @@ aspect BackendAbstractGrammar { if (getNTA()) { b.append("/"); } - - if (!getName().equals("")) { + if (!getName().equals("") && !getName().equals(getTypeDecl().getName())) { b.append(getName()).append(":"); } b.append(getTypeDecl().getName()).append("*"); @@ -91,7 +91,7 @@ aspect BackendAbstractGrammar { b.append("/"); } b.append("["); - if (!getName().equals("")) { + if (!getName().equals("") && !getName().equals(getTypeDecl().getName())) { b.append(getName()).append(":"); } b.append(getTypeDecl().getName()).append("]"); @@ -187,7 +187,7 @@ aspect BackendAbstractGrammar { } public void SingleLineComment.generateAbstractGrammar(StringBuilder b) { - b.append("//").append(getText()).append("\n"); + b.append("//").append(getText()); } public void MultiLineComment.generateAbstractGrammar(StringBuilder b) { diff --git a/src/main/java/org/jastadd/relast/compiler/Mustache.java b/src/main/java/org/jastadd/relast/compiler/Mustache.java index 3b428ab..24d368a 100644 --- a/src/main/java/org/jastadd/relast/compiler/Mustache.java +++ b/src/main/java/org/jastadd/relast/compiler/Mustache.java @@ -6,16 +6,34 @@ import com.github.jknack.handlebars.io.ClassPathTemplateLoader; import com.github.jknack.handlebars.io.TemplateLoader; import org.yaml.snakeyaml.Yaml; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.io.Writer; +import java.io.*; +import java.nio.file.Paths; public class Mustache { - public static void javaMustache(String templateFileName, String yamlFileName, String outputFileName) throws IOException { + private Mustache() { + // hide public constructor + } + + public static void javaMustache(String templateFileName, File yamlFile, String outputFileName) throws IOException { + + //noinspection ResultOfMethodCallIgnored + Paths.get(outputFileName).getParent().toFile().mkdirs(); // create directory structure if necessary + + Object context = new Yaml().load(new FileReader(yamlFile)); + applyTemplate(templateFileName, outputFileName, context); + } + + public static void javaMustache(String templateFileName, String yaml, String outputFileName) throws IOException { + + //noinspection ResultOfMethodCallIgnored + Paths.get(outputFileName).getParent().toFile().mkdirs(); // create directory structure if necessary + + Object context = new Yaml().load(new StringReader(yaml)); + applyTemplate(templateFileName, outputFileName, context); + } - Object context = new Yaml().load(new FileReader(yamlFileName)); + private static void applyTemplate(String templateFileName, String outputFileName, Object context) throws IOException { TemplateLoader loader = new ClassPathTemplateLoader(); loader.setSuffix(".mustache"); // the default is ".hbs" diff --git a/src/main/java/org/jastadd/relast/compiler/RelAstProcessor.java b/src/main/java/org/jastadd/relast/compiler/RelAstProcessor.java index b5624f2..4e3c97d 100644 --- a/src/main/java/org/jastadd/relast/compiler/RelAstProcessor.java +++ b/src/main/java/org/jastadd/relast/compiler/RelAstProcessor.java @@ -21,7 +21,7 @@ public abstract class RelAstProcessor extends AbstractCompiler { protected ValueOption optionOutputBaseDir; protected ValueOption optionInputBaseDir; - public RelAstProcessor(String name, boolean jastAddCompliant) { + protected RelAstProcessor(String name, boolean jastAddCompliant) { super(name, jastAddCompliant); } diff --git a/src/test/java/org/jastadd/relast/tests/PreprocessorTest.java b/src/test/java/org/jastadd/relast/tests/PreprocessorTest.java index c6fde6f..b583496 100644 --- a/src/test/java/org/jastadd/relast/tests/PreprocessorTest.java +++ b/src/test/java/org/jastadd/relast/tests/PreprocessorTest.java @@ -1,15 +1,13 @@ package org.jastadd.relast.tests; import org.jastadd.relast.compiler.RelastSourceToSourceCompiler; -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.nio.file.Paths; +import org.junit.jupiter.api.BeforeAll; public class PreprocessorTest extends RelAstProcessorTestBase { - @Test - void testMinimalGrammar() throws IOException, InterruptedException { - directoryTest(RelastSourceToSourceCompiler.class, Paths.get("src/test/resources/MinimalGrammar")); + @BeforeAll + static void init() { + mainClass = RelastSourceToSourceCompiler.class; } + } diff --git a/src/test/resources/Comments/config.yaml b/src/test/resources/Comments/config.yaml new file mode 100644 index 0000000..6095c8d --- /dev/null +++ b/src/test/resources/Comments/config.yaml @@ -0,0 +1,8 @@ +- name: "Parse and reprint tests" + args: + - "--inputBaseDir=in" + - "--outputBaseDir=out" + - "CommentsA.relast" + out: "out" + expected: "in" + compare: true diff --git a/src/test/resources/Comments/in/CommentsA.relast b/src/test/resources/Comments/in/CommentsA.relast new file mode 100644 index 0000000..37e963f --- /dev/null +++ b/src/test/resources/Comments/in/CommentsA.relast @@ -0,0 +1,15 @@ +// this file only contains comments +// + +// + +// like this one +/* or this one */ +/* or this + one */ +/**/ +//test +/*test*/ + +// + diff --git a/src/test/resources/MinimalGrammar/config.yaml b/src/test/resources/MinimalGrammar/config.yaml index c22d8d5..135f6bd 100644 --- a/src/test/resources/MinimalGrammar/config.yaml +++ b/src/test/resources/MinimalGrammar/config.yaml @@ -1,10 +1,9 @@ -- name: "MinimalGrammar" +- name: "Parse and reprint tests" args: - "--inputBaseDir=in" - "--outputBaseDir=out" - "Example.relast" -- name: "Comments in front" - args: - - "--inputBaseDir=in" - - "--outputBaseDir=out" - "CommentInFront.relast" + out: "out" + expected: "in" + compare: true diff --git a/src/test/resources/MinimalGrammar/in/CommentInFront.relast b/src/test/resources/MinimalGrammar/in/CommentInFront.relast index 99b677e..96c5eec 100644 --- a/src/test/resources/MinimalGrammar/in/CommentInFront.relast +++ b/src/test/resources/MinimalGrammar/in/CommentInFront.relast @@ -1,2 +1,2 @@ // comment -CommentInFront ; +CommentInFront; diff --git a/src/test/resources/MinimalGrammar/in/Example.relast b/src/test/resources/MinimalGrammar/in/Example.relast index 0d9c906..7c28b7b 100644 --- a/src/test/resources/MinimalGrammar/in/Example.relast +++ b/src/test/resources/MinimalGrammar/in/Example.relast @@ -4,7 +4,7 @@ ZoneModel ::= <Size:IntPosition> SafetyZone:Zone*; Zone ::= Coordinate*; -RobotArm ::= Joint* EndEffector <_AttributeTestSource:int> /<_AppropriateSpeed:double>/ ; // normally this would be: <AttributeTestSource:int> ; +RobotArm ::= Joint* EndEffector <_AttributeTestSource:int> /<_AppropriateSpeed:double>/; // normally this would be: <AttributeTestSource:int> ; Joint ::= <Name> <CurrentPosition:IntPosition>; // normally this would be: <CurrentPosition:IntPosition> diff --git a/src/testFixtures/java/org/jastadd/relast/tests/RelAstProcessorTestBase.java b/src/testFixtures/java/org/jastadd/relast/tests/RelAstProcessorTestBase.java index bdd620c..e83746c 100644 --- a/src/testFixtures/java/org/jastadd/relast/tests/RelAstProcessorTestBase.java +++ b/src/testFixtures/java/org/jastadd/relast/tests/RelAstProcessorTestBase.java @@ -3,25 +3,25 @@ package org.jastadd.relast.tests; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.filefilter.FileFilterUtils; +import org.apache.commons.io.filefilter.TrueFileFilter; import org.assertj.core.util.Files; import org.jastadd.relast.tests.config.Configuration; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; +import java.io.*; +import java.nio.charset.Charset; import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; -import java.util.Objects; - -import static org.junit.jupiter.api.Assertions.assertTrue; +import java.util.*; +import java.util.stream.Stream; public class RelAstProcessorTestBase { + protected static Class<?> mainClass; + protected static int runProcess(File workingDirectory, List<String> command, StringBuilder outStringBuider, StringBuilder errStringBuilder) throws IOException, InterruptedException { File outFile = Files.newTemporaryFile(); @@ -72,7 +72,7 @@ public class RelAstProcessorTestBase { command.addAll(args); } - System.out.println("Running java -jar -cp [...] " + className + " " + args.stream().reduce((s1, s2) -> s1 + " " + s2).orElse("")); + System.out.println("Running java -jar -cp [...] " + className + " " + (args != null ? args.stream().reduce((s1, s2) -> s1 + " " + s2).orElse("") : "")); return runProcess(workingDirectory, command, outStringBuider, errStringBuilder); } @@ -86,6 +86,9 @@ public class RelAstProcessorTestBase { for (Configuration config : configs) { + FileUtils.forceMkdir(dir.resolve(config.getOut()).toFile()); + FileUtils.cleanDirectory(dir.resolve(config.getOut()).toFile()); + StringBuilder outBuilder = new StringBuilder(); StringBuilder errBuilder = new StringBuilder(); int returnValue = runJavaProcess(mainClass, dir.toFile(), Arrays.asList(config.getArgs()), outBuilder, errBuilder); @@ -96,46 +99,78 @@ public class RelAstProcessorTestBase { System.err.println(err); if (config.shouldFail()) { - Assertions.assertNotEquals(0, returnValue, "Zero return value of preprocessor for negative test."); + Assertions.assertNotEquals(0, returnValue, config.getName() + ": Zero return value of preprocessor for negative test."); } else { - Assertions.assertEquals(0, returnValue, "Non-Zero return value of preprocessor for positive test."); + Assertions.assertEquals(0, returnValue, config.getName() + ": Non-Zero return value of preprocessor for positive test."); } - for (String errMatchString : config.getErrMatches()) { - if (!err.matches(errMatchString)) { - Assertions.fail("Error stream does not match '" + errMatchString + "'"); - } + checkOutput(config, out, err); + + if (config.shouldCompare()) { + Path outPath = dir.resolve(config.getOut()); + Path expectedPath = dir.resolve(config.getExpected()); + comparePaths(outPath, expectedPath); } + } + } - for (String errContainsString : config.getErrContains()) { - if (!err.contains(errContainsString)) { - Assertions.fail("Error stream does not contain '" + errContainsString + "'"); - } + private void checkOutput(Configuration config, String out, String err) { + for (String errMatchString : config.getErrMatches()) { + if (!err.matches(errMatchString)) { + Assertions.fail("Error stream does not match '" + errMatchString + "'"); } + } - for (String outMatchString : config.getOutMatches()) { - if (!out.matches(outMatchString)) { - Assertions.fail("Error stream does not match '" + outMatchString + "'"); - } + for (String errContainsString : config.getErrContains()) { + if (!err.contains(errContainsString)) { + Assertions.fail("Error stream does not contain '" + errContainsString + "'"); } + } - for (String outContainsString : config.getOutContains()) { - if (!out.contains(outContainsString)) { - Assertions.fail("Error stream does not contain '" + outContainsString + "'"); - } + for (String outMatchString : config.getOutMatches()) { + if (!out.matches(outMatchString)) { + Assertions.fail("Output stream does not match '" + outMatchString + "'"); } } - } - protected void ensureOutputDir(String outputDir) { - File outputDirFile = Paths.get(outputDir).toFile(); - if (outputDirFile.exists()) { - assertTrue(outputDirFile.isDirectory()); - if (Objects.requireNonNull(outputDirFile.list(), "Could not read output directory").length != 0) { - System.out.println("output directory is not empty!"); + for (String outContainsString : config.getOutContains()) { + if (!out.contains(outContainsString)) { + Assertions.fail("Output stream does not contain '" + outContainsString + "'"); } - } else { - assertTrue(outputDirFile.mkdirs()); } } + + private void comparePaths(Path outPath, Path expectedPath) { + final Collection<File> files = FileUtils.listFiles(expectedPath.toFile(), TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE); + files.forEach(f -> { + final Path relative = expectedPath.relativize(f.toPath()); + final String relativePath = relative.toFile().getPath(); + final File bfile = new File(outPath.toFile(), relativePath); + if (bfile.exists()) { + try { + final Charset charset = Charset.defaultCharset(); + final String expected = FileUtils.readFileToString(f, charset); + final String result = FileUtils.readFileToString(bfile, charset); + Assertions.assertEquals(expected, result); + } catch (IOException e) { + Assertions.fail("Unable to compare input files '" + f + "' and '" + bfile + "'", e); + } + } else { + Assertions.fail(relativePath + " expected to exist"); + } + }); + } + + @TestFactory + Stream<DynamicTest> testAll() { + File baseDir = new File("src/test/resources/"); + + Assertions.assertTrue(baseDir.exists()); + Assertions.assertTrue(baseDir.isDirectory()); + File[] files = baseDir.listFiles((FileFilter) FileFilterUtils.directoryFileFilter()); + Assertions.assertNotNull(files); + return Arrays.stream(files).map(File::toPath).map(f -> DynamicTest.dynamicTest(f.getFileName().toString(), + () -> directoryTest(mainClass, f))); + } + } diff --git a/src/testFixtures/java/org/jastadd/relast/tests/config/Configuration.java b/src/testFixtures/java/org/jastadd/relast/tests/config/Configuration.java index 7a1ec94..faeb91a 100644 --- a/src/testFixtures/java/org/jastadd/relast/tests/config/Configuration.java +++ b/src/testFixtures/java/org/jastadd/relast/tests/config/Configuration.java @@ -9,6 +9,35 @@ public class Configuration { private String[] errContains = new String[]{}; private String[] outMatches = new String[]{}; private String[] outContains = new String[]{}; + private String in = "in"; + private String out = "out"; + private String expected = "expected"; + + private boolean compare = false; + + public String getIn() { + return in; + } + + public void setIn(String in) { + this.in = in; + } + + public String getOut() { + return out; + } + + public void setOut(String out) { + this.out = out; + } + + public String getExpected() { + return expected; + } + + public void setExpected(String expected) { + this.expected = expected; + } @com.fasterxml.jackson.annotation.JsonGetter("out-matches") public String[] getOutMatches() { @@ -46,7 +75,7 @@ public class Configuration { } @com.fasterxml.jackson.annotation.JsonSetter("err-contains") - public void setErrContains(String[] errMatches) { + public void setErrContains(String[] errContains) { this.errContains = errContains; } @@ -75,4 +104,14 @@ public class Configuration { public void shouldFail(boolean fail) { this.fail = fail; } + + @com.fasterxml.jackson.annotation.JsonGetter("compare") + public boolean shouldCompare() { + return compare; + } + + @com.fasterxml.jackson.annotation.JsonSetter("compare") + public void shouldCompare(boolean compare) { + this.compare = compare; + } } -- GitLab