/*******************************************************************************
* Copyright (c) 2012 Centrum Wiskunde en Informatica (CWI)
* 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:
* Anya Helene Bagge (University of Bergen) - implementation
* Arnold Lankamp - base implementation (from TestBinaryIO.java)
*******************************************************************************/
package org.rascalmpl.value;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.rascalmpl.value.IConstructor;
import org.rascalmpl.value.IMap;
import org.rascalmpl.value.IMapWriter;
import org.rascalmpl.value.IString;
import org.rascalmpl.value.IValue;
import org.rascalmpl.value.IValueFactory;
import org.rascalmpl.value.io.binary.BinaryReader;
import org.rascalmpl.value.io.binary.BinaryWriter;
import org.rascalmpl.value.type.Type;
import org.rascalmpl.value.type.TypeFactory;
import org.rascalmpl.value.type.TypeStore;
import junit.framework.TestCase;
/**
* @author Anya Helene Bagge
*/
public abstract class BaseTestMap extends TestCase {
protected final TypeStore ts = new TypeStore();
protected final TypeFactory tf = TypeFactory.getInstance();
private final Type fromToMapType = tf.mapType(tf.stringType(), "from", tf.stringType(), "to");
private final Type fromToValueMapType = tf.mapType(tf.valueType(), "from", tf.valueType(), "to");
private final Type keyValueMapType = tf.mapType(tf.stringType(), "key", tf.stringType(), "value");
private final Type unlabeledMapType = tf.mapType(tf.stringType(), tf.stringType());
enum Kind { BINARY };
protected IValueFactory vf;
private Type a;
private Type b;
private TestValue[] testValues;
private IMap[] testMaps;
private StringPair[] keyValues;
protected void setUp(IValueFactory factory) throws Exception {
vf = factory;
a = tf.abstractDataType(ts, "A");
b = tf.abstractDataType(ts, "B");
testValues = new TestValue[]{
new TestValue(this, "Bergen", "Amsterdam", "from", "to"),
new TestValue(this, "New York", "London", null, null),
new TestValue(this, "Banana", "Fruit", "key", "value"),
};
testMaps = new IMap[] {
vf.map(fromToMapType),
vf.map(keyValueMapType),
vf.map(unlabeledMapType),
vf.map(fromToMapType).put(vf.string("Bergen"), vf.string("Amsterdam")),
vf.map(fromToValueMapType).put(vf.string("Bergen"), vf.string("Amsterdam")).put(vf.string("Mango"), vf.string("Yummy")),
vf.map(fromToMapType).put(vf.string("Bergen"), vf.string("Amsterdam")).put(vf.string("Amsterdam"), vf.string("Frankfurt")),
vf.map(fromToMapType).put(vf.string("Bergen"), vf.string("Amsterdam")).put(vf.string("Amsterdam"), vf.string("Frankfurt")).put(vf.string("Frankfurt"), vf.string("Moscow")),
vf.map(keyValueMapType).put(vf.string("Bergen"), vf.string("Rainy")).put(vf.string("Helsinki"), vf.string("Cold")),
vf.map(unlabeledMapType).put(vf.string("Mango"), vf.string("Sweet")).put(vf.string("Banana"), vf.string("Yummy")),
};
String[] strings = new String[] { "Bergen", "Amsterdam", "Frankfurt", "Helsinki", "Moscow", "Rainy", "Cold", "Mango", "Banana", "Sweet", "Yummy" };
List<String> list1 = Arrays.asList(strings);
List<String> list2 = Arrays.asList(strings);
Collections.shuffle(list1);
Collections.shuffle(list2);
keyValues = new StringPair[strings.length];
for(int i = 0; i < strings.length; i++) {
keyValues[i] = new StringPair(vf.string(list1.get(i)), vf.string(list2.get(i)));
}
}
public void testNoLabels() {
// make a non-labeled map type, and the labels should be null
Type type = tf.mapType(a, b);
assertNull(type.getKeyLabel());
assertNull(type.getValueLabel());
}
public void testLabels() {
// make a labeled map type, and the labels should match
Type type = tf.mapType(a, "apple", b, "banana");
assertEquals("apple", type.getKeyLabel());
assertEquals("banana", type.getValueLabel());
}
public void testTwoLabels1() {
// make two map types with same key/value types but different labels,
// and the labels should be kept distinct
Type type1 = tf.mapType(a, "apple", b, "banana");
Type type2 = tf.mapType(a, "orange", b, "mango");
Type type3 = tf.mapType(a, b);
assertEquals("apple", type1.getKeyLabel());
assertEquals("banana", type1.getValueLabel());
assertEquals("orange", type2.getKeyLabel());
assertEquals("mango", type2.getValueLabel());
assertNull(type3.getKeyLabel());
assertNull(type3.getValueLabel());
}
public void testTwoLabels2() {
Type type1 = tf.mapType(a, "apple", b, "banana");
Type type2 = tf.mapType(a, "orange", b, "mango");
assertTrue("Two map types with different labels should be equivalent", type1.equivalent(type2));
assertTrue("Two map types with different labels should be equivalent", type2.equivalent(type1));
assertFalse("Two map types with different labels should not be equals", type1.equals(type2));
assertFalse("Two map types with different labels should not be equals", type2.equals(type1));
Type type3 = tf.mapType(a, b);
assertTrue("Labeled and unlabeled maps should be equivalent", type1.equivalent(type3));
assertTrue("Labeled and unlabeled maps should be equivalent", type3.equivalent(type1));
assertTrue("Labeled and unlabeled maps should be equivalent", type2.equivalent(type3));
assertTrue("Labeled and unlabeled maps should be equivalent", type3.equivalent(type2));
assertFalse("Labeled and unlabeled maps should not be equals", type1.equals(type3));
assertFalse("Labeled and unlabeled maps should not be equals", type3.equals(type1));
assertFalse("Labeled and unlabeled maps should not be equals", type2.equals(type3));
assertFalse("Labeled and unlabeled maps should not be equals", type3.equals(type2));
}
/**
* Check basic properties of put()
*/
public void testPut() {
for(IMap map : testMaps) {
for(StringPair p : keyValues) {
IMap newMap = map.put(p.a, p.b);
assertTrue(newMap.containsKey(p.a));
assertEquals(p.b, newMap.get(p.a));
assertEquals(map.getType().getKeyLabel(), newMap.getType().getKeyLabel());
assertEquals(map.getType().getValueLabel(), newMap.getType().getValueLabel());
assertTrue(map.getType().isSubtypeOf(newMap.getType()));
}
}
}
/**
* Check that putting doesn't modify original map, and doesn't modify other elements.
*/
public void testPutModification() {
for(IMap map : testMaps) {
for(StringPair p : keyValues) { // testing with an arbitrary element of map is sufficient
if(map.containsKey(p.a)) {
IValue val = map.get(p.a);
for(StringPair q : keyValues) {
IMap newMap = map.put(q.a, q.b);
assertEquals(val, map.get(p.a)); // original is never modified
if(!p.a.isEqual(q.a))
assertEquals(val, newMap.get(p.a)); // only element q.a is modified
}
}
}
}
}
public void testCommon() {
for(IMap map1 : testMaps) {
for(IMap map2 : testMaps) {
IMap map3 = map1.common(map2);
// all common values are present
for(IValue key : map1) {
if(map1.get(key).equals(map2.get(key))) {
assertEquals(map1.get(key), map3.get(key));
}
}
// type is lub of map1 and map2 types
if(!map3.isEmpty()) {
assertTrue(map1.getType().toString() + " <: " + map3.getType(), map1.getType().isSubtypeOf(map3.getType()));
assertTrue(map2.getType().toString() + " <: " + map3.getType(), map2.getType().isSubtypeOf(map3.getType()));
}
// check labels
if(!map2.getType().hasFieldNames()) {
assertEquals(map1.getType().getKeyLabel(), map3.getType().getKeyLabel());
assertEquals(map1.getType().getValueLabel(), map3.getType().getValueLabel());
}
if(!map1.getType().hasFieldNames()) {
assertEquals(map2.getType().getKeyLabel(), map3.getType().getKeyLabel());
assertEquals(map2.getType().getValueLabel(), map3.getType().getValueLabel());
}
}
}
}
public void testJoin() {
for(IMap map1 : testMaps) {
for(IMap map2 : testMaps) {
IMap map3 = map1.join(map2);
// should contain all values from map2...
for(IValue key : map2) {
assertEquals(map2.get(key), map3.get(key));
}
// ...and all values from map1 unless the keys are in map2
for(IValue key : map1) {
if(!map2.containsKey(key)) {
assertEquals(map1.get(key), map3.get(key));
}
}
// type is lub of map1 and map2 types
if(!map3.isEmpty()) {
assertTrue(map1.getType().toString() + " <: " + map3.getType(), map1.getType().isSubtypeOf(map3.getType()));
assertTrue(map2.getType().toString() + " <: " + map3.getType(), map2.getType().isSubtypeOf(map3.getType()));
}
// check labels
if(!map2.getType().hasFieldNames()) {
assertEquals(map1.getType().getKeyLabel(), map3.getType().getKeyLabel());
assertEquals(map1.getType().getValueLabel(), map3.getType().getValueLabel());
}
if(!map1.getType().hasFieldNames()) {
assertEquals(map2.getType().getKeyLabel(), map3.getType().getKeyLabel());
assertEquals(map2.getType().getValueLabel(), map3.getType().getValueLabel());
}
}
}
}
public void testCompose() {
for(IMap map1 : testMaps) {
for(IMap map2 : testMaps) {
IMap map3 = map1.compose(map2);
// should map keys in map1 to values in map2
for(IValue key : map1) {
if(map2.containsKey(map1.get(key)))
assertEquals(map2.get(map1.get(key)), map3.get(key));
else
assertNull(map3.get(key));
}
// type is key type of map1 and value type of map2
if(!map3.isEmpty()) {
assertEquals(map1.getType().getKeyType(), map3.getType().getKeyType());
assertEquals(map2.getType().getValueType(), map3.getType().getValueType());
}
// check labels
if(map1.getType().hasFieldNames() && map2.getType().hasFieldNames()) {
assertEquals(map1.getType().getKeyLabel(), map3.getType().getKeyLabel());
assertEquals(map2.getType().getValueLabel(), map3.getType().getValueLabel());
}
else {
assertFalse(map3.getType().hasFieldNames());
}
}
}
}
public void testRemove() {
for(IMap map1 : testMaps) {
for(IMap map2 : testMaps) {
IMap map3 = map1.remove(map2);
for(IValue key : map2) {
assertFalse("Key " + key + " should not exist", map3.containsKey(key));
}
// type is same as map1
if(!map3.isEmpty()) {
assertEquals(map1.getType(), map3.getType());
}
// labels are same as map1
if(map1.getType().hasFieldNames()) {
assertEquals(map1.getType().getKeyLabel(), map3.getType().getKeyLabel());
assertEquals(map1.getType().getValueLabel(), map3.getType().getValueLabel());
}
}
}
}
public void testLabelsIO(){
try{
for(int i = 0; i < testValues.length; i++){
for(Kind k : Kind.values()) {
TestValue testValue = testValues[i];
assertEquals(testValue.keyLabel, testValue.value.getType().getKeyLabel());
assertEquals(testValue.valueLabel, testValue.value.getType().getValueLabel());
System.out.println(testValue + " : " + testValue.value.getType()); // Temp
IValue result = doIO(testValue.value, k);
System.out.println(result + " : " + result.getType()); // Temp
System.out.println(); // Temp
if(!testValue.value.isEqual(result)){
String message = "Not equal: \n\t"+testValue+" : "+testValue.value.getType()+"\n\t"+result+" : "+result.getType();
System.err.println(message);
fail(message);
}
Type resultType = result.getType();
assertEquals("Labels should be preserved by " + k.name() + " IO: ", testValue.keyLabel, resultType.getKeyLabel());
assertEquals("Labels should be preserved by " + k.name() + " IO: ", testValue.valueLabel, resultType.getValueLabel());
}
}
}catch(IOException ioex){
ioex.printStackTrace();
fail(ioex.getMessage());
}
}
private IValue doIO(IValue val, Kind kind) throws IOException {
switch(kind) {
case BINARY: {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
BinaryWriter binaryWriter = new BinaryWriter(val, baos, ts);
binaryWriter.serialize();
byte[] data = baos.toByteArray();
ByteArrayInputStream bais = new ByteArrayInputStream(data);
BinaryReader binaryReader = new BinaryReader(vf, ts, bais);
System.out.print("data: ");
printBytes(data); // Temp
return binaryReader.deserialize();
}
/*// Doesn't work, but should, perhaps?
case XML: {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
XMLWriter writer = new XMLWriter();
writer.write(val, new OutputStreamWriter(baos), ts);
byte[] data = baos.toByteArray();
ByteArrayInputStream bais = new ByteArrayInputStream(data);
XMLReader reader = new XMLReader();
printBytes(data); // Temp
return reader.read(vf, new InputStreamReader(bais));
}
*/
/* TEXT IO shouldn't work, since the labels aren't present in the standard text representation */
/* // Doesn't work, but should, perhaps?
case ATERM: {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ATermWriter writer = new ATermWriter();
writer.write(val, new OutputStreamWriter(baos), ts);
byte[] data = baos.toByteArray();
ByteArrayInputStream bais = new ByteArrayInputStream(data);
ATermReader reader = new ATermReader();
printBytes(data); // Temp
return reader.read(vf, bais);
}
*/
default:
throw new RuntimeException("Missing case: " + kind.name());
}
}
private final static String[] HEX = new String[]{"0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"};
// May be handy when debugging.
private static void printBytes(byte[] bytes){
for(int i = 0; i < bytes.length; i++){
byte b = bytes[i];
int higher = (b & 0xf0) >> 4;
int lower = b & 0xf;
System.out.print("0x");
System.out.print(HEX[higher]);
System.out.print(HEX[lower]);
System.out.print(" ");
}
System.out.println();
}
static class TestValue {
Type type;
IValue value;
String keyLabel;
String valueLabel;
TestValue(BaseTestMap baseTestMap, String key, String value, String keyLabel, String valueLabel) {
TypeFactory tf = baseTestMap.tf;
IValueFactory vf = baseTestMap.vf;
this.keyLabel = keyLabel;
this.valueLabel = valueLabel;
if(keyLabel != null && valueLabel != null)
type = tf.mapType(tf.stringType(), keyLabel, tf.stringType(), valueLabel);
else
type = tf.mapType(tf.stringType(), tf.stringType());
this.value = vf.map(type).put(vf.string(key), vf.string(value));
}
public String toString() {
return value.toString();
}
}
static class StringPair {
IString a;
IString b;
StringPair(IString a, IString b) {
this.a = a;
this.b = b;
}
@Override
public String toString() {
return String.format("(%s,%s)", a, b);
}
}
public void testPutReplaceGet() {
final IMap m1 = vf.mapWriter().done()
.put(vf.integer(1), vf.integer(1))
.put(vf.integer(1), vf.integer(2));
assertEquals(1, m1.size());
assertEquals(vf.integer(2), m1.get(vf.integer(1)));
}
public void testDynamicTypesAfterMapUpdatesGrow() {
final IMap m1 = vf.mapWriter().done()
.put(vf.integer(1), vf.integer(1))
.put(vf.integer(1), vf.real(1));
assertEquals(1, m1.size());
assertEquals(tf.integerType(), m1.getType().getKeyType());
assertEquals(tf.realType(), m1.getType().getValueType());
}
public void testDynamicTypesAfterMapWriterUpdatesGrow() {
final IMapWriter w1 = vf.mapWriter();
w1.put(vf.integer(1), vf.integer(1));
w1.put(vf.integer(1), vf.real(1));
final IMap m1 = w1.done();
assertEquals(1, m1.size());
assertEquals(tf.integerType(), m1.getType().getKeyType());
assertEquals(tf.realType(), m1.getType().getValueType());
}
public void testDynamicTypesAfterMapUpdatesShrink() {
final IMap m1 = vf.mapWriter().done()
.put(vf.integer(1), vf.integer(1))
.put(vf.integer(1), vf.real(1))
.put(vf.integer(1), vf.integer(1));
assertEquals(1, m1.size());
assertEquals(tf.integerType(), m1.getType().getKeyType());
assertEquals(tf.integerType(), m1.getType().getValueType());
}
public void testDynamicTypesAfterMapWriterUpdatesShrink() {
final IMapWriter w1 = vf.mapWriter();
w1.put(vf.integer(1), vf.integer(1));
w1.put(vf.integer(1), vf.real(1));
w1.put(vf.integer(1), vf.integer(1));
final IMap m1 = w1.done();
assertEquals(1, m1.size());
assertEquals(tf.integerType(), m1.getType().getKeyType());
assertEquals(tf.integerType(), m1.getType().getValueType());
}
public void testPutReplaceWithAnnotations_Map() {
final Type E = tf.abstractDataType(ts, "E");
final Type N = tf.constructor(ts, E, "n", tf.integerType());
ts.declareAnnotation(E, "x", tf.integerType());
final IConstructor n = vf.constructor(N, vf.integer(1));
final IConstructor na = n.asAnnotatable().setAnnotation("x", vf.integer(1));
final IMap m1 = vf.mapWriter().done()
.put(n, vf.integer(1))
.put(na, vf.integer(1));
assertEquals(1, m1.size());
assertEquals(vf.integer(1), m1.get(n));
assertEquals(vf.integer(1), m1.get(na));
}
public void testPutReplaceWithAnnotationsValue_Map() {
final Type E = tf.abstractDataType(ts, "E");
final Type N = tf.constructor(ts, E, "n", tf.integerType());
ts.declareAnnotation(E, "x", tf.integerType());
final IConstructor n = vf.constructor(N, vf.integer(1));
final IConstructor na = n.asAnnotatable().setAnnotation("x", vf.integer(1));
final IMap m1 = vf.mapWriter().done()
.put(vf.integer(1), n)
.put(vf.integer(1), na);
assertEquals(1, m1.size());
assertEquals(na, m1.get(vf.integer(1)));
}
public void testPutReplaceWithAnnotations_MapWriter() {
final Type E = tf.abstractDataType(ts, "E");
final Type N = tf.constructor(ts, E, "n", tf.integerType());
ts.declareAnnotation(E, "x", tf.integerType());
final IConstructor n = vf.constructor(N, vf.integer(1));
final IConstructor na = n.asAnnotatable().setAnnotation("x", vf.integer(1));
final IMapWriter w1 = vf.mapWriter();
w1.put(n, vf.integer(1));
w1.put(na, vf.integer(1));
final IMap m1 = w1.done();
assertEquals(1, m1.size());
assertEquals(vf.integer(1), m1.get(n));
assertEquals(vf.integer(1), m1.get(na));
}
public void testPutReplaceWithAnnotationsValue_MapWriter() {
final Type E = tf.abstractDataType(ts, "E");
final Type N = tf.constructor(ts, E, "n", tf.integerType());
ts.declareAnnotation(E, "x", tf.integerType());
final IConstructor n = vf.constructor(N, vf.integer(1));
final IConstructor na = n.asAnnotatable().setAnnotation("x", vf.integer(1));
final IMapWriter w1 = vf.mapWriter();
w1.put(vf.integer(1), n);
w1.put(vf.integer(1), na);
final IMap m1 = w1.done();
assertEquals(1, m1.size());
assertEquals(na, m1.get(vf.integer(1)));
}
}