Verified Commit 35e1c688 authored by Stefan Brunecker's avatar Stefan Brunecker
Browse files

Add more tests for GeneratorUtil

parent 68da7bb5
......@@ -3,25 +3,49 @@ package de.monticore.lang.monticar.generator;
import static de.monticore.lang.monticar.contract.Precondition.requiresNotNull;
import static de.monticore.lang.monticar.contract.StringPrecondition.requiresNotBlank;
import com.google.common.annotations.VisibleForTesting;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.PortSymbol;
import de.monticore.lang.monticar.common2._ast.ASTCommonDimensionElement;
import de.monticore.lang.monticar.common2._ast.ASTCommonMatrixType;
import de.monticore.lang.monticar.ts.MCTypeSymbol;
import de.monticore.lang.monticar.ts.references.MCASTTypeSymbolReference;
import de.monticore.lang.monticar.ts.references.MCTypeReference;
import de.monticore.lang.monticar.types2._ast.ASTElementType;
import de.monticore.lang.monticar.types2._ast.ASTType;
import de.monticore.symboltable.Symbol;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import org.springframework.util.StringUtils;
/**
* Class for methods commonly used by generator classes.
*/
public class GeneratorUtil {
private static final String GETTER_PREFIX = "get";
private static final String SETTER_PREFIX = "set";
private static final String BOOLEAN_TYPE = "B";
private static final String NATURAL_NUMBER_TYPE = "N";
private static final String INTEGER_NUMBER_TYPE = "Z";
private static final String RATIONAL_NUMBER_TYPE = "Q";
private static final String COMPLEX_NUMBER_TYPE = "C";
/**
* Takes a collection of {@link PortSymbol}s and filters out ports that
* belong to an array except for one. The order of the ports in the supplied
* collection remains otherwise unchanged.<p>
* It is guaranteed that the first port of an array as returned by the
* collections iterator is put into the returned collection. All subsequent
* ports belonging to this array will be discarded.<p>
* Two ports are determined to belong to the same port array if their names
* match disregarding the trailing square brackets.
*
* @param ports a collection of ports
* @return collection that contains only one port for each port array and is
* otherwise unchanged from the supplied collection
*/
public static Collection<PortSymbol> filterMultipleArrayPorts(Collection<PortSymbol> ports) {
List<PortSymbol> filteredPorts = new ArrayList<>();
List<String> processedArrays = new ArrayList<>();
......@@ -38,23 +62,123 @@ public class GeneratorUtil {
return filteredPorts;
}
/**
* Returns the method name to be used when generating a setter method for the
* supplied port.
*
* @param port {@code PortSymbol}
* @return method name for the supplied port
*/
public static String getSetterMethodName(PortSymbol port) {
return getSetterMethodName(requiresNotNull(port).getNameWithoutArrayBracketPart());
}
/**
* Returns the method name to be used when generating a setter method for a
* {@link PortSymbol} with the supplied name.
*
* @param portName the name of a {@code PortSymbol}
* @return method name for the supplied port name
*/
public static String getSetterMethodName(String portName) {
return SETTER_PREFIX + StringUtils.capitalize(requiresNotBlank(portName));
}
/**
* Returns the method name to be used when generating a getter method for the
* supplied port.
*
* @param port {@code PortSymbol}
* @return method name for the supplied port
*/
public static String getGetterMethodName(PortSymbol port) {
return getGetterMethodName(requiresNotNull(port).getNameWithoutArrayBracketPart());
}
/**
* Returns the method name to be used when generating a getter method for a
* {@link PortSymbol} with the supplied name.
*
* @param portName the name of a {@code PortSymbol}
* @return method name for the supplied port name
*/
public static String getGetterMethodName(String portName) {
return GETTER_PREFIX + StringUtils.capitalize(requiresNotBlank(portName));
}
@VisibleForTesting
/**
* Returns the component name for the supplied {@code Symbol}. This name
* always starts with an capital letter.
*
* @param model {@code Symbol}
* @return component name for the supplied symbol
*/
public static String getComponentName(Symbol model) {
return StringUtils.capitalize(requiresNotNull(model).getName());
}
/**
* Tries to determine the supplied {@code PortSymbol}'s type. The type
* includes the number set, any defined range and corresponding units.
*
* @param port {@code PortSymbol}
* @return the port's type
* @throws RuntimeException if it failed to determine the port's type
*/
public static String getType(PortSymbol port) {
MCASTTypeSymbolReference typeReference = (MCASTTypeSymbolReference) port.getTypeReference();
ASTType astType = typeReference.getAstType();
if (astType instanceof ASTCommonMatrixType) {
return matrixType((ASTCommonMatrixType) astType);
} else if (astType instanceof ASTElementType) {
ASTElementType type = (ASTElementType) astType;
Optional<String> elementType = type.getTElementType();
return elementType.orElse(typeReference.getName());
} else {
throw new RuntimeException("Unexpected ASTType: " + astType);
}
}
private static String matrixType(ASTCommonMatrixType type) {
ASTElementType elementType = type.getElementType();
Optional<String> tElementTypeOpt = elementType.getTElementType();
if (tElementTypeOpt.isPresent()) {
return tElementTypeOpt.get();
} else {
if (elementType.isIsBoolean()) {
return BOOLEAN_TYPE;
} else if (elementType.isIsNatural()) {
return NATURAL_NUMBER_TYPE;
} else if (elementType.isIsWholeNumberNumber()) {
return INTEGER_NUMBER_TYPE;
} else if (elementType.isIsRational()) {
return RATIONAL_NUMBER_TYPE;
} else if (elementType.isIsComplex()) {
return COMPLEX_NUMBER_TYPE;
} else {
throw new RuntimeException("Unable to determine type from " + type);
}
}
}
/**
* Returns a string array representing the dimension of the supplied port.
* The entries in this array are strings of integer numbers. The dimension
* is a combination of the port's potential matrix dimension and array size.
* In case the supplied port is defined as an array of matrices, a string
* array with the array and matrix dimension is returned. The size of the
* port array will be in position 0 of the returned array.
* In case the supplied port is either defined as a matrix or belongs to an
* array, the respective dimension is returned.
* Otherwise, a string array of size 0 is returned.<p>
* A collection of all ports of the component that the supplied port belongs
* to is needed to determine a potential array size.
*
* @param ports all ports directly belonging to the component of the
* supplied port
* @param port PortSymbol to determine the dimension for
* @return dimension of the port or an empty array
*/
public static String[] getDimension(Collection<PortSymbol> ports, PortSymbol port) {
int arrayDimension = port.isPartOfPortArray() ?
getArrayDimension(ports, port.getNameWithoutArrayBracketPart()) : 0;
......
package de.monticore.lang.monticar.generator.html;
import static de.monticore.lang.monticar.generator.GeneratorUtil.filterMultipleArrayPorts;
import static de.monticore.lang.monticar.generator.GeneratorUtil.getComponentName;
import static de.monticore.lang.monticar.generator.GeneratorUtil.getDimension;
import static de.monticore.lang.monticar.generator.GeneratorUtil.getType;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.ExpandedComponentInstanceSymbol;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.PortSymbol;
import de.monticore.lang.monticar.common2._ast.ASTCommonMatrixType;
import de.monticore.lang.monticar.freemarker.TemplateProcessor;
import de.monticore.lang.monticar.generator.GeneratorUtil;
import de.monticore.lang.monticar.ts.references.MCASTTypeSymbolReference;
import de.monticore.lang.monticar.types2._ast.ASTElementType;
import de.monticore.lang.monticar.types2._ast.ASTType;
import freemarker.template.TemplateException;
import java.io.IOException;
import java.util.Arrays;
......@@ -18,10 +16,8 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.springframework.util.StringUtils;
/**
* This generator generates html files that can be used in combination with a
......@@ -63,7 +59,7 @@ public class HtmlGenerator {
List<Port> outports = produceOutports(model.getOutgoingPorts());
Map<String, Object> dataModel = new HashMap<>();
dataModel.put("modelName", getModelName(model));
dataModel.put("modelName", getComponentName(model));
dataModel.put("model", wasmNamingFunction.apply(model));
dataModel.put("model_wrapper", wrapperNamingFunction.apply(model));
dataModel.put("inports", inports);
......@@ -84,23 +80,11 @@ public class HtmlGenerator {
Function<PortSymbol, String> methodNameFunction) {
return filterMultipleArrayPorts(ports).stream().map(
p -> port(p.getNameWithoutArrayBracketPart(), methodNameFunction.apply(p),
getType(ports, p))).collect(Collectors.toList());
getPortDefinition(ports, p))).collect(Collectors.toList());
}
private String getType(Collection<PortSymbol> ports, PortSymbol port) {
MCASTTypeSymbolReference typeReference = (MCASTTypeSymbolReference) port.getTypeReference();
ASTType astType = typeReference.getAstType();
Optional<String> elementType;
if (astType instanceof ASTCommonMatrixType) {
ASTCommonMatrixType type = (ASTCommonMatrixType) astType;
elementType = type.getElementType().getTElementType();
} else if (astType instanceof ASTElementType) {
ASTElementType type = (ASTElementType) astType;
elementType = type.getTElementType();
} else {
throw new RuntimeException("Unexpected ASTType: " + astType);
}
String type = elementType.orElse(typeReference.getName());
private String getPortDefinition(Collection<PortSymbol> ports, PortSymbol port) {
String type = getType(port);
String[] dimension = getDimension(ports, port);
String dim = dimension.length > 0 ?
......@@ -109,10 +93,6 @@ public class HtmlGenerator {
return type + dim;
}
private String getModelName(ExpandedComponentInstanceSymbol model) {
return StringUtils.capitalize(model.getName());
}
private Port port(String name, String wrapperFunction, String type) {
Port port = new Port();
port.setName(name);
......
package de.monticore.lang.monticar.generator;
import static de.monticore.lang.monticar.generator.GeneratorUtil.filterMultipleArrayPorts;
import static de.monticore.lang.monticar.generator.GeneratorUtil.getComponentName;
import static de.monticore.lang.monticar.generator.GeneratorUtil.getDimension;
import static de.monticore.lang.monticar.generator.GeneratorUtil.getGetterMethodName;
import static de.monticore.lang.monticar.generator.GeneratorUtil.getSetterMethodName;
import static de.monticore.lang.monticar.generator.GeneratorUtil.getType;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.Mockito.mock;
......@@ -14,6 +16,7 @@ import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.PortSymb
import de.monticore.lang.monticar.contract.Precondition.PreconditionViolationException;
import de.monticore.lang.monticar.resolver.Resolver;
import de.monticore.lang.monticar.resolver.ResolverFactory;
import de.monticore.symboltable.Symbol;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection;
......@@ -21,7 +24,10 @@ import java.util.stream.Collectors;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
@SuppressWarnings("ConstantConditions")
class GeneratorUtilTest {
private static final Path RESOLVING_BASE_DIR = Paths.get("src/test/resources/generatorutil");
......@@ -49,6 +55,14 @@ class GeneratorUtilTest {
private static final String COLUMN_VECTOR = "ColumnVector";
private static final String MATRIX = "Matrix";
private static final String MATRIX_ARRAY = "MatrixArray[2]";
private static final Symbol NULL_SYMBOL = null;
private static final String ALL_LOWERCASE_NAME = "somename";
private static final String EXPECTED_ALL_LOWERCASE_COMPONENT_NAME = "Somename";
private static final String EXPECTED_FIRST_UPPERCASE_COMPONENT_NAME = "Somename";
private static final String EXPECTED_ALL_UPPERCASE_COMPONENT_NAME = "SOMENAME";
private static final String FIRST_UPPERCASE_NAME = "Somename";
private static final String ALL_UPPERCASE_NAME = "SOMENAME";
private static final String TYPES_MODEL = "models.types";
private static String portName(String direction, String name) {
return direction + name;
......@@ -292,6 +306,101 @@ class GeneratorUtilTest {
}
}
@Nested
class GetComponentName {
@Nested
class ShouldThrowException {
@Test
void whenSymbolIsNull() {
assertThatExceptionOfType(PreconditionViolationException.class)
.isThrownBy(() -> getComponentName(NULL_SYMBOL));
}
}
@Nested
class ShouldReturnCapitalizedSymbolName {
@Test
void whenAllLowercase() {
Symbol symbol = mock(Symbol.class);
when(symbol.getName()).thenReturn(ALL_LOWERCASE_NAME);
assertThat(getComponentName(symbol)).isEqualTo(EXPECTED_ALL_LOWERCASE_COMPONENT_NAME);
}
@Test
void whenFirstUppercase() {
Symbol symbol = mock(Symbol.class);
when(symbol.getName()).thenReturn(FIRST_UPPERCASE_NAME);
assertThat(getComponentName(symbol)).isEqualTo(EXPECTED_FIRST_UPPERCASE_COMPONENT_NAME);
}
@Test
void whenAllUppercase() {
Symbol symbol = mock(Symbol.class);
when(symbol.getName()).thenReturn(ALL_UPPERCASE_NAME);
assertThat(getComponentName(symbol)).isEqualTo(EXPECTED_ALL_UPPERCASE_COMPONENT_NAME);
}
}
}
@Nested
class GetType {
private ExpandedComponentInstanceSymbol typesModel;
@BeforeEach
void setUp() {
ResolverFactory resolverFactory = new ResolverFactory(RESOLVING_BASE_DIR);
Resolver resolver = resolverFactory.get();
typesModel = resolver.getExpandedComponentInstanceSymbol(TYPES_MODEL);
}
@ParameterizedTest
@CsvSource({
"bool, B",
// "natural, N",
"integer, Z",
"rational, Q",
"complex, C",
"boolMatrix, B",
"naturalMatrix, N",
"integerMatrix, Z",
"rationalMatrix, Q",
"complexMatrix, C",
"boolArray[1], B",
"boolArray[2], B",
// "naturalArray[1], N",
// "naturalArray[2], N",
// "naturalArray[3], N",
"integerArray[1], Z",
"integerArray[2], Z",
"rationalArray[1], Q",
"rationalArray[2], Q",
"complexArray[1], C",
"complexArray[2], C",
"boolMatrixArray[1], B",
"boolMatrixArray[2], B",
"naturalMatrixArray[1], N",
"naturalMatrixArray[2], N",
"integerMatrixArray[1], Z",
"integerMatrixArray[2], Z",
"rationalMatrixArray[1], Q",
"rationalMatrixArray[2], Q",
"complexMatrixArray[1], C",
"complexMatrixArray[2], C",
"rangeNoUnit, Q(-10 : 0.01 : oo)",
"rangeUnit, Z(0 g : 1 kg)",
"matrixRangeUnit, Q(-oo m/s : 0.5 m/s : oo m/s)",
})
void testType(String portName, String expectedType) {
PortSymbol port = typesModel.getPort(portName).get();
assertThat(getType(port)).isEqualToIgnoringWhitespace(expectedType);
}
}
@Nested
class GetDimension {
......
package models;
component Types {
ports
in B bool,
//out N natural,
in Z integer,
out Q rational,
in C complex,
out B^{3, 4} boolMatrix,
in N^{3, 4} naturalMatrix,
out Z^{3, 4} integerMatrix,
in Q^{3, 4} rationalMatrix,
out C^{3, 4} complexMatrix,
in B boolArray[3],
//out N naturalArray[3],
in Z integerArray[3],
out Q rationalArray[3],
in C complexArray[3],
out B^{2, 2} boolMatrixArray[2],
in N^{2, 2} naturalMatrixArray[2],
out Z^{2, 2} integerMatrixArray[2],
in Q^{2, 2} rationalMatrixArray[2],
out C^{2, 2} complexMatrixArray[2],
in Q(-10 : 0.01 : oo) rangeNoUnit,
out Z(0 g : 1 kg) rangeUnit,
in Q(-oo m/s : 0.5 m/s : oo m/s)^{3, 4} matrixRangeUnit;
}
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment