/******************************************************************************* * Copyright (c) 2007 IBM Corporation. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Robert Fuhrer (rfuhrer@watson.ibm.com) - initial API and implementation *******************************************************************************/ package org.rascalmpl.value; import org.rascalmpl.value.IConstructor; import org.rascalmpl.value.IMap; import org.rascalmpl.value.IMapWriter; import org.rascalmpl.value.INode; import org.rascalmpl.value.IValue; import org.rascalmpl.value.IValueFactory; import org.rascalmpl.value.exceptions.FactTypeDeclarationException; import org.rascalmpl.value.exceptions.FactTypeUseException; import org.rascalmpl.value.type.Type; import org.rascalmpl.value.type.TypeFactory; import org.rascalmpl.value.type.TypeStore; import junit.framework.TestCase; public abstract class BaseTestAnnotations extends TestCase { private IValueFactory vf; private TypeFactory tf = TypeFactory.getInstance(); private TypeStore ts = new TypeStore(); private Type E; private Type N; protected void setUp(IValueFactory factory) throws Exception { super.setUp(); vf = factory; E = tf.abstractDataType(ts, "E"); N = tf.constructor(ts, E, "n", tf.integerType()); ts.declareAnnotation(E, "x", tf.integerType()); } public void testDeclarationOnNonAllowedType() { try { ts.declareAnnotation(tf.integerType(), "a", tf.integerType()); } catch (FactTypeDeclarationException e) { // this should happen } try { ts.declareAnnotation(tf.realType(), "a", tf.integerType()); } catch (FactTypeDeclarationException e) { // this should happen } } public void testDoubleDeclaration() { try { ts.declareAnnotation(E, "size", tf.integerType()); } catch (FactTypeDeclarationException | FactTypeUseException e) { fail(e.toString()); } try { ts.declareAnnotation(E, "size", tf.realType()); fail("double declaration is not allowed"); } catch (FactTypeDeclarationException e) { // this should happen } } public void testSetAnnotation() { IConstructor n = vf.constructor(N, vf.integer(0)); ts.declareAnnotation(E, "size", tf.integerType()); try { n.asAnnotatable().setAnnotation("size", vf.integer(0)); } catch (FactTypeDeclarationException | FactTypeUseException e) { fail(e.toString()); } } public void testGetAnnotation() { IConstructor n = vf.constructor(N, vf.integer(0)); ts.declareAnnotation(E, "size", tf.integerType()); try { if (n.asAnnotatable().getAnnotation("size") != null) { fail("annotation should be null"); } } catch (FactTypeUseException e) { fail(e.toString()); } IConstructor m = n.asAnnotatable().setAnnotation("size", vf.integer(1)); IValue b = m.asAnnotatable().getAnnotation("size"); if (!b.isEqual(vf.integer(1))) { fail(); } } public void testImmutability() { IConstructor n = vf.constructor(N, vf.integer(0)); ts.declareAnnotation(E, "size", tf.integerType()); IConstructor m = n.asAnnotatable().setAnnotation("size", vf.integer(1)); if (m == n) { fail("annotation setting should change object identity"); } assertTrue(m.isEqual(n)); } public void testDeclaresAnnotation() { IConstructor n = vf.constructor(N, vf.integer(0)); ts.declareAnnotation(E, "size", tf.integerType()); if (!n.declaresAnnotation(ts, "size")) { fail(); } if (n.declaresAnnotation(ts, "size2")) { fail(); } } public void testEqualityNode() { final INode n = vf.node("hello"); final INode na = n.asAnnotatable().setAnnotation("audience", vf.string("world")); assertEqualityOfValueWithAndWithoutAnnotations(n, na); } public void testEqualityConstructor() { final IConstructor n = vf.constructor(N, vf.integer(1)); final IConstructor na = n.asAnnotatable().setAnnotation("x", vf.integer(1)); assertEqualityOfValueWithAndWithoutAnnotations(n, na); } public void assertEqualityOfValueWithAndWithoutAnnotations(IValue n, IValue na) { assertIsEqualButNotEquals(n, na); assertIsEqualButNotEquals(vf.set(n), vf.set(na)); assertIsEqualButNotEquals(vf.set(vf.set(n)), vf.set(vf.set(na))); assertIsEqualButNotEquals(vf.list(n), vf.list(na)); assertIsEqualButNotEquals(vf.list(vf.list(n)), vf.list(vf.list(na))); // check: with keys { final IMap mapN = createMap(n, vf.integer(1)); final IMap mapNA = createMap(na, vf.integer(1)); final IMap mapMapN = createMap(mapN, vf.integer(1)); final IMap mapMapNA = createMap(mapNA, vf.integer(1)); assertIsEqualButNotEquals(mapN, mapNA); assertIsEqualButNotEquals(mapMapN, mapMapNA); } // check: with values { final IMap mapN = createMap(vf.integer(1), n); final IMap mapNA = createMap(vf.integer(1), na); final IMap mapMapN = createMap(vf.integer(1), mapN); final IMap mapMapNA = createMap(vf.integer(1), mapNA); assertIsEqualButNotEquals(mapN, mapNA); assertIsEqualButNotEquals(mapMapN, mapMapNA); } assertIsEqualButNotEquals(vf.node("nestingInNode", n), vf.node("nestingInNode", na)); final TypeStore ts = new TypeStore(); final Type adtType = tf.abstractDataType(ts, "adtTypeNameThatIsIgnored"); final Type constructorType = tf.constructor(ts, adtType, "nestingInConstructor", tf.valueType()); assertIsEqualButNotEquals(vf.constructor(constructorType, n), vf.constructor(constructorType, na)); } /** * Create a @IMap from a variable argument list. * * @param keyValuePairs * a sequence of alternating keys and values * @return an new @IMap instance */ public IMap createMap(IValue... keyValuePairs) { assert (keyValuePairs.length % 2 == 0); IMapWriter w = vf.mapWriter(); for (int i = 0; i < keyValuePairs.length / 2; i++) { w.put(keyValuePairs[i], keyValuePairs[i+1]); } return w.done(); } /** * Asserting the current implementation w.r.t. hash codes and different * equalities. Note, that this does not reflect the envisioned design that * we are working towards (= structurally where annotations contribute to * equality and hash code). * * @param a * an object that does not use annotations * @param b * a structurally equal object to {@literal a} with annotations */ public void assertIsEqualButNotEquals(IValue a, IValue b) { assertFalse(a.equals(b)); assertTrue(a.isEqual(b)); assertTrue(a.hashCode() == b.hashCode()); } public void testNoKeywordParametersOnAnnotatedNode() { try { vf.node("hallo") .asAnnotatable() .setAnnotation("a", vf.integer(1)) .asWithKeywordParameters() .setParameter("b", vf.integer(2)); } catch (UnsupportedOperationException e) { assertTrue(true); } } public void testAnnotationsOnNodeWithKeywordParameters() { try { vf.node("hallo") .asWithKeywordParameters() .setParameter("b", vf.integer(2)) .asAnnotatable() .setAnnotation("a", vf.integer(1)); } catch (UnsupportedOperationException e) { assertTrue(true); } } public void testNodeAnnotation() { ts.declareAnnotation(tf.nodeType(), "foo", tf.boolType()); INode n = vf.node("hello"); INode na = n.asAnnotatable().setAnnotation("foo", vf.bool(true)); assertTrue(na.asAnnotatable().getAnnotation("foo").getType().isBool()); // annotations on node type should be propagated assertTrue(ts.getAnnotationType(tf.nodeType(), "foo").isBool()); assertTrue(ts.getAnnotations(E).containsKey("foo")); // annotations sets should not collapse into one big set ts.declareAnnotation(E, "a", tf.integerType()); ts.declareAnnotation(N, "b", tf.boolType()); assertTrue(!ts.getAnnotations(E).equals(ts.getAnnotations(N))); } public void testNodeKeywordParameter() { INode n = vf.node("hello"); INode na = n.asWithKeywordParameters().setParameter("foo", vf.bool(true)); assertTrue(na.asWithKeywordParameters().getParameter("foo").getType().isBool()); assertTrue(na.asWithKeywordParameters().getParameter("foo").equals(vf.bool(true))); } public void testConstructorKeywordParameter() { TypeStore ts = new TypeStore(); Type adt = tf.abstractDataType(ts, "adt"); Type cons = tf.constructorFromTuple(ts, adt, "cons", tf.tupleEmpty()); IConstructor n1 = vf.constructor(cons); // overrides work IConstructor n2 = n1.asWithKeywordParameters().setParameter("foo", vf.bool(false)); assertTrue(n2.asWithKeywordParameters().getParameter("foo").isEqual(vf.bool(false))); // keywordparameters work on equality: assertFalse(n1.isEqual(n2)); } }