blob: c40f5049ea689cdbb7c75091562708f352b7a135 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.sis.feature;
import java.util.Map;
import java.util.Collection;
import java.util.AbstractMap.SimpleEntry;
import org.apache.sis.test.DependsOnMethod;
import org.apache.sis.test.DependsOn;
import org.apache.sis.test.TestCase;
import org.junit.Test;
import static org.apache.sis.test.Assert.*;
/**
* Tests {@link CharacteristicMap} indirectly, through {@link AbstractAttribute} construction.
*
* @author Martin Desruisseaux (Geomatys)
* @version 0.5
* @since 0.5
* @module
*/
@DependsOn(CharacteristicTypeMapTest.class)
public final strictfp class CharacteristicMapTest extends TestCase {
/**
* Creates an attribute for a temperature measurement of 20°C with a ±0.1°C accuracy.
*
* @return a {@code "temperature"} attribute with two characteristics: {@code "accuracy"} and {@code "units"}.
*/
public static AbstractAttribute<Float> temperature() {
return new SingletonAttribute<>(CharacteristicTypeMapTest.temperature(), 20f);
}
/**
* Creates an attribute for the given characteristic.
*
* @param temperature the attribute created by {@link #temperature()}.
* @param name either {@code "accuracy"} or {@code "units"}.
* @return an attribute for the given name.
*/
private static AbstractAttribute<?> create(final AbstractAttribute<?> temperature, final String name) {
return new SingletonAttribute<>(((DefaultAttributeType<?>) temperature.getType()).characteristics().get(name));
}
/**
* Tests adding explicitly a characteristic with {@code CharacteristicMap.put(String, Attribute)}.
*/
@Test
public void testPut() {
final AbstractAttribute<?> temperature = temperature();
final AbstractAttribute<?> units = create(temperature, "units");
final AbstractAttribute<?> accuracy = create(temperature, "accuracy");
final Map<String,AbstractAttribute<?>> characteristics = temperature.characteristics();
/*
* Verify that the map is initially empty.
*/
assertTrue ("isEmpty", characteristics.isEmpty());
assertEquals("size", 0, characteristics.size());
assertFalse ("containsKey", characteristics.containsKey("units"));
assertFalse ("containsKey", characteristics.containsKey("accuracy"));
assertFalse ("containsKey", characteristics.containsKey("temperature"));
assertNull ("get", characteristics.get("units"));
assertNull ("get", characteristics.get("accuracy"));
assertNull ("get", characteristics.get("temperature"));
assertFalse ("containsValue", characteristics.containsValue(units));
assertFalse ("containsValue", characteristics.containsValue(accuracy));
assertFalse ("containsValue", characteristics.containsValue(temperature));
/*
* Store "units" characteristic and verify.
*/
assertNull ("put", characteristics.put("units", units));
assertFalse ("isEmpty", characteristics.isEmpty());
assertEquals("size", 1, characteristics.size());
assertTrue ("containsKey", characteristics.containsKey("units"));
assertFalse ("containsKey", characteristics.containsKey("accuracy"));
assertFalse ("containsKey", characteristics.containsKey("temperature"));
assertSame ("get", units, characteristics.get("units"));
assertNull ("get", characteristics.get("accuracy"));
assertNull ("get", characteristics.get("temperature"));
assertTrue ("containsValue", characteristics.containsValue(units));
assertFalse ("containsValue", characteristics.containsValue(accuracy));
assertFalse ("containsValue", characteristics.containsValue(temperature));
/*
* Store "accuracy" characteristic and verify.
*/
assertNull ("put", characteristics.put("accuracy", accuracy));
assertFalse ("isEmpty", characteristics.isEmpty());
assertEquals("size", 2, characteristics.size());
assertTrue ("containsKey", characteristics.containsKey("units"));
assertTrue ("containsKey", characteristics.containsKey("accuracy"));
assertFalse ("containsKey", characteristics.containsKey("temperature"));
assertSame ("get", units, characteristics.get("units"));
assertSame ("get", accuracy, characteristics.get("accuracy"));
assertNull ("get", characteristics.get("temperature"));
assertTrue ("containsValue", characteristics.containsValue(units));
assertTrue ("containsValue", characteristics.containsValue(accuracy));
assertFalse ("containsValue", characteristics.containsValue(temperature));
/*
* Overwrite values. Map shall stay unchanged.
*/
assertSame ("put", accuracy, characteristics.put("accuracy", accuracy));
assertSame ("put", units, characteristics.put("units", units));
assertEquals("size", 2, characteristics.size());
/*
* Try putting an attribute for a non-existent name.
* Map shall stay unchanged.
*/
try {
characteristics.put("dummy", units);
fail("Operation shall not have been allowed.");
} catch (IllegalArgumentException e) {
final String message = e.getMessage();
assertTrue(message, message.contains("dummy")); // Message shall contain the wrong name.
assertTrue(message, message.contains("temperature")); // Message shall contain the enclosing attribute name.
}
/*
* Try putting an attribute of the wrong type.
* Map shall stay unchanged.
*/
try {
characteristics.put("units", accuracy);
fail("Operation shall not have been allowed.");
} catch (IllegalArgumentException e) {
final String message = e.getMessage();
assertTrue(message, message.contains("accuracy"));
assertTrue(message, message.contains("temperature:units"));
}
assertEntriesEqual(units, accuracy, characteristics);
assertSame(characteristics, temperature.characteristics());
}
/**
* Tests adding a characteristic indirectly with {@code CharacteristicMap.addValue(Attribute)}.
*/
@Test
@DependsOnMethod("testPut")
public void testAddValue() {
final AbstractAttribute<?> temperature = temperature();
final AbstractAttribute<?> units = create(temperature, "units");
final AbstractAttribute<?> accuracy = create(temperature, "accuracy");
final Map<String,AbstractAttribute<?>> characteristics = temperature.characteristics();
final Collection<AbstractAttribute<?>> values = characteristics.values();
/*
* Verify that the collection is initially empty.
*/
assertTrue ("isEmpty", values.isEmpty());
assertEquals("size", 0, values.size());
assertFalse ("contains", values.contains(units));
assertFalse ("contains", values.contains(accuracy));
assertFalse ("contains", values.contains(temperature));
/*
* Store "units" characteristic and verify.
*/
assertTrue ("add", values.add(units));
assertFalse ("isEmpty", values.isEmpty());
assertEquals("size", 1, values.size());
assertTrue ("contains", values.contains(units));
assertFalse ("contains", values.contains(accuracy));
assertFalse ("contains", values.contains(temperature));
/*
* Store "accuracy" characteristic and verify.
*/
assertTrue ("add", values.add(accuracy));
assertFalse ("isEmpty", values.isEmpty());
assertEquals("size", 2, values.size());
assertTrue ("contains", values.contains(units));
assertTrue ("contains", values.contains(accuracy));
assertFalse ("contains", values.contains(temperature));
/*
* Overwrite values. Map shall stay unchanged.
*/
assertFalse ("add", values.add(accuracy));
assertFalse ("add", values.add(units));
assertEquals("size", 2, values.size());
/*
* Try adding an attribute of the wrong type.
* Map shall stay unchanged.
*/
try {
values.add(temperature);
fail("Operation shall not have been allowed.");
} catch (IllegalArgumentException e) {
final String message = e.getMessage();
assertTrue(message, message.contains("temperature"));
}
assertEntriesEqual(units, accuracy, characteristics);
assertSame(characteristics, temperature.characteristics());
}
/**
* Tests adding a characteristic indirectly with {@link CharacteristicMap#addKey(String)}.
*/
@Test
@DependsOnMethod("testPut")
public void testAddKey() {
final AbstractAttribute<?> units, accuracy;
final AbstractAttribute<?> temperature = temperature();
final Map<String,AbstractAttribute<?>> characteristics = temperature.characteristics();
final Collection<String> keys = characteristics.keySet();
/*
* Verify that the collection is initially empty.
*/
assertTrue ("isEmpty", keys.isEmpty());
assertEquals("size", 0, keys.size());
assertFalse ("contains", keys.contains("units"));
assertFalse ("contains", keys.contains("accuracy"));
assertFalse ("contains", keys.contains("temperature"));
/*
* Store "units" characteristic and verify.
*/
assertTrue ("add", keys.add("units"));
assertFalse ("isEmpty", keys.isEmpty());
assertEquals ("size", 1, keys.size());
assertTrue ("contains", keys.contains("units"));
assertFalse ("contains", keys.contains("accuracy"));
assertFalse ("contains", keys.contains("temperature"));
assertNotNull("get", units = characteristics.get("units"));
/*
* Store "accuracy" characteristic and verify.
*/
assertTrue ("add", keys.add("accuracy"));
assertFalse ("isEmpty", keys.isEmpty());
assertEquals("size", 2, keys.size());
assertTrue ("contains", keys.contains("units"));
assertTrue ("contains", keys.contains("accuracy"));
assertFalse ("contains", keys.contains("temperature"));
assertNotNull("get", accuracy = characteristics.get("accuracy"));
/*
* Overwrite values. Map shall stay unchanged.
*/
assertFalse ("add", keys.add("accuracy"));
assertFalse ("add", keys.add("units"));
assertEquals("size", 2, keys.size());
/*
* Try adding an attribute of the wrong type.
* Map shall stay unchanged.
*/
try {
keys.add("dummy");
fail("Operation shall not have been allowed.");
} catch (IllegalArgumentException e) {
final String message = e.getMessage();
assertTrue(message, message.contains("temperature"));
assertTrue(message, message.contains("dummy"));
}
assertEntriesEqual(units, accuracy, characteristics);
assertSame(characteristics, temperature.characteristics());
}
/**
* Verifies that the given characteristics map contains entries for the given attributes.
*
* @param units the first expected value in iteration order.
* @param accuracy the second expected value in iteration order.
* @param characteristics the map to verify.
*/
private static void assertEntriesEqual(final AbstractAttribute<?> units, final AbstractAttribute<?> accuracy,
final Map<String,AbstractAttribute<?>> characteristics)
{
assertArrayEquals("keySet", new String[] {"accuracy", "units"}, characteristics.keySet().toArray());
assertArrayEquals("values", new Object[] { accuracy , units }, characteristics.values().toArray());
assertArrayEquals("entrySet", new Object[] {
new SimpleEntry<>("accuracy", accuracy),
new SimpleEntry<>("units", units)
}, characteristics.entrySet().toArray());
}
/**
* Sets the accuracy characteristic in the given attribute.
*
* @param temperature the attribute where to set the accuracy.
* @param isFirstTime {@code true} if the accuracy value is set for the first time.
* @param value the new accuracy value.
*/
private static void setAccuracy(final AbstractAttribute<Float> temperature, final boolean isFirstTime, final float value) {
assertEquals("keySet.add", isFirstTime, temperature.characteristics().keySet().add("accuracy"));
final AbstractAttribute<Float> accuracy = Features.cast(temperature.characteristics().get("accuracy"), Float.class);
accuracy.setValue(value);
}
/**
* Tests {@link CharacteristicMap#equals(Object)} and (opportunistically) {@link CharacteristicMap#clone()}
*/
@Test
@DependsOnMethod("testAddKey")
public void testEquals() {
final AbstractAttribute<Float> t1 = temperature();
final AbstractAttribute<Float> t2 = temperature();
assertEquals("equals", t1, t2);
assertEquals("hashCode", t1.hashCode(), t2.hashCode());
setAccuracy(t1, true, 0.2f);
assertFalse("equals", t1.equals(t2));
assertFalse("equals", t2.equals(t1));
assertFalse("hashCode", t1.hashCode() == t2.hashCode());
setAccuracy(t2, true, 0.3f);
assertFalse("equals", t1.equals(t2));
assertFalse("equals", t2.equals(t1));
assertFalse("hashCode", t1.hashCode() == t2.hashCode());
setAccuracy(t1, false, 0.3f);
assertEquals("equals", t1, t2);
assertEquals("hashCode", t1.hashCode(), t2.hashCode());
}
/**
* Tests the reconstruction of {@link CharacteristicTypeMap} after serialization.
*/
@Test
@DependsOnMethod({"testEquals", "testAddValue"}) // Implementation of readObject use values().addAll(…).
public void testSerialization() {
final AbstractAttribute<Float> temperature = temperature();
setAccuracy(temperature, true, 0.2f);
final AbstractAttribute<Float> unserialized = assertSerializedEquals(temperature);
assertNotSame(temperature, unserialized);
assertNotSame(temperature.characteristics(), unserialized.characteristics());
assertSame(((DefaultAttributeType<?>) temperature .getType()).characteristics(),
((DefaultAttributeType<?>) unserialized.getType()).characteristics());
}
}