Skip to content

Commit

Permalink
Unit test for LayerProcessingState
Browse files Browse the repository at this point in the history
  • Loading branch information
smithkm committed Oct 25, 2024
1 parent 29ac998 commit 4adebb5
Show file tree
Hide file tree
Showing 4 changed files with 304 additions and 44 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package ca.bc.gov.nrs.vdyp.back;

import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.compatibilityVariable;
import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.notPresent;
import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.present;
import static org.hamcrest.MatcherAssert.assertThat;
Expand Down Expand Up @@ -37,6 +38,7 @@
import ca.bc.gov.nrs.vdyp.model.VdypPolygon;
import ca.bc.gov.nrs.vdyp.model.VolumeVariable;
import ca.bc.gov.nrs.vdyp.test.TestUtils;
import ca.bc.gov.nrs.vdyp.test.VdypMatchers;

class BackProcessingEngineTest {

Expand Down Expand Up @@ -332,55 +334,11 @@ protected boolean matchesSafely(ComponentSizeLimits item, Description mismatchDe

};
}

static Matcher<BackProcessingState> backCV(String name, Object p1, Object p2, Matcher<Float> expected) {
return compatibilityVariable(name, expected, BackProcessingState.class, p1, p2);
}

static Matcher<BackProcessingState> backCV(String name, Object p1, Matcher<Float> expected) {
return compatibilityVariable(name, expected, BackProcessingState.class, p1);
}

static <T> Matcher<T>
compatibilityVariable(String name, Matcher<Float> expected, Class<T> klazz, Object... params) {
return new TypeSafeDiagnosingMatcher<>(klazz) {

@Override
public void describeTo(Description description) {
description.appendText(name).appendValueList("(", ", ", ") ", params);
description.appendDescriptionOf(expected);
}

@Override
protected boolean matchesSafely(T item, Description mismatchDescription) {
Method method;
try {
method = klazz.getMethod(
name,
(Class<?>[]) Arrays.stream(params)
.map(o -> o instanceof Integer ? Integer.TYPE : o.getClass()).toArray(Class[]::new)
);
} catch (NoSuchMethodException e) {
mismatchDescription.appendText("Method " + name + " does not exist");
return false;
}

float result;
try {
result = (float) method.invoke(item, params);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
mismatchDescription.appendText(e.getMessage());
return false;
}

if (expected.matches(result)) {
return true;
}

expected.describeMismatch(result, mismatchDescription);
return false;
}

};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
package ca.bc.gov.nrs.vdyp.application;

import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.compatibilityVariable;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasProperty;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;

import org.easymock.EasyMock;
import org.easymock.IMocksControl;
import org.hamcrest.Matcher;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

import ca.bc.gov.nrs.vdyp.controlmap.ResolvedControlMap;
import ca.bc.gov.nrs.vdyp.model.LayerType;
import ca.bc.gov.nrs.vdyp.model.MatrixMap2;
import ca.bc.gov.nrs.vdyp.model.MatrixMap2Impl;
import ca.bc.gov.nrs.vdyp.model.MatrixMap3;
import ca.bc.gov.nrs.vdyp.model.MatrixMap3Impl;
import ca.bc.gov.nrs.vdyp.model.UtilizationClass;
import ca.bc.gov.nrs.vdyp.model.UtilizationClassVariable;
import ca.bc.gov.nrs.vdyp.model.VdypLayer;
import ca.bc.gov.nrs.vdyp.model.VdypPolygon;
import ca.bc.gov.nrs.vdyp.model.VdypSpecies;
import ca.bc.gov.nrs.vdyp.model.VolumeVariable;
import ca.bc.gov.nrs.vdyp.processing_state.LayerProcessingState;
import ca.bc.gov.nrs.vdyp.processing_state.ProcessingState;
import ca.bc.gov.nrs.vdyp.test.TestUtils;

class LayerProcessingStateTest {

static class TestLayerProcessingState extends LayerProcessingState<ResolvedControlMap, TestLayerProcessingState> {

protected TestLayerProcessingState(
ProcessingState<ResolvedControlMap, TestLayerProcessingState> ps, VdypPolygon polygon,
LayerType subjectLayerType
) throws ProcessingException {
super(ps, polygon, subjectLayerType);
}

@Override
protected Predicate<VdypSpecies> getBankFilter() {
return spec -> true;
}

@Override
protected void applyCompatibilityVariables(VdypSpecies species, int i) {
// TODO Auto-generated method stub

}

@Override
protected VdypLayer updateLayerFromBank() {
// TODO Auto-generated method stub
return null;
}

}

@Nested
class Constructor {

@Test
void testConstructNoSpecies() throws ProcessingException {
var em = EasyMock.createStrictControl();
ProcessingState<ResolvedControlMap, TestLayerProcessingState> parent = em
.createMock("parent", ProcessingState.class);
var polygon = VdypPolygon.build(pb -> {
pb.polygonIdentifier("Test", 2024);
pb.biogeoclimaticZone(TestUtils.mockBec());
pb.forestInventoryZone("A");
pb.percentAvailable(90f);

pb.addLayer(lb -> {
lb.layerType(LayerType.PRIMARY);

});
});

em.replay();

var unit = new TestLayerProcessingState(parent, polygon, LayerType.PRIMARY);

assertThat(unit, hasProperty("NSpecies", is(0)));

em.verify();

}

@Test
void testConstructOneSpecies() throws ProcessingException {
var em = EasyMock.createStrictControl();
ProcessingState<ResolvedControlMap, TestLayerProcessingState> parent = em
.createMock("parent", ProcessingState.class);
var polygon = VdypPolygon.build(pb -> {
pb.polygonIdentifier("Test", 2024);
pb.biogeoclimaticZone(TestUtils.mockBec());
pb.forestInventoryZone("A");
pb.percentAvailable(90f);

pb.addLayer(lb -> {
lb.layerType(LayerType.PRIMARY);

lb.addSpecies(sb -> {
sb.genus("A", 1);
});
});
});

em.replay();

var unit = new TestLayerProcessingState(parent, polygon, LayerType.PRIMARY);

assertThat(unit, hasProperty("NSpecies", is(1)));

em.verify();

}
}

@Nested
class SetCompatibilityVariables {
IMocksControl em;
LayerProcessingState<?, ?> unit;

MatrixMap3<UtilizationClass, VolumeVariable, LayerType, Float>[] cvVolume;
MatrixMap2<UtilizationClass, LayerType, Float>[] cvBa;
MatrixMap2<UtilizationClass, LayerType, Float>[] cvDq;
Map<UtilizationClassVariable, Float>[] cvSm;

@BeforeEach
@SuppressWarnings("unchecked")
void setup() throws ProcessingException {
var em = EasyMock.createStrictControl();
ProcessingState<ResolvedControlMap, TestLayerProcessingState> parent = em
.createMock("parent", ProcessingState.class);
var polygon = VdypPolygon.build(pb -> {
pb.polygonIdentifier("Test", 2024);
pb.biogeoclimaticZone(TestUtils.mockBec());
pb.forestInventoryZone("A");
pb.percentAvailable(90f);

pb.addLayer(lb -> {
lb.layerType(LayerType.PRIMARY);

lb.addSpecies(sb -> {
sb.genus("A", 1);
});
});
});

em.replay();

unit = new TestLayerProcessingState(parent, polygon, LayerType.PRIMARY);

cvVolume = new MatrixMap3[] { null, new MatrixMap3Impl<UtilizationClass, VolumeVariable, LayerType, Float>(
List.of(UtilizationClass.values()), List.of(VolumeVariable.values()), List.of(LayerType.values()),
(uc, vv, lt) -> 11f + vv.ordinal() * 2f + uc.ordinal() * 3f + lt.ordinal() * 5f
) };

cvBa = new MatrixMap2[] { null,
new MatrixMap2Impl<UtilizationClass, LayerType, Float>(
List.of(UtilizationClass.values()), List.of(LayerType.values()),
(uc, lt) -> 13f + uc.ordinal() * 3f + lt.ordinal() * 5f
) };

cvDq = new MatrixMap2[] { null,
new MatrixMap2Impl<UtilizationClass, LayerType, Float>(
List.of(UtilizationClass.values()), List.of(LayerType.values()),
(uc, lt) -> 17f + uc.ordinal() * 3f + lt.ordinal() * 5f
) };
cvSm = new EnumMap[] { null, new EnumMap<UtilizationClassVariable, Float>(UtilizationClassVariable.class) };

for (var uc : UtilizationClassVariable.values()) {
cvSm[1].put(uc, uc.ordinal() * 7f);
}

}

@Test
void testFailBeforeSet() throws ProcessingException {
assertThrows(
IllegalStateException.class,
() -> unit.getCVVolume(1, UtilizationClass.ALL, VolumeVariable.CLOSE_UTIL_VOL, LayerType.PRIMARY),
"getCVVolume"
);
assertThrows(
IllegalStateException.class, () -> unit.getCVBasalArea(1, UtilizationClass.ALL, LayerType.PRIMARY),
"getCVBasalArea"
);
assertThrows(
IllegalStateException.class,
() -> unit.getCVQuadraticMeanDiameter(1, UtilizationClass.ALL, LayerType.PRIMARY),
"getCVQuadraticMeanDiameter"
);
assertThrows(
IllegalStateException.class, () -> unit.getCVSmall(1, UtilizationClassVariable.BASAL_AREA),
"getCVSmall"
);
}

@Test
void testSucceedAfterSet() throws ProcessingException {
unit.setCompatibilityVariableDetails(cvVolume, cvBa, cvDq, cvSm);
assertThat(
unit,
testCV(
"getCVVolume", 1, UtilizationClass.ALL, VolumeVariable.CLOSE_UTIL_VOL, LayerType.PRIMARY,
is(16f)
)
);
assertThat(unit, testCV("getCVBasalArea", 1, UtilizationClass.ALL, LayerType.PRIMARY, is(16f)));
assertThat(
unit,
testCV("getCVQuadraticMeanDiameter", 1, UtilizationClass.U125TO175, LayerType.PRIMARY, is(26f))
);
assertThat(unit, testCV("getCVSmall", 1, UtilizationClassVariable.QUAD_MEAN_DIAMETER, is(7f)));
}

@Test
void testFailDoubleSet() throws ProcessingException {
unit.setCompatibilityVariableDetails(cvVolume, cvBa, cvDq, cvSm);
assertThrows(
IllegalStateException.class, () -> unit.setCompatibilityVariableDetails(cvVolume, cvBa, cvDq, cvSm)
);
}

@SuppressWarnings({ "unchecked", "rawtypes" })
Matcher<LayerProcessingState<?, ?>>
testCV(String name, Object p1, Object p2, Object p3, Object p4, Matcher<Float> match) {
return (Matcher) compatibilityVariable(name, match, LayerProcessingState.class, p1, p2, p3, p4);
}

@SuppressWarnings({ "unchecked", "rawtypes" })
Matcher<LayerProcessingState<?, ?>> testCV(String name, Object p1, Object p2, Object p3, Matcher<Float> match) {
return (Matcher) compatibilityVariable(name, match, LayerProcessingState.class, p1, p2, p3);
}

@SuppressWarnings({ "unchecked", "rawtypes" })
Matcher<LayerProcessingState<?, ?>> testCV(String name, Object p1, Object p2, Matcher<Float> match) {
return (Matcher) compatibilityVariable(name, match, LayerProcessingState.class, p1, p2);
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -634,4 +634,8 @@ public static BiFunction<Integer, Integer, Optional<Coefficients>> netDecayMap(i

return layer.getSpecies().get(ids[0]);
}

public static BecDefinition mockBec() {
return new BecDefinition("T", Region.COASTAL, "Test");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -693,4 +695,47 @@ protected boolean matchesSafely(Coefficients item, Description mismatchDescripti
};
}

public static <T> Matcher<T>
compatibilityVariable(String name, Matcher<Float> expected, Class<T> klazz, Object... params) {
return new TypeSafeDiagnosingMatcher<>(klazz) {

@Override
public void describeTo(Description description) {
description.appendText(name).appendValueList("(", ", ", ") ", params);
description.appendDescriptionOf(expected);
}

@Override
protected boolean matchesSafely(T item, Description mismatchDescription) {
Method method;
try {
method = klazz.getMethod(
name,
(Class<?>[]) Arrays.stream(params)
.map(o -> o instanceof Integer ? Integer.TYPE : o.getClass()).toArray(Class[]::new)
);
} catch (NoSuchMethodException e) {
mismatchDescription.appendText("Method " + name + " does not exist");
return false;
}

float result;
try {
result = (float) method.invoke(item, params);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
mismatchDescription.appendText(e.getMessage());
return false;
}

if (expected.matches(result)) {
return true;
}

expected.describeMismatch(result, mismatchDescription);
return false;
}

};
}

}

0 comments on commit 4adebb5

Please sign in to comment.