Model ::= RobotArm ZoneModel;
ZoneModel ::= <Size:IntPosition> SafetyZone:Zone*;
Zone ::= Coordinate*;
RobotArm ::= Joint* EndEffector <_AttributeTestSource:int> /<_AppropriateSpeed:double>/; // normally this would be: <AttributeTestSource:int> ;
Joint ::= <Name> <CurrentPosition:IntPosition>; // normally this would be: <CurrentPosition:IntPosition>
EndEffector : Joint;
Coordinate ::= <Position:IntPosition>;
- name: "No whitespace/comment between and after rules required"
args:
- "--inputBaseDir=in"
- "--outputBaseDir=out"
- "Grammar.relast"
- "Relation.relast"
out: "out"
expected: "in"
compare: true
Person ::= <FullName> <Address> Gender;abstract Gender;
Male : Gender;Female : Gender;Diverse : Gender;
\ No newline at end of file
rel Person.Friend -> Person;rel Person.Child <-> Person.Parent;
\ No newline at end of file
Model ::= RobotArm ZoneModel ;
ZoneModel ::= <Size:IntPosition> SafetyZone:Zone* ;
Zone ::= Coordinate* ;
RobotArm ::= Joint* EndEffector <_AttributeTestSource:int> /<_AppropriateSpeed:double>/ ; // normally this would be: <AttributeTestSource:int> ;
Joint ::= <Name> <CurrentPosition:IntPosition> ; // normally this would be: <CurrentPosition:IntPosition>
EndEffector : Joint;
Coordinate ::= <Position:IntPosition> ;
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.*;
import java.nio.charset.Charset;
import java.nio.file.Path;
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();
File errFile = Files.newTemporaryFile();
ProcessBuilder pb = new ProcessBuilder(command).
directory(workingDirectory)
.redirectOutput(outFile)
.redirectError(errFile);
Process p = pb.start();
try {
p.waitFor();
} catch (InterruptedException e) {
if (Thread.interrupted()) // Clears interrupted status!
throw e;
}
try (BufferedReader outReader = new BufferedReader(new FileReader(outFile))) {
String outLine;
while ((outLine = outReader.readLine()) != null) {
outStringBuider.append(outLine).append("\n");
}
}
try (BufferedReader errReader = new BufferedReader(new FileReader(errFile))) {
String errLine;
while ((errLine = errReader.readLine()) != null) {
errStringBuilder.append(errLine).append("\n");
}
}
return p.exitValue();
}
protected static int runJavaProcess(Class<?> klass, File workingDirectory, List<String> args, StringBuilder outStringBuider, StringBuilder errStringBuilder) throws IOException, InterruptedException {
String javaHome = System.getProperty("java.home");
String javaBin = javaHome + File.separator + "bin" + File.separator + "java";
String classpath = System.getProperty("java.class.path");
String className = klass.getName();
List<String> command = new LinkedList<>();
command.add(javaBin);
command.add("-cp");
command.add(classpath);
command.add(className);
if (args != null) {
command.addAll(args);
}
System.out.println("Running java -jar -cp [...] " + className + " " + (args != null ? args.stream().reduce((s1, s2) -> s1 + " " + s2).orElse("") : ""));
return runProcess(workingDirectory, command, outStringBuider, errStringBuilder);
}
protected void directoryTest(Class<?> mainClass, Path dir) throws IOException, InterruptedException {
dir = dir.toAbsolutePath();
Path configFile = dir.resolve("config.yaml");
ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
List<Configuration> configs = mapper.readValue(configFile.toFile(), new TypeReference<List<Configuration>>() {
});
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);
String out = outBuilder.toString();
String err = errBuilder.toString();
System.out.println(out);
System.err.println(err);
if (config.shouldFail()) {
Assertions.assertNotEquals(0, returnValue, config.getName() + ": Zero return value of preprocessor for negative test.");
} else {
Assertions.assertEquals(0, returnValue, config.getName() + ": Non-Zero return value of preprocessor for positive test.");
}
checkOutput(config, out, err);
if (config.shouldCompare()) {
Path outPath = dir.resolve(config.getOut());
Path expectedPath = dir.resolve(config.getExpected());
comparePaths(outPath, expectedPath);
}
}
}
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 errContainsString : config.getErrContains()) {
if (!err.contains(errContainsString)) {
Assertions.fail("Error stream does not contain '" + errContainsString + "'");
}
}
for (String outMatchString : config.getOutMatches()) {
if (!out.matches(outMatchString)) {
Assertions.fail("Output stream does not match '" + outMatchString + "'");
}
}
for (String outContainsString : config.getOutContains()) {
if (!out.contains(outContainsString)) {
Assertions.fail("Output stream does not contain '" + outContainsString + "'");
}
}
}
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)
// TODO consider also supporting "config.yml"
.filter(f -> Objects.requireNonNull(f.listFiles(x -> x.getName().matches("config\\.yaml"))).length == 1)
.map(File::toPath)
.map(f -> DynamicTest.dynamicTest(f.getFileName().toString(), () -> directoryTest(mainClass, f)));
}
}
package org.jastadd.relast.tests.config;
public class Configuration {
private String name;
private String[] args = new String[]{};
private boolean fail = false;
private String[] errMatches = new String[]{};
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() {
return outMatches;
}
@com.fasterxml.jackson.annotation.JsonSetter("out-matches")
public void setOutMatches(String[] outMatches) {
this.outMatches = outMatches;
}
@com.fasterxml.jackson.annotation.JsonGetter("out-contains")
public String[] getOutContains() {
return outContains;
}
@com.fasterxml.jackson.annotation.JsonSetter("out-contains")
public void setOutContains(String[] outContains) {
this.outContains = outContains;
}
@com.fasterxml.jackson.annotation.JsonGetter("err-matches")
public String[] getErrMatches() {
return errMatches;
}
@com.fasterxml.jackson.annotation.JsonSetter("err-matches")
public void setErrMatches(String[] errMatches) {
this.errMatches = errMatches;
}
@com.fasterxml.jackson.annotation.JsonGetter("err-contains")
public String[] getErrContains() {
return errContains;
}
@com.fasterxml.jackson.annotation.JsonSetter("err-contains")
public void setErrContains(String[] errContains) {
this.errContains = errContains;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String[] getArgs() {
return args;
}
public void setArgs(String[] args) {
this.args = args;
}
@com.fasterxml.jackson.annotation.JsonGetter("fail")
public boolean shouldFail() {
return fail;
}
@com.fasterxml.jackson.annotation.JsonSetter("fail")
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;
}
}