/* * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors * * SPDX-License-Identifier: EPL-2.0 */ package tools.refinery.language.tests.serializer; import com.google.inject.Inject; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.xtext.testing.InjectWith; import org.eclipse.xtext.testing.extensions.InjectionExtension; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import tools.refinery.language.model.problem.*; import tools.refinery.language.model.tests.utils.WrappedProblem; import tools.refinery.language.tests.ProblemInjectorProvider; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Map; import java.util.stream.Stream; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; @ExtendWith(InjectionExtension.class) @InjectWith(ProblemInjectorProvider.class) class ProblemSerializerTest { @Inject private ResourceSet resourceSet; private Resource resource; private Problem problem; private WrappedProblem builtin; @BeforeEach void beforeEach() { problem = ProblemFactory.eINSTANCE.createProblem(); resource = resourceSet.createResource(URI.createFileURI("test.problem")); resource.getContents().add(problem); var wrappedProblem = new WrappedProblem(problem); builtin = wrappedProblem.builtin(); } @ParameterizedTest @MethodSource void assertionTest(LogicValue value, String serializedAssertion) { var pred = createPred(); var node = ProblemFactory.eINSTANCE.createNode(); node.setName("a"); var atomDeclaration = ProblemFactory.eINSTANCE.createNodeDeclaration(); atomDeclaration.setKind(NodeKind.ATOM); atomDeclaration.getNodes().add(node); problem.getStatements().add(atomDeclaration); createAssertion(pred, node, value); assertSerializedResult(""" pred foo(node p). atom a. """ + serializedAssertion + "\n"); } static Stream assertionTest() { return Stream.of(Arguments.of(LogicValue.TRUE, "foo(a)."), Arguments.of(LogicValue.FALSE, "!foo(a)."), Arguments.of(LogicValue.UNKNOWN, "?foo(a)."), Arguments.of(LogicValue.ERROR, "foo(a): error.")); } @ParameterizedTest @MethodSource("assertionTest") void defaultAssertionTest(LogicValue value, String serializedAssertion) { var pred = createPred(); var node = ProblemFactory.eINSTANCE.createNode(); node.setName("a"); var atomDeclaration = ProblemFactory.eINSTANCE.createNodeDeclaration(); atomDeclaration.setKind(NodeKind.ATOM); atomDeclaration.getNodes().add(node); problem.getStatements().add(atomDeclaration); createAssertion(pred, node, value, true); assertSerializedResult(""" pred foo(node p). atom a. default\040""" + serializedAssertion + "\n"); } @Test void implicitNodeTest() { var pred = createPred(); var node = ProblemFactory.eINSTANCE.createNode(); node.setName("a"); problem.getNodes().add(node); createAssertion(pred, node); assertSerializedResult(""" pred foo(node p). foo(a). """); } private PredicateDefinition createPred() { var pred = ProblemFactory.eINSTANCE.createPredicateDefinition(); pred.setName("foo"); var parameter = ProblemFactory.eINSTANCE.createParameter(); var nodeType = builtin.findClass("node"); parameter.setParameterType(nodeType.get()); parameter.setName("p"); pred.getParameters().add(parameter); problem.getStatements().add(pred); return pred; } @Test void newNodeTest() { var classDeclaration = ProblemFactory.eINSTANCE.createClassDeclaration(); classDeclaration.setName("Foo"); var newNode = ProblemFactory.eINSTANCE.createNode(); newNode.setName("new"); classDeclaration.setNewNode(newNode); problem.getStatements().add(classDeclaration); createAssertion(classDeclaration, newNode); assertSerializedResult(""" class Foo. Foo(Foo::new). """); } private void createAssertion(Relation relation, Node node) { createAssertion(relation, node, LogicValue.TRUE); } private void createAssertion(Relation relation, Node node, LogicValue logicValue) { createAssertion(relation, node, logicValue, false); } private void createAssertion(Relation relation, Node node, LogicValue logicValue, boolean isDefault) { var assertion = ProblemFactory.eINSTANCE.createAssertion(); assertion.setRelation(relation); var argument = ProblemFactory.eINSTANCE.createNodeAssertionArgument(); argument.setNode(node); assertion.getArguments().add(argument); var value = ProblemFactory.eINSTANCE.createLogicConstant(); value.setLogicValue(logicValue); assertion.setValue(value); assertion.setDefault(isDefault); problem.getStatements().add(assertion); } @Test void implicitVariableTest() { var pred = ProblemFactory.eINSTANCE.createPredicateDefinition(); pred.setName("foo"); var nodeType = builtin.findClass("node"); var parameter1 = ProblemFactory.eINSTANCE.createParameter(); parameter1.setParameterType(nodeType.get()); parameter1.setName("p1"); pred.getParameters().add(parameter1); var parameter2 = ProblemFactory.eINSTANCE.createParameter(); parameter2.setParameterType(nodeType.get()); parameter2.setName("p2"); pred.getParameters().add(parameter2); var conjunction = ProblemFactory.eINSTANCE.createConjunction(); var variable = ProblemFactory.eINSTANCE.createImplicitVariable(); variable.setName("q"); conjunction.getImplicitVariables().add(variable); var equals = builtin.pred("equals").get(); conjunction.getLiterals().add(createAtom(equals, parameter1, variable)); conjunction.getLiterals().add(createAtom(equals, variable, parameter2)); pred.getBodies().add(conjunction); problem.getStatements().add(pred); assertSerializedResult(""" pred foo(node p1, node p2) <-> equals(p1, q), equals(q, p2). """); } private Atom createAtom(Relation relation, VariableOrNode variable1, VariableOrNode variable2) { var atom = ProblemFactory.eINSTANCE.createAtom(); atom.setRelation(relation); var arg1 = ProblemFactory.eINSTANCE.createVariableOrNodeExpr(); arg1.setVariableOrNode(variable1); atom.getArguments().add(arg1); var arg2 = ProblemFactory.eINSTANCE.createVariableOrNodeExpr(); arg2.setVariableOrNode(variable2); atom.getArguments().add(arg2); return atom; } @Test void singletonVariableTest() { var pred = ProblemFactory.eINSTANCE.createPredicateDefinition(); pred.setName("foo"); var nodeType = builtin.findClass("node"); var parameter = ProblemFactory.eINSTANCE.createParameter(); parameter.setParameterType(nodeType.get()); parameter.setName("p"); pred.getParameters().add(parameter); var conjunction = ProblemFactory.eINSTANCE.createConjunction(); var atom = ProblemFactory.eINSTANCE.createAtom(); var equals = builtin.pred("equals").get(); atom.setRelation(equals); var arg1 = ProblemFactory.eINSTANCE.createVariableOrNodeExpr(); arg1.setVariableOrNode(parameter); atom.getArguments().add(arg1); var arg2 = ProblemFactory.eINSTANCE.createVariableOrNodeExpr(); var variable = ProblemFactory.eINSTANCE.createImplicitVariable(); variable.setName("_q"); arg2.setSingletonVariable(variable); arg2.setVariableOrNode(variable); atom.getArguments().add(arg2); conjunction.getLiterals().add(atom); pred.getBodies().add(conjunction); problem.getStatements().add(pred); assertSerializedResult(""" pred foo(node p) <-> equals(p, _q). """); } @Test void unambiguousNameTest() { createClassAndAssertion("Foo", "foo"); assertSerializedResult(""" class Foo { Foo ref } ref(foo, foo). """); } @Test void ambiguousNameTest() { createClassAndAssertion("Foo", "foo"); createClassAndAssertion("Bar", "bar"); assertSerializedResult(""" class Foo { Foo ref } Foo::ref(foo, foo). class Bar { Bar ref } Bar::ref(bar, bar). """); } private void createClassAndAssertion(String className, String nodeName) { var classDeclaration = ProblemFactory.eINSTANCE.createClassDeclaration(); classDeclaration.setName(className); var referenceDeclaration = ProblemFactory.eINSTANCE.createReferenceDeclaration(); referenceDeclaration.setReferenceType(classDeclaration); referenceDeclaration.setName("ref"); classDeclaration.getFeatureDeclarations().add(referenceDeclaration); problem.getStatements().add(classDeclaration); var node = ProblemFactory.eINSTANCE.createNode(); node.setName(nodeName); problem.getNodes().add(node); createBinaryAssertion(referenceDeclaration, node, node); } private void createBinaryAssertion(Relation relation, Node from, Node to) { var assertion = ProblemFactory.eINSTANCE.createAssertion(); assertion.setRelation(relation); var fromArgument = ProblemFactory.eINSTANCE.createNodeAssertionArgument(); fromArgument.setNode(from); assertion.getArguments().add(fromArgument); var toArgument = ProblemFactory.eINSTANCE.createNodeAssertionArgument(); toArgument.setNode(to); assertion.getArguments().add(toArgument); var value = ProblemFactory.eINSTANCE.createLogicConstant(); value.setLogicValue(LogicValue.TRUE); assertion.setValue(value); problem.getStatements().add(assertion); } private void assertSerializedResult(String expected) { String problemString; try (var outputStream = new ByteArrayOutputStream()) { resource.save(outputStream, Map.of()); problemString = outputStream.toString(); } catch (IOException e) { throw new AssertionError("Failed to serialize problem", e); } // Nothing to handle in a test. assertThat(problemString.replace("\r\n", "\n"), equalTo(expected.replace("\r\n", "\n"))); } }