I'm trying to undersand how ConstraintVerifier works by reviewing the VehicleRoutingContraintProviderTest test cases and the one thing that is not clear to me is the inclusion of other two Visit facts to the .given method. Looking at the VehicleRoutingContraintProviderTest.vehicleCapacityPenalized method I see the test being setup with new insteances of Vehicle and two Visit instances. The visits are then added to the Vehicle List. Then both the Vehicle and two aditional Visit are passed to the .given method. My question: given the Visit objects are now a child of the Vehicle object, why pass them to the given method? What actions or checks are taken on the other two Visit objects? Doesn't the test simply invoke the VehicleRoutingConstraintProvider::minimizeTraveTime method with only the Vehicle visible? If I remove the two Visit instance parameters, the test still succeeds.
For the vehicle capacity constraint:
protected Constraint vehicleCapacity(ConstraintFactory factory) {
return factory.forEach(Vehicle.class)
.filter(vehicle -> vehicle.getTotalDemand() > vehicle.getCapacity())
.penalizeLong(HardSoftLongScore.ONE_HARD,
vehicle -> vehicle.getTotalDemand() - vehicle.getCapacity())
.justifyWith((vehicle, score) -> new VehicleCapacityJustification(vehicle.getId(), vehicle.getTotalDemand(),
vehicle.getCapacity()))
.asConstraint(VEHICLE_CAPACITY);
}
which has this test:
@Test
void vehicleCapacityPenalized() {
LocalDateTime tomorrow_07_00 = LocalDateTime.of(TOMORROW, LocalTime.of(7, 0));
LocalDateTime tomorrow_08_00 = LocalDateTime.of(TOMORROW, LocalTime.of(8, 0));
LocalDateTime tomorrow_10_00 = LocalDateTime.of(TOMORROW, LocalTime.of(10, 0));
Vehicle vehicleA = new Vehicle("1", 100, LOCATION_1, tomorrow_07_00);
Visit visit1 = new Visit("2", "John", LOCATION_2, 80, tomorrow_08_00, tomorrow_10_00, Duration.ofMinutes(30L));
vehicleA.getVisits().add(visit1);
Visit visit2 = new Visit("3", "Paul", LOCATION_3, 40, tomorrow_08_00, tomorrow_10_00, Duration.ofMinutes(30L));
vehicleA.getVisits().add(visit2);
constraintVerifier.verifyThat(VehicleRoutingConstraintProvider::vehicleCapacity)
.given(vehicleA, visit1, visit2)
.penalizesBy(20);
}
The Visit
facts are not required to be given, since Visit
is not used in a forEach
, join
, ifExists
or ifNotExists
building block.
However, if the constraint was hypothetically modified later to use Visit
via one of the above building blocks:
protected Constraint vehicleCapacity(ConstraintFactory factory) {
return factory.forEach(Vehicle.class)
.filter(vehicle -> vehicle.getTotalDemand() > vehicle.getCapacity())
.ifExists(Visit.class, Joiners.filtering((vehicle, visit) -> vehicle.getVisits().contains(visit)))
.penalizeLong(HardSoftLongScore.ONE_HARD,
vehicle -> vehicle.getTotalDemand() - vehicle.getCapacity())
.justifyWith((vehicle, score) -> new VehicleCapacityJustification(vehicle.getId(), vehicle.getTotalDemand(),
vehicle.getCapacity()))
.asConstraint(VEHICLE_CAPACITY);
}
Then the tests will fail without the Visit
facts being given, since the visits
from the Vehicle
are not automatically considered by the ConstraintVerifier
(so ifExists
will fail to find any Visit
that match the predicate despite the two Visits
in vehicle.visits
meeting the predicate).
Thus the purpose of adding the visit
facts to given
is so the test does not need to be modified if the Constraint
was changed to use Visit
via one of the above building blocks.