use of org.ballerinalang.testerina.core.entity.Test in project ballerina by ballerina-lang.
the class TestAnnotationProcessor method process.
@Override
public void process(FunctionNode functionNode, List<AnnotationAttachmentNode> annotations) {
// to avoid processing those, we have to have below check.
if (!suite.getSuiteName().equals(functionNode.getPosition().getSource().getPackageName())) {
return;
}
// traverse through the annotations of this function
for (AnnotationAttachmentNode attachmentNode : annotations) {
String annotationName = attachmentNode.getAnnotationName().getValue();
String functionName = functionNode.getName().getValue();
if (BEFORE_SUITE_ANNOTATION_NAME.equals(annotationName)) {
suite.addBeforeSuiteFunction(functionName);
} else if (AFTER_SUITE_ANNOTATION_NAME.equals(annotationName)) {
suite.addAfterSuiteFunction(functionName);
} else if (BEFORE_EACH_ANNOTATION_NAME.equals(annotationName)) {
suite.addBeforeEachFunction(functionName);
} else if (AFTER_EACH_ANNOTATION_NAME.equals(annotationName)) {
suite.addAfterEachFunction(functionName);
} else if (MOCK_ANNOTATION_NAME.equals(annotationName)) {
String[] vals = new String[2];
// If package property not present the package is .
// TODO: when default values are supported in annotation struct we can remove this
vals[0] = ".";
if (attachmentNode.getExpression() instanceof BLangRecordLiteral) {
List<BLangRecordLiteral.BLangRecordKeyValue> attributes = ((BLangRecordLiteral) attachmentNode.getExpression()).getKeyValuePairs();
attributes.forEach(attributeNode -> {
String name = attributeNode.getKey().toString();
String value = attributeNode.getValue().toString();
if (PACKAGE.equals(name)) {
vals[0] = value;
} else if (FUNCTION.equals(name)) {
vals[1] = value;
}
});
suite.addMockFunction(vals[0] + MOCK_ANNOTATION_DELIMITER + vals[1], functionName);
}
} else if (TEST_ANNOTATION_NAME.equals(annotationName)) {
Test test = new Test();
test.setTestName(functionName);
AtomicBoolean shouldSkip = new AtomicBoolean();
AtomicBoolean groupsFound = new AtomicBoolean();
List<String> groups = registry.getGroups();
boolean shouldIncludeGroups = registry.shouldIncludeGroups();
if (attachmentNode.getExpression() instanceof BLangRecordLiteral) {
List<BLangRecordLiteral.BLangRecordKeyValue> attributes = ((BLangRecordLiteral) attachmentNode.getExpression()).getKeyValuePairs();
attributes.forEach(attributeNode -> {
String name = attributeNode.getKey().toString();
// Check if enable property is present in the annotation
if (TEST_ENABLE_ANNOTATION_NAME.equals(name) && "false".equals(attributeNode.getValue().toString())) {
// If enable is false, disable the test, no further processing is needed
shouldSkip.set(true);
return;
}
// Check whether user has provided a group list
if (groups != null && !groups.isEmpty()) {
// check if groups attribute is present in the annotation
if (GROUP_ANNOTATION_NAME.equals(name)) {
if (attributeNode.getValue() instanceof BLangArrayLiteral) {
BLangArrayLiteral values = (BLangArrayLiteral) attributeNode.getValue();
boolean isGroupPresent = isGroupAvailable(groups, values.exprs.stream().map(node -> node.toString()).collect(Collectors.toList()));
if (shouldIncludeGroups) {
// include only if the test belong to one of these groups
if (!isGroupPresent) {
// skip the test if this group is not defined in this test
shouldSkip.set(true);
return;
}
} else {
// exclude only if the test belong to one of these groups
if (isGroupPresent) {
// skip if this test belongs to one of the excluded groups
shouldSkip.set(true);
return;
}
}
groupsFound.set(true);
}
}
}
if (VALUE_SET_ANNOTATION_NAME.equals(name)) {
test.setDataProvider(attributeNode.getValue().toString());
}
if (BEFORE_FUNCTION.equals(name)) {
test.setBeforeTestFunction(attributeNode.getValue().toString());
}
if (AFTER_FUNCTION.equals(name)) {
test.setAfterTestFunction(attributeNode.getValue().toString());
}
if (DEPENDS_ON_FUNCTIONS.equals(name)) {
if (attributeNode.getValue() instanceof BLangArrayLiteral) {
BLangArrayLiteral values = (BLangArrayLiteral) attributeNode.getValue();
values.exprs.stream().map(node -> node.toString()).forEach(test::addDependsOnTestFunction);
}
}
});
}
if (groups != null && !groups.isEmpty() && !groupsFound.get() && shouldIncludeGroups) {
// if the user has asked to run only a specific list of groups and this test doesn't have
// that group, we should skip the test
shouldSkip.set(true);
}
if (!shouldSkip.get()) {
suite.addTests(test);
}
} else {
// disregard this annotation
}
}
}
use of org.ballerinalang.testerina.core.entity.Test in project ballerina by ballerina-lang.
the class TestAnnotationProcessor method checkCyclicDependencies.
private static int[] checkCyclicDependencies(List<Test> tests) {
int numberOfNodes = tests.size();
int[] indegrees = new int[numberOfNodes];
int[] sortedElts = new int[numberOfNodes];
List<Integer>[] dependencyMatrix = new ArrayList[numberOfNodes];
for (int i = 0; i < numberOfNodes; i++) {
dependencyMatrix[i] = new ArrayList<>();
}
List<String> testNames = tests.stream().map(k -> k.getTestName()).collect(Collectors.toList());
int i = 0;
for (Test test : tests) {
if (!test.getDependsOnTestFunctions().isEmpty()) {
for (String dependsOnFn : test.getDependsOnTestFunctions()) {
int idx = testNames.indexOf(dependsOnFn);
if (idx == -1) {
String message = String.format("Test [%s] depends on function [%s], but it couldn't be found" + ".", test.getTestFunction().getName(), dependsOnFn);
throw new BallerinaException(message);
}
dependencyMatrix[i].add(idx);
}
}
i++;
}
// fill in degrees
for (int j = 0; j < numberOfNodes; j++) {
List<Integer> dependencies = dependencyMatrix[j];
for (int node : dependencies) {
indegrees[node]++;
}
}
// Create a queue and enqueue all vertices with indegree 0
Queue<Integer> q = new LinkedList<Integer>();
for (i = 0; i < numberOfNodes; i++) {
if (indegrees[i] == 0) {
q.add(i);
}
}
// Initialize count of visited vertices
int cnt = 0;
// Create a vector to store result (A topological ordering of the vertices)
Vector<Integer> topOrder = new Vector<Integer>();
while (!q.isEmpty()) {
// Extract front of queue (or perform dequeue) and add it to topological order
int u = q.poll();
topOrder.add(u);
// Iterate through all its neighbouring nodes of dequeued node u and decrease their in-degree by 1
for (int node : dependencyMatrix[u]) {
// If in-degree becomes zero, add it to queue
if (--indegrees[node] == 0) {
q.add(node);
}
}
cnt++;
}
// Check if there was a cycle
if (cnt != numberOfNodes) {
String message = "Cyclic test dependency detected";
throw new BallerinaException(message);
}
i = numberOfNodes - 1;
for (int elt : topOrder) {
sortedElts[i] = elt;
i--;
}
return sortedElts;
}
use of org.ballerinalang.testerina.core.entity.Test in project ballerina by ballerina-lang.
the class TestAnnotationProcessor method packageProcessed.
/**
* TODO this is a temporary solution, till we get a proper API from Ballerina Core.
* This method will get executed at the completion of the processing of a ballerina package.
*
* @param programFile {@link ProgramFile} corresponds to the current ballerina package
*/
public void packageProcessed(ProgramFile programFile) {
packageInit = false;
// TODO the below line is required since this method is currently getting explicitly called from BTestRunner
suite = TesterinaRegistry.getInstance().getTestSuites().get(programFile.getEntryPkgName());
suite.setInitFunction(new TesterinaFunction(programFile, programFile.getEntryPackage().getInitFunctionInfo(), TesterinaFunction.Type.INIT));
// add all functions of the package as utility functions
Arrays.stream(programFile.getEntryPackage().getFunctionInfoEntries()).forEach(functionInfo -> {
suite.addTestUtilityFunction(new TesterinaFunction(programFile, functionInfo, TesterinaFunction.Type.UTIL));
});
int[] testExecutionOrder = checkCyclicDependencies(suite.getTests());
resolveFunctions(suite);
List<Test> sortedTests = orderTests(suite.getTests(), testExecutionOrder);
suite.setTests(sortedTests);
suite.setProgramFile(programFile);
}
use of org.ballerinalang.testerina.core.entity.Test in project ballerina by ballerina-lang.
the class TestAnnotationProcessor method resolveFunctions.
/**
* Resolve function names to {@link TesterinaFunction}s.
*
* @param suite {@link TestSuite} whose functions to be resolved.
*/
private static void resolveFunctions(TestSuite suite) {
List<TesterinaFunction> functions = suite.getTestUtilityFunctions();
List<String> functionNames = functions.stream().map(testerinaFunction -> testerinaFunction.getName()).collect(Collectors.toList());
for (Test test : suite.getTests()) {
if (test.getTestName() != null && functionNames.contains(test.getTestName())) {
test.setTestFunction(functions.stream().filter(e -> e.getName().equals(test.getTestName())).findFirst().get());
}
if (test.getBeforeTestFunction() != null && functionNames.contains(test.getBeforeTestFunction())) {
test.setBeforeTestFunctionObj(functions.stream().filter(e -> e.getName().equals(test.getBeforeTestFunction())).findFirst().get());
}
if (test.getAfterTestFunction() != null && functionNames.contains(test.getAfterTestFunction())) {
test.setAfterTestFunctionObj(functions.stream().filter(e -> e.getName().equals(test.getAfterTestFunction())).findFirst().get());
}
if (test.getDataProvider() != null && functionNames.contains(test.getDataProvider())) {
String dataProvider = test.getDataProvider();
test.setDataProviderFunction(functions.stream().filter(e -> e.getName().equals(test.getDataProvider())).findFirst().map(func -> {
// TODO these validations are not working properly with the latest refactoring
if (func.getbFunction().getRetParamTypes().length == 1) {
BType bType = func.getbFunction().getRetParamTypes()[0];
if (bType.getTag() == TypeTags.ARRAY_TAG) {
BArrayType bArrayType = (BArrayType) bType;
if (bArrayType.getElementType().getTag() != TypeTags.ARRAY_TAG) {
String message = String.format("Data provider function [%s] should return an array of" + " arrays.", dataProvider);
throw new BallerinaException(message);
}
} else {
String message = String.format("Data provider function [%s] should return an array of " + "arrays.", dataProvider);
throw new BallerinaException(message);
}
} else {
String message = String.format("Data provider function [%s] should have only one return type" + ".", dataProvider);
throw new BallerinaException(message);
}
return func;
}).get());
if (test.getDataProviderFunction() == null) {
String message = String.format("Data provider function [%s] cannot be found.", dataProvider);
throw new BallerinaException(message);
}
}
for (String dependsOnFn : test.getDependsOnTestFunctions()) {
// TODO handle missing func case
test.addDependsOnTestFunction(functions.stream().filter(e -> e.getName().equals(dependsOnFn)).findFirst().get());
}
}
// resolve mock functions
suite.getMockFunctionNamesMap().forEach((id, functionName) -> {
TesterinaFunction function = suite.getTestUtilityFunctions().stream().filter(e -> e.getName().equals(functionName)).findFirst().get();
suite.addMockFunctionObj(id, function);
});
suite.getBeforeSuiteFunctionNames().forEach(functionName -> {
TesterinaFunction function = suite.getTestUtilityFunctions().stream().filter(e -> e.getName().equals(functionName)).findFirst().get();
suite.addBeforeSuiteFunctionObj(function);
});
suite.getAfterSuiteFunctionNames().forEach(functionName -> {
TesterinaFunction function = suite.getTestUtilityFunctions().stream().filter(e -> e.getName().equals(functionName)).findFirst().get();
suite.addAfterSuiteFunctionObj(function);
});
suite.getBeforeEachFunctionNames().forEach(functionName -> {
TesterinaFunction function = suite.getTestUtilityFunctions().stream().filter(e -> e.getName().equals(functionName)).findFirst().get();
suite.addBeforeEachFunctionObj(function);
});
suite.getAfterEachFunctionNames().forEach(functionName -> {
TesterinaFunction function = suite.getTestUtilityFunctions().stream().filter(e -> e.getName().equals(functionName)).findFirst().get();
suite.addAfterEachFunctionObj(function);
});
}
Aggregations