package org.ovirt.engine.core.utils.collections;
import static org.junit.Assert.assertEquals;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.junit.Before;
import org.junit.Test;
public class CopyOnAccessMapTest {
private static final int VALUE2_LENGTH = 50;
private static final int VALUE2_WIDTH = 25;
private static final int VALUE1_LENGTH = 10;
private static final int VALUE1_WIDTH = 5;
public static class MyKey implements Serializable {
private static final long serialVersionUID = 3675790292994230887L;
private String name;
private int age;
public MyKey() {
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MyKey other = (MyKey) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
public MyKey(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public static class MyValue implements Serializable {
private static final long serialVersionUID = 2509582906973648615L;
private int width;
private int length;
public MyValue() {
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + length;
result = prime * result + width;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MyValue other = (MyValue) obj;
if (length != other.length)
return false;
if (width != other.width)
return false;
return true;
}
public MyValue(int width, int length) {
super();
this.width = width;
this.length = length;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
}
private CopyOnAccessMap<MyKey, MyValue> testedMap = null;
private MyKey key1;
private MyValue value1;
private MyKey key2;
private MyValue value2;
private Map<MyKey, MyValue> innerMap = null;
@Before
public void setup() {
innerMap = new HashMap<MyKey, MyValue>();
testedMap = new CopyOnAccessMap<MyKey, MyValue>(innerMap);
key1 = new MyKey("RHEL", 0);
value1 = new MyValue(VALUE1_WIDTH, VALUE1_LENGTH);
key2 = new MyKey("Fedora", 0);
value2 = new MyValue(VALUE2_WIDTH, VALUE2_LENGTH);
}
/**
* Changes the data of value1 after put is used to insert it to the map. If this was a regular HashMap, the data of
* the value held by the map would have also changed.
*/
@Test
public void testPut() {
testedMap.put(key1, value1);
value1.setWidth(10);
assertSingleMapEntryUnchanged();
}
private void assertSingleMapEntryUnchanged() {
MyValue newValue = innerMap.get(key1);
assertEquals(VALUE1_WIDTH, newValue.getWidth());
assertEquals(VALUE1_LENGTH, newValue.getLength());
}
/**
* Getting data from map and changing data on the retrieved object. If this was a hash map - the change would effect
* the referenced object held by the map
*/
@Test
public void testGet() {
innerMap.put(key1, value1);
MyValue newValue = testedMap.get(key1);
newValue.setWidth(10); // Changing the data
assertSingleMapEntryUnchanged();
}
/**
* Changes the data of value1 and value2, after putAll is used to put them in the map If this was a regular HashMap,
* the data of the value held by the map would change
*/
@Test
public void testPutAll() {
Map<MyKey, MyValue> map = new HashMap<MyKey, MyValue>();
map.put(key1, value1);
map.put(key2, value2);
testedMap.putAll(map);
value1.setLength(10);
value2.setLength(30);
assertEntireMapContentsUnchanged();
}
/**
* Tests activating the "values" method, changing some of the values, and activating the method again. If this was a
* regular hashmap, the 2nd activating of the values method would return values with altered data the map were
* effected
*/
@Test
public void testValues() {
fillMapValues();
Collection<MyValue> values = testedMap.values();
for (MyValue val : values) {
val.setLength(val.getLength() + 5);
}
assertEntireMapContentsUnchanged();
}
@Test
public void testKeySet() {
fillMapValues();
Set<MyKey> keySet = testedMap.keySet();
// If this was a regular map - updating the keys data would have effected them
for (MyKey key : keySet) {
key.setAge(key.getAge() + 5);
}
assertEntireMapContentsUnchanged();
}
/**
* Tests activating "entrySet" method, changing some values and keys and activating this method again. If this was a
* regular map, the keys and values obtained from the 2nd entrySet would contain the altered data.
*/
@Test
public void testEntrySet() {
fillMapValues();
Set<Entry<MyKey, MyValue>> entrySet = testedMap.entrySet();
for (Map.Entry<MyKey, MyValue> entry : entrySet) {
MyKey key = entry.getKey();
key.setAge(key.getAge() + 5);
MyValue value = entry.getValue();
value.setLength(value.getLength() + 5);
}
assertEntireMapContentsUnchanged();
}
public void assertEntireMapContentsUnchanged() {
MyValue newVal1 = innerMap.get(key1);
MyValue newVal2 = innerMap.get(key2);
assertEquals(VALUE1_WIDTH, newVal1.getWidth());
assertEquals(VALUE1_LENGTH, newVal1.getLength());
assertEquals(VALUE2_WIDTH, newVal2.getWidth());
assertEquals(VALUE2_LENGTH, newVal2.getLength());
}
private void fillMapValues() {
innerMap.put(key1, value1);
innerMap.put(key2, value2);
}
}