Aufgrund einer Wartung wird GitLab am 18.01. zwischen 8:00 und 9:00 Uhr kurzzeitig nicht zur Verfügung stehen. / Due to maintenance, GitLab will be temporarily unavailable on 18.01. between 8:00 and 9:00 am.

Commit e4be7768 authored by Alexander Ryndin's avatar Alexander Ryndin
Browse files

impl. tests generator

parent cae2eebd
......@@ -8,7 +8,7 @@
<groupId>de.monticore.lang.monticar</groupId>
<artifactId>embedded-montiarc-math-generator</artifactId>
<version>0.0.3-SNAPSHOT</version>
<version>0.0.4</version>
<!-- == PROJECT DEPENDENCIES ============================================= -->
......@@ -19,11 +19,11 @@
<se-commons.version>1.7.7</se-commons.version>
<mc.grammars.assembly.version>0.0.6-SNAPSHOT</mc.grammars.assembly.version>
<SIUnit.version>0.0.7</SIUnit.version>
<Common-MontiCar.version>0.0.4</Common-MontiCar.version>
<Embedded-MontiArc.version>0.0.5</Embedded-MontiArc.version>
<Embedded-MontiArc-Behaviour.version>0.0.5</Embedded-MontiArc-Behaviour.version>
<Math.version>0.0.4</Math.version>
<Embedded-MontiArc-Math.version>0.0.5</Embedded-MontiArc-Math.version>
<Common-MontiCar.version>0.0.7</Common-MontiCar.version>
<Embedded-MontiArc.version>0.0.7</Embedded-MontiArc.version>
<Embedded-MontiArc-Behaviour.version>0.0.7</Embedded-MontiArc-Behaviour.version>
<Math.version>0.0.7</Math.version>
<Embedded-MontiArc-Math.version>0.0.7</Embedded-MontiArc-Math.version>
<tagging.version>0.0.1</tagging.version>
<!-- .. Libraries .................................................. -->
<guava.version>18.0</guava.version>
......@@ -189,6 +189,18 @@
<version>4.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.23</version>
</dependency>
<dependency>
<groupId>commons-cli</groupId>
<artifactId>commons-cli</artifactId>
<version>1.4</version>
</dependency>
<!-- .. Test Libraries ............................................... -->
<dependency>
<groupId>junit</groupId>
......
......@@ -2,10 +2,12 @@ package de.monticore.lang.monticar.generator.cpp;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.ExpandedComponentInstanceSymbol;
import de.monticore.lang.math.math._symboltable.MathStatementsSymbol;
import de.monticore.lang.monticar.generator.*;
import de.monticore.lang.monticar.generator.BluePrint;
import de.monticore.lang.monticar.generator.FileContent;
import de.monticore.lang.monticar.generator.Generator;
import de.monticore.lang.monticar.generator.Helper;
import de.monticore.lang.monticar.generator.MathCommandRegister;
import de.monticore.lang.monticar.generator.cpp.converter.MathConverter;
import de.monticore.lang.monticar.generator.cpp.resolver.Resolver;
import de.monticore.lang.monticar.generator.cpp.resolver.SymTabCreator;
import de.monticore.lang.tagging._symboltable.TaggingResolver;
import de.monticore.symboltable.Scope;
import de.se_rwth.commons.logging.Log;
......@@ -14,16 +16,20 @@ import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @author Sascha Schneiders
*/
public class GeneratorCPP implements Generator {
private Path modelsDirPath;
private boolean isGenerateTests = false;
private final List<BluePrintCPP> bluePrints = new ArrayList<>();
protected String generationTargetPath = "./target/generated-sources-cpp/";
protected boolean algebraicOptimizations = false;
......@@ -46,23 +52,6 @@ public class GeneratorCPP implements Generator {
MathConverter.curBackend = new OctaveBackend();
}
public static void main(String[] args) throws IOException, URISyntaxException {
Path resolvingPath = Paths.get(args[0]);
String fullName = args[1];
String outputPath = args[2];
SymTabCreator symTabCreator = new SymTabCreator(resolvingPath);
TaggingResolver symtab = symTabCreator.createSymTabAndTaggingResolver();
Resolver resolver = new Resolver(symtab);
ExpandedComponentInstanceSymbol componentSymbol = resolver.getExpandedComponentInstanceSymbol(fullName)
.orElseThrow(() -> new IllegalArgumentException("Argument must include the full component name"));
GeneratorCPP generatorCPP = new GeneratorCPP();
generatorCPP.setGenerationTargetPath(outputPath);
generatorCPP.generateFiles(symtab, componentSymbol, symtab);
}
public String generateString(TaggingResolver taggingResolver, ExpandedComponentInstanceSymbol componentInstanceSymbol, Scope symtab) {
MathStatementsSymbol mathSymbol = Helper.getMathStatementsSymbolFor(componentInstanceSymbol, symtab);
......@@ -95,6 +84,10 @@ public class GeneratorCPP implements Generator {
}
}
if (bluePrintCPP != null) {
bluePrints.add(bluePrintCPP);
}
String result = languageUnitCPP.getGeneratedHeader(taggingResolver, bluePrintCPP);
return result;
}
......@@ -144,6 +137,10 @@ public class GeneratorCPP implements Generator {
public List<File> generateFiles(TaggingResolver taggingResolver, ExpandedComponentInstanceSymbol componentSymbol,
Scope symtab) throws IOException {
List<FileContent> fileContents = generateStrings(taggingResolver, componentSymbol, symtab);
if (isGenerateTests()) {
TestsGeneratorCPP g = new TestsGeneratorCPP(this);
fileContents.addAll(g.generateStreamTests(symtab));
}
//System.out.println(fileContents);
if (getGenerationTargetPath().charAt(getGenerationTargetPath().length() - 1) != '/') {
setGenerationTargetPath(getGenerationTargetPath() + "/");
......@@ -246,4 +243,24 @@ public class GeneratorCPP implements Generator {
public void setUseMPIDefinitionFix(boolean useFix) {
this.MPIDefinitionFix = useFix;
}
public Path getModelsDirPath() {
return modelsDirPath;
}
public void setModelsDirPath(Path modelsDirPath) {
this.modelsDirPath = modelsDirPath;
}
public boolean isGenerateTests() {
return isGenerateTests;
}
public void setGenerateTests(boolean generateTests) {
isGenerateTests = generateTests;
}
public List<BluePrintCPP> getBluePrints() {
return Collections.unmodifiableList(bluePrints);
}
}
package de.monticore.lang.monticar.generator.cpp;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.ExpandedComponentInstanceSymbol;
import de.monticore.lang.monticar.generator.cpp.resolver.Resolver;
import de.monticore.lang.monticar.generator.cpp.resolver.SymTabCreator;
import de.monticore.lang.tagging._symboltable.TaggingResolver;
import de.se_rwth.commons.logging.Log;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
/**
* Usage example:
* java -cp emam2cpp.jar \
* de.monticore.lang.monticar.generator.cpp.GeneratorCppCli \
* --models-dir=C:\Users\vpupkin\proj\src\emam \
* --root-model=de.rwth.vpupkin.modeling.autopilot.autopilot \
* --output-dir=C:\Users\vpupkin\proj\target\cpp-gen\autopilot \
* --flag-generate-tests \
* --flag-use-armadillo-backend
*/
public final class GeneratorCppCli {
private static final Option OPTION_MODELS_PATH = Option.builder("m")
.longOpt("models-dir")
.desc("full path to directory with EMAM models e.g. C:\\Users\\vpupkin\\proj\\MyAwesomeAutopilot\\src\\main\\emam")
.hasArg(true)
.required(true)
.build();
private static final Option OPTION_ROOT_MODEL = Option.builder("r")
.longOpt("root-model")
.desc("fully qualified name of the root model e.g. de.rwth.vpupkin.modeling.mySuperAwesomeAutopilotComponent")
.hasArg(true)
.required(true)
.build();
private static final Option OPTION_OUTPUT_PATH = Option.builder("o")
.longOpt("output-dir")
.desc("full path to output directory for tests e.g. C:\\Users\\vpupkin\\proj\\MyAwesomeAutopilot\\target\\gen-cpp")
.hasArg(true)
.required(true)
.build();
private static final Option OPTION_FLAG_TESTS = Option.builder("t")
.longOpt("flag-generate-tests")
.desc("optional flag indicating if tests generation is needed")
.hasArg(false)
.required(false)
.build();
private static final Option OPTION_FLAG_ARMADILLO = Option.builder("a")
.longOpt("flag-use-armadillo-backend")
.desc("optional flag indicating if Armadillo library should be used as backend")
.hasArg(false)
.required(false)
.build();
private GeneratorCppCli() {
}
public static void main(String[] args) {
Options options = getOptions();
CommandLineParser parser = new DefaultParser();
CommandLine cliArgs = parseArgs(options, parser, args);
if (cliArgs != null) {
runGenerator(cliArgs);
}
}
private static Options getOptions() {
Options options = new Options();
options.addOption(OPTION_MODELS_PATH);
options.addOption(OPTION_ROOT_MODEL);
options.addOption(OPTION_OUTPUT_PATH);
options.addOption(OPTION_FLAG_TESTS);
options.addOption(OPTION_FLAG_ARMADILLO);
return options;
}
private static CommandLine parseArgs(Options options, CommandLineParser parser, String[] args) {
CommandLine cliArgs;
try {
cliArgs = parser.parse(options, args);
} catch (ParseException e) {
System.err.println("argument parsing exception: " + e.getMessage());
System.exit(1);
return null;
}
return cliArgs;
}
private static void runGenerator(CommandLine cliArgs) {
Path modelsDirPath = Paths.get(cliArgs.getOptionValue(OPTION_MODELS_PATH.getOpt()));
String rootModelName = cliArgs.getOptionValue(OPTION_ROOT_MODEL.getOpt());
String outputPath = cliArgs.getOptionValue(OPTION_OUTPUT_PATH.getOpt());
TaggingResolver symTab = getSymTab(modelsDirPath);
Resolver resolver = new Resolver(symTab);
ExpandedComponentInstanceSymbol componentSymbol = resolveSymbol(resolver, rootModelName);
if (componentSymbol != null) {
GeneratorCPP g = new GeneratorCPP();
g.setModelsDirPath(modelsDirPath);
g.setGenerationTargetPath(outputPath);
g.setGenerateTests(cliArgs.hasOption(OPTION_FLAG_TESTS.getOpt()));
if (cliArgs.hasOption(OPTION_FLAG_ARMADILLO.getOpt())) {
g.useArmadilloBackend();
}
try {
g.generateFiles(symTab, componentSymbol, symTab);
} catch (IOException e) {
Log.error("error during generation", e);
System.exit(1);
}
}
}
private static TaggingResolver getSymTab(Path modelsDirPath) {
SymTabCreator symTabCreator = new SymTabCreator(modelsDirPath);
return symTabCreator.createSymTabAndTaggingResolver();
}
private static ExpandedComponentInstanceSymbol resolveSymbol(Resolver resolver, String rootModelName) {
ExpandedComponentInstanceSymbol componentSymbol = resolver.getExpandedComponentInstanceSymbol(rootModelName).orElse(null);
if (componentSymbol == null) {
Log.error("could not resolve component " + rootModelName);
System.exit(1);
return null;
}
return componentSymbol;
}
}
package de.monticore.lang.monticar.generator.cpp;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc.StreamScanner;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.ComponentSymbol;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.ExpandedComponentInstanceSymbol;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.PortSymbol;
import de.monticore.lang.monticar.generator.FileContent;
import de.monticore.lang.monticar.generator.cpp.template.AllTemplates;
import de.monticore.lang.monticar.generator.cpp.viewmodel.ComponentStreamTestViewModel;
import de.monticore.lang.monticar.generator.cpp.viewmodel.StreamViewModel;
import de.monticore.lang.monticar.generator.cpp.viewmodel.TestsMainEntryViewModel;
import de.monticore.lang.monticar.generator.cpp.viewmodel.check.BooleanOutputPortCheck;
import de.monticore.lang.monticar.generator.cpp.viewmodel.check.ComponentCheckViewModel;
import de.monticore.lang.monticar.generator.cpp.viewmodel.check.IOutputPortCheck;
import de.monticore.lang.monticar.generator.cpp.viewmodel.check.RangeOutputPortCheck;
import de.monticore.lang.monticar.literals2._ast.ASTBooleanLiteral;
import de.monticore.lang.monticar.streamunits._ast.ASTNamedStreamUnits;
import de.monticore.lang.monticar.streamunits._ast.ASTPrecisionNumber;
import de.monticore.lang.monticar.streamunits._ast.ASTStream;
import de.monticore.lang.monticar.streamunits._ast.ASTStreamInstruction;
import de.monticore.lang.monticar.streamunits._ast.ASTStreamValue;
import de.monticore.lang.monticar.streamunits._symboltable.ComponentStreamUnitsSymbol;
import de.monticore.lang.monticar.streamunits._symboltable.NamedStreamUnitsSymbol;
import de.monticore.lang.monticar.streamunits._visitor.StreamUnitsVisitor;
import de.monticore.symboltable.Scope;
import de.se_rwth.commons.logging.Log;
import siunit.monticoresiunit.si._ast.ASTUnitNumber;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
public final class TestsGeneratorCPP {
public static final String TESTS_DIRECTORY_NAME = "test";
private final GeneratorCPP generator;
private List<BluePrintCPP> bluePrints;
private Map<ComponentSymbol, Set<ComponentStreamUnitsSymbol>> availableStreams;
private Set<String> testedComponents;
private List<FileContent> files;
private TestsMainEntryViewModel viewModelForMain;
TestsGeneratorCPP(GeneratorCPP generator) {
this.generator = Log.errorIfNull(generator);
}
public List<FileContent> generateStreamTests(Scope symTab) {
bluePrints = new ArrayList<>(generator.getBluePrints());
if (bluePrints.isEmpty()) {
Log.warn("no blue prints were generated");
return Collections.emptyList();
}
findStreams(symTab);
return generateFiles();
}
private void findStreams(Scope symTab) {
StreamScanner scanner = new StreamScanner(generator.getModelsDirPath(), symTab);
availableStreams = new HashMap<>(scanner.scan());
}
private List<FileContent> generateFiles() {
testedComponents = new HashSet<>();
files = new ArrayList<>();
viewModelForMain = new TestsMainEntryViewModel();
viewModelForMain.setIncludes(new ArrayList<>());
for (BluePrintCPP b : bluePrints) {
ExpandedComponentInstanceSymbol s = b.getOriginalSymbol();
if (s != null) {
processBluePrint(b, s);
} else {
Log.warn("no symbol info for blue print " + b.getName() + " (package: " + b.getPackageName() + ")");
}
}
files.add(new FileContent(AllTemplates.generateMainEntry(viewModelForMain), TESTS_DIRECTORY_NAME + "/tests_main.cpp"));
files.add(getCatchLib());
return files;
}
private void processBluePrint(BluePrintCPP b, ExpandedComponentInstanceSymbol s) {
ComponentSymbol cs = s.getComponentType().getReferencedSymbol();
if (testedComponents.add(cs.getFullName())) {
processBluePrint(b, cs);
}
}
private void processBluePrint(BluePrintCPP b, ComponentSymbol cs) {
Set<ComponentStreamUnitsSymbol> streamsForComponent = availableStreams.get(cs);
if (streamsForComponent == null || streamsForComponent.isEmpty()) {
return;
}
ComponentStreamTestViewModel viewModel = getStreamViewModel(b, cs, streamsForComponent);
String genTestCode = AllTemplates.generateComponentStreamTest(viewModel);
files.add(new FileContent(genTestCode, getFileName(viewModel)));
viewModelForMain.getIncludes().add(viewModel.getFileNameWithExtension());
}
private static ComponentStreamTestViewModel getStreamViewModel(BluePrintCPP b, ComponentSymbol cs, Set<ComponentStreamUnitsSymbol> streamsForComponent) {
ComponentStreamTestViewModel viewModel = new ComponentStreamTestViewModel();
viewModel.setComponentName(b.getName());
viewModel.setFileNameWithoutExtension(b.getName() + "_test");
viewModel.setStreams(new ArrayList<>());
for (ComponentStreamUnitsSymbol stream : streamsForComponent) {
StreamViewModel svm = new StreamViewModel();
viewModel.getStreams().add(svm);
svm.setName(stream.getFullName());
svm.setChecks(getComponentPortChecks(cs, stream));
}
return viewModel;
}
private static List<ComponentCheckViewModel> getComponentPortChecks(ComponentSymbol cs, ComponentStreamUnitsSymbol stream) {
Map<PortSymbol, ASTStream> port2NamedStream = getPort2NamedStream(cs, stream);
int streamLength = getStreamLengths(port2NamedStream, stream);
List<ComponentCheckViewModel> result = new ArrayList<>();
for (int i = 0; i < streamLength; i++) {
ComponentCheckViewModel vm = new ComponentCheckViewModel();
vm.setInputPortName2Value(new HashMap<>());
vm.setOutputPortName2Check(new HashMap<>());
for (Map.Entry<PortSymbol, ASTStream> kv : port2NamedStream.entrySet()) {
ASTStreamInstruction nextInstruction = kv.getValue().getStreamInstructions().get(i);
processInstruction(vm, nextInstruction, kv.getKey());
}
result.add(vm);
}
return result;
}
private static Map<PortSymbol, ASTStream> getPort2NamedStream(ComponentSymbol cs, ComponentStreamUnitsSymbol stream) {
Map<PortSymbol, ASTStream> port2NamedStream = new HashMap<>();
for (PortSymbol port : cs.getPorts()) {
NamedStreamUnitsSymbol namedStreamForPort = stream.getNamedStream(port.getName()).orElse(null);
if (namedStreamForPort != null && namedStreamForPort.getAstNode().isPresent()) {
ASTNamedStreamUnits node = (ASTNamedStreamUnits) namedStreamForPort.getAstNode().get();
port2NamedStream.put(port, node.getStream());
}
}
return port2NamedStream;
}
private static int getStreamLengths(Map<PortSymbol, ASTStream> port2NamedStream, ComponentStreamUnitsSymbol stream) {
int streamLength = -1;
for (ASTStream ns : port2NamedStream.values()) {
int l = ns.getStreamInstructions().size();
if (streamLength == -1) {
streamLength = l;
} else if (streamLength != l) {
String msg = String.format("streams have different lengths: %s and %s (stream %s)", streamLength, l, stream.getFullName());
Log.error(msg);
throw new RuntimeException(msg);
}
}
if (streamLength <= 0) {
String msg = String.format("invalid stream data in %s", stream.getFullName());
Log.error(msg);
throw new RuntimeException(msg);
}
return streamLength;
}
private static void processInstruction(ComponentCheckViewModel vm, ASTStreamInstruction nextInstruction, PortSymbol port) {
if (nextInstruction.getStreamValue().isPresent()) {
ASTStreamValue sv = nextInstruction.getStreamValue().get();
String portName = port.getName();
if (port.isIncoming()) {
processIncomingPort(vm, sv, portName);
} else {
processOutgoingPort(vm, sv, portName);
}
}
}
private static void processIncomingPort(ComponentCheckViewModel vm, ASTStreamValue sv, String portName) {
ASTStreamValue2InputPortValue converter = new ASTStreamValue2InputPortValue();
sv.accept(converter);
if (converter.getResult() != null) {
vm.getInputPortName2Value().put(portName, converter.getResult());
}
}
private static void processOutgoingPort(ComponentCheckViewModel vm, ASTStreamValue sv, String portName) {
ASTStreamValue2OutputPortCheck converter = new ASTStreamValue2OutputPortCheck();
sv.accept(converter);
if (converter.getResult() != null) {
vm.getOutputPortName2Check().put(portName, converter.getResult());
}
}
private static FileContent getCatchLib() {
InputStream resource = TestsGeneratorCPP.class.getResourceAsStream("/vendor/catch.hpp");
String body = new Scanner(resource, "UTF-8").useDelimiter("\\A").next();
return new FileContent(body, TESTS_DIRECTORY_NAME + "/catch.hpp");
}
private static String getFileName(ComponentStreamTestViewModel viewModel) {
return TESTS_DIRECTORY_NAME + "/" + viewModel.getFileNameWithExtension();
}
private static final class ASTStreamValue2OutputPortCheck implements StreamUnitsVisitor {
private IOutputPortCheck result = null;
public IOutputPortCheck getResult() {
return result;
}
@Override
public void visit(ASTBooleanLiteral node) {
if (node.getValue()) {
result = BooleanOutputPortCheck.TRUE_EXPECTED;
} else {
result = BooleanOutputPortCheck.FALSE_EXPECTED;
}
}
@Override
public void visit(ASTPrecisionNumber node) {
ASTUnitNumber unitNumber = node.getUnitNumber();
if (!unitNumber.getNumber().isPresent()) {
return;
}
double baseValue = unitNumber.getNumber().get().doubleValue();
if (node.getPrecision().isPresent()
&& node.getPrecision().get().getUnitNumber().getNumber().isPresent()) {
double delta = node.getPrecision().get().getUnitNumber().getNumber().get().doubleValue();
result = RangeOutputPortCheck.from(baseValue - delta, baseValue + delta);
} else {
result = RangeOutputPortCheck.from(baseValue, baseValue);
}
}
}
private static final class ASTStreamValue2InputPortValue implements StreamUnitsVisitor {
private String result = null;
public String getResult() {
return result;
}
@Override
public void visit(ASTBooleanLiteral node) {
result = node.getValue() ? "true" : "false";
}
@Override
public void visit(ASTPrecisionNumber node) {
ASTUnitNumber unitNumber = node.getUnitNumber();
if (!unitNumber.getNumber().isPresent()) {
return;
}
result = Double.toString(unitNumber.getNumber().get().doubleValue());
}
}
}
......@@ -2,7 +2,6 @@ package de.monticore.lang.monticar.generator.cpp.resolver;
import de.monticore.ModelingLanguageFamily;
import de.monticore.io.paths.ModelPath;
import de.monticore.java.lang.JavaDSLLanguage;
import de.monticore.lang.embeddedmontiarc.LogConfig;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.ConstantPortSymbol;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarcmath._symboltable.EmbeddedMontiArcMathLanguage;
......@@ -51,7 +50,6 @@ public class SymTabCreator {
fam.addModelingLanguage(montiArcLanguage);
fam.addModelingLanguage(new StreamUnitsLanguage());
fam.addModelingLanguage(new JavaDSLLanguage());