Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@
*/
class OffRoadValidator extends SourceValidator<OffRoadMobileEmissionSource> {

private enum EmissionMethod {
POWER, FUEL, NOT_REQUIRED, MISSING
}

private final OffRoadValidationHelper validationHelper;

OffRoadValidator(final List<AeriusException> errors, final List<AeriusException> warnings, final OffRoadValidationHelper validationHelper) {
Expand Down Expand Up @@ -65,30 +69,67 @@ private boolean validateStandardOffRoad(final StandardOffRoadMobileSource subSou

private boolean validateOffRoadProperties(final StandardOffRoadMobileSource subSource) {
// Combine all validations in separate statements to make sure each validation is run to collect all validation warnings/errors.
boolean valid = validatePowerOrLiterFuel(subSource);
valid = validateOffRoadPowerRange(subSource) && valid;
valid = validateOffRoadOperatingHours(subSource) && valid;
return validateOffRoadLiterAdBlue(subSource) && valid;
final boolean valid = switch (determineEmissionMethod(subSource)) {
case POWER -> validatePowerBased(subSource);
case FUEL -> validateFuelBased(subSource);
case NOT_REQUIRED -> validateNotRequired(subSource);
case MISSING -> {
getErrors().add(new AeriusException(ImaerExceptionReason.MOBILE_SOURCE_MISSING_POWER_OR_LITER_FUEL, subSource.getDescription()));
yield false;
}
};
return validateOffRoadOperatingHours(subSource) && valid;
}

private EmissionMethod determineEmissionMethod(final StandardOffRoadMobileSource subSource) {
final String code = subSource.getOffRoadMobileSourceCode();
final boolean hasPower = subSource.getPower() != null && subSource.getPower() > 0;
final boolean hasFuel = subSource.getLiterFuelPerYear() != null && subSource.getLiterFuelPerYear() > 0;
// A category can accept power, fuel or neither.
if (hasPower && validationHelper.expectsPower(code)) {
return EmissionMethod.POWER;
} else if (hasFuel && validationHelper.expectsLiterFuelPerYear(code)) {
return EmissionMethod.FUEL;
} else if (!validationHelper.expectsPower(code) && !validationHelper.expectsLiterFuelPerYear(code)) {
return EmissionMethod.NOT_REQUIRED;
} else {
return EmissionMethod.MISSING;
}
}

private boolean validatePowerOrLiterFuel(final StandardOffRoadMobileSource subSource) {
private boolean validateNotRequired(final StandardOffRoadMobileSource subSource) {
// Category expects neither power nor fuel; clear those inputs.
subSource.setPower(null);
subSource.setLiterFuelPerYear(null);
return validateOffRoadLiterAdBlue(subSource);
}

private boolean validatePowerBased(final StandardOffRoadMobileSource subSource) {
// Power "!= null && > 0" checks have been handled in determineEmissionMethod already

// Unused for power based; Set to null
subSource.setLiterFuelPerYear(null);
subSource.setLiterAdBluePerYear(null);

return validateOffRoadPowerRange(subSource);
}

private boolean validateFuelBased(final StandardOffRoadMobileSource subSource) {
boolean valid = true;
final String code = subSource.getOffRoadMobileSourceCode();
final boolean expectsPower = validationHelper.expectsPower(code);
final boolean expectsFuel = validationHelper.expectsLiterFuelPerYear(code);
final boolean noPowerButExpected = expectsPower && subSource.getPower() == null;
final boolean noFuelButExpected = expectsFuel && subSource.getLiterFuelPerYear() == null;

if ((noPowerButExpected && noFuelButExpected) || (noPowerButExpected && !expectsFuel) || (noFuelButExpected && !expectsPower)) {
if (expectsFuel && subSource.getLiterFuelPerYear() == null) {
getErrors().add(new AeriusException(ImaerExceptionReason.MOBILE_SOURCE_MISSING_POWER_OR_LITER_FUEL, subSource.getDescription()));
valid = false;
}
if (!expectsPower) {
subSource.setPower(null);
}
if (!expectsFuel) {
subSource.setLiterFuelPerYear(null);
}
// Fallback in case custom GML is passed with power = 0
subSource.setPower(null);

valid = validateOffRoadLiterAdBlue(subSource) && valid;
return valid;
}

Expand All @@ -98,7 +139,7 @@ private boolean validateOffRoadPowerRange(final StandardOffRoadMobileSource subS
if (validationHelper.expectsPower(code)) {
final Optional<IntRange> powerRange = validationHelper.getPowerRange(code);

if (subSource.getPower() != null && subSource.getPower() > 0 && !powerRange.map(range -> range.inRange(subSource.getPower())).orElse(false)) {
if (!powerRange.map(range -> range.inRange(subSource.getPower())).orElse(false)) {
getErrors().add(new AeriusException(ImaerExceptionReason.MOBILE_SOURCE_POWER_NOT_WITHIN_RANGE, subSource.getDescription(),
powerRange.get().toString(), String.valueOf(subSource.getPower())));
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ void testValidSubSourceAllRequired() {
*/
@ParameterizedTest
@CsvSource({",", "10_000"})
void testSubSourceMissingPower(final Integer fuel) {
void testPowerSubSourceMissingPower(final Integer fuel) {
final OffRoadMobileEmissionSource source = createSource(null, fuel, 3_000, 500);

mockCategory(MockCategory.POWER);
Expand All @@ -151,6 +151,16 @@ void testSubSourceMissingPower(final Integer fuel) {
assertFirstError(ImaerExceptionReason.MOBILE_SOURCE_MISSING_POWER_OR_LITER_FUEL, List.of(SUB_SOURCE_DESCRIPTION));
}

@Test
void testSubSourceMissingLiterFuelAndPower() {
final OffRoadMobileEmissionSource source = createSource(null, null, 3_000, 500);

mockCategory(MockCategory.FUEL, MockCategory.HOURS, MockCategory.ADBLUE);

assertValidate(source, 1, 0);
assertFirstError(ImaerExceptionReason.MOBILE_SOURCE_MISSING_POWER_OR_LITER_FUEL, List.of(SUB_SOURCE_DESCRIPTION));
}

/**
* Test if correct error is given when fuel value is missing.
*
Expand All @@ -167,6 +177,15 @@ void testSubSourceMissingLiterFuel(final Integer power) {
assertFirstError(ImaerExceptionReason.MOBILE_SOURCE_MISSING_POWER_OR_LITER_FUEL, List.of(SUB_SOURCE_DESCRIPTION));
}

@Test
void testSubSourceFuelSuppliedForPowerAndFuelCategoryShouldBeValid() {
final OffRoadMobileEmissionSource source = createSource(null, 10_000, null, null);

mockCategory(MockCategory.POWER, MockCategory.FUEL);

assertNoErrorsOrWarnings(source);
}

@Test
void testSubSourceMissingOperatingHours() {
final OffRoadMobileEmissionSource source = createSource(null, 10_000, null, 500);
Expand All @@ -190,7 +209,9 @@ void testSubSourceMissingAll() {
final OffRoadMobileEmissionSource source = createSource(null, null, null, null);

mockCategory(MockCategory.POWER, MockCategory.FUEL, MockCategory.HOURS, MockCategory.ADBLUE);
assertValidate(source, 3, 0);
assertValidate(source, 2, 0);
Comment thread
MichielJanssen-DAT marked this conversation as resolved.
assertAeriusException(ImaerExceptionReason.MOBILE_SOURCE_MISSING_POWER_OR_LITER_FUEL, List.of(SUB_SOURCE_DESCRIPTION), errors.get(0));
assertAeriusException(ImaerExceptionReason.MOBILE_SOURCE_MISSING_OPERATING_HOURS, List.of(SUB_SOURCE_DESCRIPTION), errors.get(1));
}

@Test
Expand All @@ -204,6 +225,15 @@ void testSubSourceTooMuchLiterAdBlue() {
warnings.get(0));
}

@Test
void testValidPowerBasedSubSourceShouldNotRequireAdBlue() {
when(validationHelper.getPowerRange(any())).thenReturn(Optional.of(IntRangeUtil.valueOf("[67,420]")));
final OffRoadMobileEmissionSource source = createSource(200, null, 1_337, null);

mockCategory(MockCategory.POWER, MockCategory.HOURS, MockCategory.ADBLUE);
assertNoErrorsOrWarnings(source);
}

private static OffRoadMobileEmissionSource createSource(final Integer power, final Integer fuel, final Integer hours, final Integer adBlue) {
final OffRoadMobileEmissionSource source = new OffRoadMobileEmissionSource();
final StandardOffRoadMobileSource subSource = new StandardOffRoadMobileSource();
Expand All @@ -222,10 +252,10 @@ private static OffRoadMobileEmissionSource createSource(final Integer power, fin
private void mockCategory(final MockCategory... mockCategories) {
final Set<MockCategory> mockCategoriesSet = Set.of(mockCategories);
when(validationHelper.isValidOffRoadMobileSourceCode(CODE)).thenReturn(true);
when(validationHelper.expectsPower(CODE)).thenReturn(mockCategoriesSet.contains(MockCategory.POWER));
when(validationHelper.expectsLiterFuelPerYear(CODE)).thenReturn(mockCategoriesSet.contains(MockCategory.FUEL));
when(validationHelper.expectsOperatingHoursPerYear(CODE)).thenReturn(mockCategoriesSet.contains(MockCategory.HOURS));
when(validationHelper.expectsLiterAdBluePerYear(CODE)).thenReturn(mockCategoriesSet.contains(MockCategory.ADBLUE));
lenient().when(validationHelper.expectsPower(CODE)).thenReturn(mockCategoriesSet.contains(MockCategory.POWER));
lenient().when(validationHelper.expectsLiterFuelPerYear(CODE)).thenReturn(mockCategoriesSet.contains(MockCategory.FUEL));
lenient().when(validationHelper.expectsLiterAdBluePerYear(CODE)).thenReturn(mockCategoriesSet.contains(MockCategory.ADBLUE));
lenient().when(validationHelper.getMaxAdBlueFuelRatio(CODE)).thenReturn(OptionalDouble.of(0.07));
}

Expand Down