package org.codefx.libfx.nesting;
import static org.codefx.libfx.nesting.testhelper.NestingAccess.getNestingObservable;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import javafx.beans.Observable;
import javafx.beans.value.ObservableValue;
import org.junit.Test;
/**
* Abstract superclass to tests of deep nestings. By implementing the few abstract methods subclasses can run all tests
* which apply to all implementations.
*
* @param <OO>
* the type of the nesting hierarchy's outer observable
* @param <IO>
* the type the nesting hierarchy's inner observable which is also the type wrapped by the nesting
*/
public abstract class AbstractDeepNestingTest<OO extends Observable, IO extends Observable>
extends AbstractNestingTest<OO, IO> {
// #begin TESTS
// construction
/**
* Tests whether creating a {@link DeepNesting} on an outer observable which contains null works correctly.
* <p>
* This ensures that a "broken" hierarchy is correctly initialized.
*/
@Test
public void testCreatingWhenOuterObservableHasValueNull() {
outerObservable = createNewNestingHierarchyWhereOuterObservableHasNullValue();
nesting = createNewNestingFromOuterObservable(outerObservable);
assertNotNull(nesting.innerObservableProperty().getValue());
assertFalse(nesting.innerObservableProperty().getValue().isPresent());
}
/**
* Tests whether creating a {@link DeepNesting} on a hierarchy where on of the nested observables contains null
* works correctly.
* <p>
* This ensures that a "broken" hierarchy is correctly initialized.
*/
@Test
public void testCreatingWhenNestedObservableHasValueNull() {
outerObservable = createNewNestingHierarchyWhereNestedObservableHasNullValue();
nesting = createNewNestingFromOuterObservable(outerObservable);
assertNotNull(nesting.innerObservableProperty().getValue());
assertFalse(nesting.innerObservableProperty().getValue().isPresent());
}
// nested value
/**
* Tests whether the {@link #nesting}'s {@link Nesting#innerObservableProperty() innerObservable} is updated correctly when
* one of the {@link #outerObservable}'s nested values is changed.
*/
@Test
public void testWhenSettingNestedValue() {
setNewValue(outerObservable, Level.NESTED, Value.ANY);
assertSame(getNestingObservable(nesting), getInnerObservable(outerObservable));
}
/**
* Tests whether the {@link #nesting}'s {@link Nesting#innerObservableProperty() innerObservable} is updated correctly when
* one of the {@link #outerObservable}'s nested values is changed to one which contains null observables.
*/
@Test
public void testWhenSettingNestedValueWithNullObservables() {
setNewValue(outerObservable, Level.NESTED, Value.ANY_WITH_NULL_OBSERVABLE);
assertNull(getNestingObservable(nesting));
}
/**
* Tests whether the {@link #nesting}'s {@link Nesting#innerObservableProperty() innerObservable} is updated correctly when
* one of the {@link #outerObservable}'s nested values is set to null.
*/
@Test
public void testWhenSettingNestedValueToNull() {
setNewValue(outerObservable, Level.NESTED, Value.NULL);
assertNull(getNestingObservable(nesting));
}
// outer value
/**
* Tests whether the {@link #nesting}'s {@link Nesting#innerObservableProperty() innerObservable} is updated correctly when
* the {@link #outerObservable}'s outer value is changed.
*/
@Test
public void testWhenSettingOuterValue() {
setNewValue(outerObservable, Level.OUTER, Value.ANY);
assertSame(getNestingObservable(nesting), getInnerObservable(outerObservable));
}
/**
* Tests whether the {@link #nesting}'s {@link Nesting#innerObservableProperty() innerObservable} is updated correctly when
* the {@link #outerObservable}'s outer value is changed to one which contains null observables.
*/
@Test
public void testWhenSettingOuterValueWithNullObservables() {
setNewValue(outerObservable, Level.OUTER, Value.ANY_WITH_NULL_OBSERVABLE);
assertNull(getNestingObservable(nesting));
}
/**
* Tests whether the {@link #nesting}'s {@link Nesting#innerObservableProperty() innerObservable} is updated correctly when
* the {@link #outerObservable}'s outer value is set to null.
*/
@Test
public void testWhenSettingOuterValueToNull() {
setNewValue(outerObservable, Level.OUTER, Value.NULL);
assertNull(getNestingObservable(nesting));
}
//#end TESTS
// #begin ABSTRACT METHODS
/**
* Creates a new outer observable with a null value. The returned instances must be new for each call.
*
* @return an {@link ObservableValue} containing null
*/
protected abstract OO createNewNestingHierarchyWhereOuterObservableHasNullValue();
/**
* Creates a new nesting hierarchy where one of the nested observables contains null and returns the outer
* observable. All returned instances must be new for each call.
*
* @return an {@link ObservableValue} containing the outer value of a nesting hierarchy
*/
protected abstract OO createNewNestingHierarchyWhereNestedObservableHasNullValue();
/**
* Sets a new value of the specified kind on the specified level of the nesting hierarchy contained in the specified
* outer observable.
*
* @param outerObservable
* the {@link ObservableValue} which contains the nesting hierarchy's outer value
* @param level
* the {@link Level} on which the new value will be set
* @param newValue
* the kind of {@link Value} which will be set
*/
protected abstract void setNewValue(OO outerObservable, Level level, Value newValue);
//#end ABSTRACT METHODS
// #begin INNER CLASSES
/**
* Indicates on which level of the nesting hierarchy a new value will be set by
* {@link AbstractDeepNestingTest#setNewValue(Observable, Level, Value) setNewValue}.
*/
protected enum Level {
/**
* The outer level.
*/
OUTER,
/**
* A level below the outer level.
*/
NESTED,
}
/**
* Indicates what kind of value will be set by {@link AbstractDeepNestingTest#setNewValue(Observable, Level, Value)
* setNewValue}.
*/
protected enum Value {
/**
* The new value will be some fully initialized instance.
*/
ANY,
/**
* The new value will be an instance whose observables are null
*/
ANY_WITH_NULL_OBSERVABLE,
/**
* The new value will be null.
*/
NULL,
}
//#end INNER CLASSES
}