From 3154c06e4539104e5bdc4c51cec55b096ff9df8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn?= Date: Mon, 11 Sep 2023 14:16:19 +0200 Subject: [PATCH] Fixed Defect in Test case for Biceps:R5025 (#125) The Requirement Biceps:R5025 applies to all DescriptionModificationReports. However, the Precondition of its SDCcc Test case could only trigger Crt and Del ReportParts. Hence, when a Device only support Upt ReportParts in DescriptionModificationReports then the Requirement is applicable to it, but it cannot satisfy the test case. This PR solves this problem by letting the Test case use another Precondition that tries to trigger DescriptionModificationReports with Crt, Del and Upt ReportParts. # Checklist The following aspects have been respected by the author of this pull request, confirmed by both pull request assignee **and** reviewer: * Adherence to coding conventions * [x] Pull Request Assignee * [x] Reviewer * Adherence to javadoc conventions * [x] Pull Request Assignee * [x] Reviewer * Changelog update (necessity checked and entry added or not added respectively) * [x] Pull Request Assignee * [x] Reviewer * README update (necessity checked and entry added or not added respectively) * [x] Pull Request Assignee * [x] Reviewer * config update (necessity checked and entry added or not added respectively) * [x] Pull Request Assignee * [x] Reviewer * SDCcc executable ran against a test device (if necessary) * [x] Pull Request Assignee * [x] Reviewer --- CHANGELOG.md | 1 + README.md | 2 +- sdccc/pom.xml | 2 +- .../manipulation/FallbackManipulations.java | 13 + .../sdccc/manipulation/GRpcManipulations.java | 14 +- .../sdccc/manipulation/Manipulations.java | 8 + .../impl/ConditionalPreconditions.java | 16 +- .../impl/ManipulationPreconditions.java | 198 ++++++++---- .../InvariantMessageModelAnnexTest.java | 10 +- ...variantParticipantModelVersioningTest.java | 3 +- .../tests/util/ManipulationParameterUtil.java | 11 + .../draeger/medical/sdccc/util/Constants.java | 1 + .../impl/ConditionalPreconditionsTest.java | 12 +- .../impl/ManipulationPreconditionsTest.java | 286 +++++++++++++++++- 14 files changed, 496 insertions(+), 81 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd93537c..69cf9cd0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - report duplication issue in biceps:C11-C15, biceps:C5, biceps:R5046_0, biceps:B-284_0 as well as biceps:R5003. - biceps:5-4-7 tests confusing changes made by SetComponentActivation manipulations with changes made by SetMetricStatus. - glue:R0036_0 test can be blocked by long-running t2iapi RPCs. +- biceps:5025_0 test case could not be satisfied by devices that do not support inserting and removing descriptors. ## [7.0.1] - 2023-03-17 diff --git a/README.md b/README.md index 1243c9eb..65f0e978 100644 --- a/README.md +++ b/README.md @@ -288,7 +288,7 @@ this case in order to minimize the risk of such an invalid application going unn | C-14 | TriggerReport | | C-15 | TriggerReport | | R5024 | TriggerReport | -| R5025_0 | GetRemovableDescriptorsOfClass, RemoveDescriptor, InsertDescriptor | +| R5025_0 | GetRemovableDescriptorsOfClass, RemoveDescriptor, InsertDescriptor, TriggerDescriptorUpdate | | R5046_0 | GetRemovableDescriptorsOfClass, RemoveDescriptor, InsertDescriptor | | R5051 | GetRemovableDescriptorsOfClass, RemoveDescriptor, InsertDescriptor | | R5052 | TriggerAnyDescriptorUpdate | diff --git a/sdccc/pom.xml b/sdccc/pom.xml index 83c1782d..e5d185a0 100644 --- a/sdccc/pom.xml +++ b/sdccc/pom.xml @@ -172,7 +172,7 @@ com.draeger.medical t2iapi - 3.0.0.262 + 3.0.0.296 diff --git a/sdccc/src/main/java/com/draeger/medical/sdccc/manipulation/FallbackManipulations.java b/sdccc/src/main/java/com/draeger/medical/sdccc/manipulation/FallbackManipulations.java index b2d178ba..64d8af26 100644 --- a/sdccc/src/main/java/com/draeger/medical/sdccc/manipulation/FallbackManipulations.java +++ b/sdccc/src/main/java/com/draeger/medical/sdccc/manipulation/FallbackManipulations.java @@ -234,6 +234,19 @@ public void close() {} return interactionResult ? ResponseTypes.Result.RESULT_SUCCESS : ResponseTypes.Result.RESULT_FAIL; } + @Override + public ResponseTypes.Result triggerDescriptorUpdate(final List handles) { + final var triggerReportString = "Trigger a descriptor update for handles %s"; + final var interactionMessage = String.format(triggerReportString, String.join(", ", handles)); + final var interactionResult = interactionFactory + .createUserInteraction(new FilterInputStream(System.in) { + @Override + public void close() {} + }) + .displayYesNoUserInteraction(interactionMessage); + return interactionResult ? ResponseTypes.Result.RESULT_SUCCESS : ResponseTypes.Result.RESULT_FAIL; + } + @Override public ResponseTypes.Result triggerAnyDescriptorUpdate() { final var interactionMessage = "Trigger a descriptor update for some descriptor"; diff --git a/sdccc/src/main/java/com/draeger/medical/sdccc/manipulation/GRpcManipulations.java b/sdccc/src/main/java/com/draeger/medical/sdccc/manipulation/GRpcManipulations.java index 57283c20..05ce40e9 100644 --- a/sdccc/src/main/java/com/draeger/medical/sdccc/manipulation/GRpcManipulations.java +++ b/sdccc/src/main/java/com/draeger/medical/sdccc/manipulation/GRpcManipulations.java @@ -294,15 +294,21 @@ public ResponseTypes.Result setMetricStatus( @Override public ResponseTypes.Result triggerDescriptorUpdate(final String handle) { - final var message = - BasicRequests.BasicHandleRequest.newBuilder().setHandle(handle).build(); + return triggerDescriptorUpdate(List.of(handle)); + } + + @Override + public ResponseTypes.Result triggerDescriptorUpdate(final List handles) { + final var message = DeviceRequests.TriggerDescriptorUpdateRequest.newBuilder() + .addAllHandle(handles) + .build(); return performCallWrapper( v -> deviceStub.triggerDescriptorUpdate(message), - v -> fallback.triggerDescriptorUpdate(handle), + v -> fallback.triggerDescriptorUpdate(handles), BasicResponses.BasicResponse::getResult, BasicResponses.BasicResponse::getResult, - ManipulationParameterUtil.buildHandleManipulationParameterData(handle)); + ManipulationParameterUtil.buildTriggerDescriptorUpdateParameterData(handles)); } @Override diff --git a/sdccc/src/main/java/com/draeger/medical/sdccc/manipulation/Manipulations.java b/sdccc/src/main/java/com/draeger/medical/sdccc/manipulation/Manipulations.java index 64fbd73a..639f7e8d 100644 --- a/sdccc/src/main/java/com/draeger/medical/sdccc/manipulation/Manipulations.java +++ b/sdccc/src/main/java/com/draeger/medical/sdccc/manipulation/Manipulations.java @@ -137,6 +137,14 @@ ResponseTypes.Result setMetricStatus( */ ResponseTypes.Result triggerDescriptorUpdate(String handle); + /** + * Trigger a descriptor update for the provided descriptor handles. + * + * @param handles list of descriptor handles to trigger an update for. + * @return the result of the manipulation + */ + ResponseTypes.Result triggerDescriptorUpdate(List handles); + /** * Trigger a descriptor update for some descriptor (chosen by the device). * diff --git a/sdccc/src/main/java/com/draeger/medical/sdccc/manipulation/precondition/impl/ConditionalPreconditions.java b/sdccc/src/main/java/com/draeger/medical/sdccc/manipulation/precondition/impl/ConditionalPreconditions.java index e93e8762..2494891c 100644 --- a/sdccc/src/main/java/com/draeger/medical/sdccc/manipulation/precondition/impl/ConditionalPreconditions.java +++ b/sdccc/src/main/java/com/draeger/medical/sdccc/manipulation/precondition/impl/ConditionalPreconditions.java @@ -461,11 +461,11 @@ static boolean manipulation(final Injector injector) { } // all options exhausted and the goal is still not reached - LOG.error("Unable to find any MdsDescriptors using the GetRemovableDescriptorsOfType() manipulation " + LOG.error("Unable to find any MdsDescriptors using the GetRemovableDescriptorsOfClass() manipulation " + "that can be inserted, updated and removed (at least one for each is required for the test " + "applying this precondition). " + "Please check if the test case applying this precondition is applicable to your device and if the " - + "GetRemovableDescriptorsOfType, InsertDescriptor, RemoveDescriptor, and TriggerDescriptorUpdate " + + "GetRemovableDescriptorsOfClass, InsertDescriptor, RemoveDescriptor, and TriggerDescriptorUpdate " + "manipulations have been implemented correctly."); return false; } @@ -660,15 +660,17 @@ static boolean manipulation(final Injector injector) { * Precondition which checks whether DescriptionModificationReport messages containing an insertion * or deletion have been received, triggering description changes otherwise. */ - public static class DescriptionChangedPrecondition extends SimplePrecondition { + public static class DescriptionModificationCrtOrDelPrecondition extends SimplePrecondition { - private static final Logger LOG = LogManager.getLogger(DescriptionChangedPrecondition.class); + private static final Logger LOG = LogManager.getLogger(DescriptionModificationCrtOrDelPrecondition.class); /** - * Creates a description changed precondition check. + * Creates a description modification crt or del precondition check. */ - public DescriptionChangedPrecondition() { - super(DescriptionChangedPrecondition::preconditionCheck, DescriptionChangedPrecondition::manipulation); + public DescriptionModificationCrtOrDelPrecondition() { + super( + DescriptionModificationCrtOrDelPrecondition::preconditionCheck, + DescriptionModificationCrtOrDelPrecondition::manipulation); } static boolean preconditionCheck(final Injector injector) throws PreconditionException { diff --git a/sdccc/src/main/java/com/draeger/medical/sdccc/manipulation/precondition/impl/ManipulationPreconditions.java b/sdccc/src/main/java/com/draeger/medical/sdccc/manipulation/precondition/impl/ManipulationPreconditions.java index db31af2e..f3199a0e 100644 --- a/sdccc/src/main/java/com/draeger/medical/sdccc/manipulation/precondition/impl/ManipulationPreconditions.java +++ b/sdccc/src/main/java/com/draeger/medical/sdccc/manipulation/precondition/impl/ManipulationPreconditions.java @@ -10,6 +10,7 @@ import com.draeger.medical.sdccc.configuration.TestSuiteConfig; import com.draeger.medical.sdccc.manipulation.Manipulations; import com.draeger.medical.sdccc.manipulation.precondition.ManipulationPrecondition; +import com.draeger.medical.sdccc.manipulation.precondition.PreconditionException; import com.draeger.medical.sdccc.sdcri.testclient.TestClient; import com.draeger.medical.sdccc.tests.util.ImpliedValueUtil; import com.draeger.medical.sdccc.util.TestRunObserver; @@ -29,6 +30,7 @@ import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; +import org.apache.commons.lang3.tuple.Pair; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.somda.sdc.biceps.common.MdibEntity; @@ -38,6 +40,7 @@ import org.somda.sdc.biceps.common.event.ContextStateModificationMessage; import org.somda.sdc.biceps.model.participant.AbstractAlertState; import org.somda.sdc.biceps.model.participant.AbstractContextState; +import org.somda.sdc.biceps.model.participant.AbstractDescriptor; import org.somda.sdc.biceps.model.participant.AbstractDeviceComponentDescriptor; import org.somda.sdc.biceps.model.participant.AbstractDeviceComponentState; import org.somda.sdc.biceps.model.participant.AbstractMetricDescriptor; @@ -55,6 +58,7 @@ import org.somda.sdc.biceps.model.participant.ContextAssociation; import org.somda.sdc.biceps.model.participant.LocationContextDescriptor; import org.somda.sdc.biceps.model.participant.LocationContextState; +import org.somda.sdc.biceps.model.participant.MdsDescriptor; import org.somda.sdc.biceps.model.participant.MetricCategory; import org.somda.sdc.biceps.model.participant.PatientContextDescriptor; import org.somda.sdc.biceps.model.participant.PatientContextState; @@ -114,6 +118,114 @@ private static boolean manipulateMetricStatus( return manipulationResults.contains(ResponseTypes.Result.RESULT_SUCCESS); } + private static boolean removeAndReinsertDescriptors(final Injector injector, final Logger log) { + final var manipulations = injector.getInstance(Manipulations.class); + final var testClient = injector.getInstance(TestClient.class); + final var testRunObserver = injector.getInstance(TestRunObserver.class); + + final MdibAccess mdibAccess; + final SdcRemoteDevice remoteDevice; + + remoteDevice = testClient.getSdcRemoteDevice(); + if (remoteDevice == null) { + testRunObserver.invalidateTestRun("Remote device could not be accessed, likely due to a disconnect"); + return false; + } + + mdibAccess = remoteDevice.getMdibAccess(); + + final var modifiableDescriptors = manipulations.getRemovableDescriptorsOfClass(); + if (modifiableDescriptors.isEmpty()) { + log.info("No modifiable descriptors available for manipulation"); + return false; + } + + final var manipulationResults = new HashSet(); + for (String handle : modifiableDescriptors) { + // determine if descriptor is currently present + var descriptorEntity = mdibAccess.getEntity(handle); + log.debug("Descriptor {} presence: {}", handle, descriptorEntity.isPresent()); + + // if the descriptor is not present, insert it first + if (descriptorEntity.isEmpty()) { + manipulationResults.add(manipulations.insertDescriptor(handle)); + descriptorEntity = mdibAccess.getEntity(handle); + if (descriptorEntity.isEmpty()) { + manipulationResults.add(ResponseTypes.Result.RESULT_FAIL); + } + log.debug("Descriptor {} presence: {}", handle, descriptorEntity.isPresent()); + } + + // remove descriptor + manipulationResults.add(manipulations.removeDescriptor(handle)); + descriptorEntity = mdibAccess.getEntity(handle); + if (descriptorEntity.isPresent()) { + manipulationResults.add(ResponseTypes.Result.RESULT_FAIL); + } + log.debug("Descriptor {} presence: {}", handle, descriptorEntity.isPresent()); + + // reinsert descriptor + manipulationResults.add(manipulations.insertDescriptor(handle)); + descriptorEntity = mdibAccess.getEntity(handle); + if (descriptorEntity.isEmpty()) { + manipulationResults.add(ResponseTypes.Result.RESULT_FAIL); + } + log.debug("Descriptor {} presence: {}", handle, descriptorEntity.isPresent()); + + if (manipulationResults.contains(ResponseTypes.Result.RESULT_FAIL)) { + testRunObserver.invalidateTestRun(String.format( + "Could not successfully modify descriptor %s, stopping the precondition", handle)); + break; + } + } + + return !manipulationResults.contains(ResponseTypes.Result.RESULT_FAIL) + && !manipulationResults.contains(ResponseTypes.Result.RESULT_NOT_IMPLEMENTED) + && manipulationResults.contains(ResponseTypes.Result.RESULT_SUCCESS); + } + + private static boolean descriptionUpdateWithParentChildRelationshipManipulation( + final Injector injector, final Logger log) { + final Pair descriptorHandles = findFirstDescriptorHandleWithChildren(injector, log); + if (descriptorHandles == null) { + log.error("Could not trigger a descriptor update with parent child relationship as there are no " + + "descriptors with children in the mdib."); + return false; + } + final String parentDescriptorHandle = descriptorHandles.getLeft(); + final String childDescriptorHandle = descriptorHandles.getRight(); + final var manipulations = injector.getInstance(Manipulations.class); + + final ResponseTypes.Result manipulationResult = + manipulations.triggerDescriptorUpdate(List.of(childDescriptorHandle, parentDescriptorHandle)); + + return ResponseTypes.Result.RESULT_SUCCESS.equals(manipulationResult); + } + + private static Pair findFirstDescriptorHandleWithChildren( + final Injector injector, final Logger log) { + final var testClient = injector.getInstance(TestClient.class); + + final var remoteDevice = testClient.getSdcRemoteDevice(); + if (remoteDevice == null) { + log.error("remote device could not be accessed, likely due to a disconnect"); + return null; + } + final var mdibAccess = remoteDevice.getMdibAccess(); + + final Collection entities = mdibAccess.findEntitiesByType(AbstractDescriptor.class); + for (MdibEntity entity : entities) { + if (entity.getDescriptor() instanceof MdsDescriptor) { + continue; // we are not interested in MdsDescriptors as many Devices do not support modifying them. + } + final List children = entity.getChildren(); + if (!children.isEmpty()) { + return Pair.of(entity.getHandle(), children.get(0)); + } + } + return null; + } + /** * Associates two new patients for every available PatientContextDescriptor present in the provider mdib. */ @@ -1607,69 +1719,37 @@ public RemoveAndReinsertDescriptorManipulation() { * @return true if successful, false otherwise */ static boolean manipulation(final Injector injector) { - final var manipulations = injector.getInstance(Manipulations.class); - final var testClient = injector.getInstance(TestClient.class); - final var testRunObserver = injector.getInstance(TestRunObserver.class); - - final MdibAccess mdibAccess; - final SdcRemoteDevice remoteDevice; - - remoteDevice = testClient.getSdcRemoteDevice(); - if (remoteDevice == null) { - testRunObserver.invalidateTestRun("Remote device could not be accessed, likely due to a disconnect"); - return false; - } - - mdibAccess = remoteDevice.getMdibAccess(); - - final var modifiableDescriptors = manipulations.getRemovableDescriptorsOfClass(); - if (modifiableDescriptors.isEmpty()) { - LOG.info("No modifiable descriptors available for manipulation"); - return false; - } - - final var manipulationResults = new HashSet(); - for (String handle : modifiableDescriptors) { - // determine if descriptor is currently present - var descriptorEntity = mdibAccess.getEntity(handle); - LOG.debug("Descriptor {} presence: {}", handle, descriptorEntity.isPresent()); - - // if the descriptor is not present, insert it first - if (descriptorEntity.isEmpty()) { - manipulationResults.add(manipulations.insertDescriptor(handle)); - descriptorEntity = mdibAccess.getEntity(handle); - if (descriptorEntity.isEmpty()) { - manipulationResults.add(ResponseTypes.Result.RESULT_FAIL); - } - LOG.debug("Descriptor {} presence: {}", handle, descriptorEntity.isPresent()); - } + return removeAndReinsertDescriptors(injector, LOG); + } + } - // remove descriptor - manipulationResults.add(manipulations.removeDescriptor(handle)); - descriptorEntity = mdibAccess.getEntity(handle); - if (descriptorEntity.isPresent()) { - manipulationResults.add(ResponseTypes.Result.RESULT_FAIL); - } - LOG.debug("Descriptor {} presence: {}", handle, descriptorEntity.isPresent()); + /** + * Precondition which triggers all possible description changes with parent-child relationships. + */ + public static class DescriptionModificationAllWithParentChildRelationshipPrecondition + extends ManipulationPrecondition { - // reinsert descriptor - manipulationResults.add(manipulations.insertDescriptor(handle)); - descriptorEntity = mdibAccess.getEntity(handle); - if (descriptorEntity.isEmpty()) { - manipulationResults.add(ResponseTypes.Result.RESULT_FAIL); - } - LOG.debug("Descriptor {} presence: {}", handle, descriptorEntity.isPresent()); + private static final Logger LOG = + LogManager.getLogger(DescriptionModificationAllWithParentChildRelationshipPrecondition.class); - if (manipulationResults.contains(ResponseTypes.Result.RESULT_FAIL)) { - testRunObserver.invalidateTestRun(String.format( - "Could not successfully modify descriptor %s, stopping the precondition", handle)); - break; - } - } + /** + * Creates a description modification all with parent child relationship precondition check. + */ + public DescriptionModificationAllWithParentChildRelationshipPrecondition() { + super(DescriptionModificationAllWithParentChildRelationshipPrecondition::manipulation); + } - return !manipulationResults.contains(ResponseTypes.Result.RESULT_FAIL) - && !manipulationResults.contains(ResponseTypes.Result.RESULT_NOT_IMPLEMENTED) - && manipulationResults.contains(ResponseTypes.Result.RESULT_SUCCESS); + /** + * Performs the removal, reinsertion and update of all modifiable descriptors in the mdib to trigger reports. + * + * @param injector to analyze mdib on + * @return true if successful, false otherwise + * @throws PreconditionException on errors + */ + static boolean manipulation(final Injector injector) { + final boolean result1 = removeAndReinsertDescriptors(injector, LOG); + final boolean result2 = descriptionUpdateWithParentChildRelationshipManipulation(injector, LOG); + return result1 || result2; } } } diff --git a/sdccc/src/main/java/com/draeger/medical/sdccc/tests/biceps/invariant/InvariantMessageModelAnnexTest.java b/sdccc/src/main/java/com/draeger/medical/sdccc/tests/biceps/invariant/InvariantMessageModelAnnexTest.java index 6416d3b1..f0a1edb2 100644 --- a/sdccc/src/main/java/com/draeger/medical/sdccc/tests/biceps/invariant/InvariantMessageModelAnnexTest.java +++ b/sdccc/src/main/java/com/draeger/medical/sdccc/tests/biceps/invariant/InvariantMessageModelAnnexTest.java @@ -15,6 +15,7 @@ import com.draeger.medical.sdccc.configuration.EnabledTestConfig; import com.draeger.medical.sdccc.manipulation.precondition.impl.ConditionalPreconditions; +import com.draeger.medical.sdccc.manipulation.precondition.impl.ManipulationPreconditions; import com.draeger.medical.sdccc.messages.MessageStorage; import com.draeger.medical.sdccc.messages.mapping.MessageContent; import com.draeger.medical.sdccc.sdcri.testclient.TestClient; @@ -332,7 +333,9 @@ void testRequirementR5024() throws NoTestData, IOException { @TestDescription("For every DescriptionModificationReport received from the DUT, and for all parent-child" + " relationships between the elements contained in the report, checks that the reportPart containing" + " the parent comes before the reportPart containing the child.") - @RequirePrecondition(simplePreconditions = ConditionalPreconditions.DescriptionChangedPrecondition.class) + @RequirePrecondition( + manipulationPreconditions = + ManipulationPreconditions.DescriptionModificationAllWithParentChildRelationshipPrecondition.class) void testRequirementR5025() throws NoTestData, IOException { try (final var messages = messageStorage.getInboundMessagesByBodyType(Constants.MSG_DESCRIPTION_MODIFICATION_REPORT)) { @@ -353,7 +356,10 @@ void testRequirementR5025() throws NoTestData, IOException { assertTestData( descriptorsSeen.get(), "No DescriptionModificationReports with Parent-Child Relationships between Descriptors " - + " seen during test run, test failed."); + + " seen during test run, test failed." + + " Please make sure that the device either supports descriptor updates or that the list" + + " of removable descriptors returned by the getRemovableDescriptors() manipulation" + + " includes at least one descriptor that has child descriptors."); } } diff --git a/sdccc/src/main/java/com/draeger/medical/sdccc/tests/biceps/invariant/InvariantParticipantModelVersioningTest.java b/sdccc/src/main/java/com/draeger/medical/sdccc/tests/biceps/invariant/InvariantParticipantModelVersioningTest.java index a07d948b..0e8d37c5 100644 --- a/sdccc/src/main/java/com/draeger/medical/sdccc/tests/biceps/invariant/InvariantParticipantModelVersioningTest.java +++ b/sdccc/src/main/java/com/draeger/medical/sdccc/tests/biceps/invariant/InvariantParticipantModelVersioningTest.java @@ -80,7 +80,8 @@ void setUp() { @TestDescription( "Starting from the initially retrieved mdib, applies every episodic report to the mdib and" + " verifies that descriptor versions are incremented by 1 whenever a child descriptor is added or deleted.") - @RequirePrecondition(simplePreconditions = {ConditionalPreconditions.DescriptionChangedPrecondition.class}) + @RequirePrecondition( + simplePreconditions = {ConditionalPreconditions.DescriptionModificationCrtOrDelPrecondition.class}) void testRequirementR0033() throws NoTestData, IOException { final var mdibHistorian = mdibHistorianFactory.createMdibHistorian( messageStorage, getInjector().getInstance(TestRunObserver.class)); diff --git a/sdccc/src/main/java/com/draeger/medical/sdccc/tests/util/ManipulationParameterUtil.java b/sdccc/src/main/java/com/draeger/medical/sdccc/tests/util/ManipulationParameterUtil.java index 1f653ba4..1fd762a8 100644 --- a/sdccc/src/main/java/com/draeger/medical/sdccc/tests/util/ManipulationParameterUtil.java +++ b/sdccc/src/main/java/com/draeger/medical/sdccc/tests/util/ManipulationParameterUtil.java @@ -48,6 +48,17 @@ public static ManipulationParameterData buildHandleManipulationParameterData(fin List.of(new ImmutablePair<>(Constants.MANIPULATION_PARAMETER_HANDLE, handle))); } + /** + * Build manipulation parameter data containing the handles that are needed for the manipulation. + * + * @param handles for which manipulation parameter data will be built. + * @return the manipulation parameter data + */ + public static ManipulationParameterData buildTriggerDescriptorUpdateParameterData(final List handles) { + return new ManipulationParameterData( + List.of(new ImmutablePair<>(Constants.MANIPULATION_PARAMETER_HANDLES, String.join(", ", handles)))); + } + /** * Build manipulation parameter data containing the location detail that is needed for the manipulation. * diff --git a/sdccc/src/main/java/com/draeger/medical/sdccc/util/Constants.java b/sdccc/src/main/java/com/draeger/medical/sdccc/util/Constants.java index c9308c7c..a29b84ac 100644 --- a/sdccc/src/main/java/com/draeger/medical/sdccc/util/Constants.java +++ b/sdccc/src/main/java/com/draeger/medical/sdccc/util/Constants.java @@ -222,6 +222,7 @@ public final class Constants { // Manipulation Data for Hibernation public static final String MANIPULATION_PARAMETER_HANDLE = "Handle"; + public static final String MANIPULATION_PARAMETER_HANDLES = "Handles"; public static final String MANIPULATION_PARAMETER_SEQUENCE_ID = "SequenceId"; public static final String MANIPULATION_PARAMETER_LOCATION_DETAIL = "LocationDetail"; public static final String MANIPULATION_PARAMETER_CONTEXT_ASSOCIATION = "ContextAssociation"; diff --git a/sdccc/src/test/java/com/draeger/medical/sdccc/manipulation/precondition/impl/ConditionalPreconditionsTest.java b/sdccc/src/test/java/com/draeger/medical/sdccc/manipulation/precondition/impl/ConditionalPreconditionsTest.java index 23662726..6cbab165 100644 --- a/sdccc/src/test/java/com/draeger/medical/sdccc/manipulation/precondition/impl/ConditionalPreconditionsTest.java +++ b/sdccc/src/test/java/com/draeger/medical/sdccc/manipulation/precondition/impl/ConditionalPreconditionsTest.java @@ -282,10 +282,11 @@ protected void configure() { */ @Test @DisplayName("DescriptionChangedPrecondition correctly checks for precondition") - public void testDescriptionModificationPreconditionCheck() + public void testDescriptionModificationCrtOrDelPreconditionCheck() throws PreconditionException, IOException, JAXBException { // no messages - assertFalse(ConditionalPreconditions.DescriptionChangedPrecondition.preconditionCheck(testInjector)); + assertFalse( + ConditionalPreconditions.DescriptionModificationCrtOrDelPrecondition.preconditionCheck(testInjector)); final var reportPart = messageBuilder.buildDescriptionModificationReportReportPart(); reportPart.setModificationType(DescriptionModificationType.CRT); @@ -297,7 +298,8 @@ public void testDescriptionModificationPreconditionCheck() messageStorageUtil.addInboundSecureHttpMessage(storage, message); - assertTrue(ConditionalPreconditions.DescriptionChangedPrecondition.preconditionCheck(testInjector)); + assertTrue( + ConditionalPreconditions.DescriptionModificationCrtOrDelPrecondition.preconditionCheck(testInjector)); } /** @@ -334,7 +336,7 @@ public void testDescriptionModificationManipulation() { } }); - assertTrue(ConditionalPreconditions.DescriptionChangedPrecondition.manipulation(testInjector)); + assertTrue(ConditionalPreconditions.DescriptionModificationCrtOrDelPrecondition.manipulation(testInjector)); final var insertCaptor = ArgumentCaptor.forClass(String.class); final var removeCaptor = ArgumentCaptor.forClass(String.class); @@ -640,7 +642,7 @@ public void testDescriptionModificationDelManipulation() { @DisplayName("DescriptionModificationPrecondition throws exception if no removable descriptors are present") void testDescriptionModificationModificationNoDescriptors() { // must fail without any removable descriptors - assertFalse(ConditionalPreconditions.DescriptionChangedPrecondition.manipulation(testInjector)); + assertFalse(ConditionalPreconditions.DescriptionModificationCrtOrDelPrecondition.manipulation(testInjector)); reset(mockManipulations); } diff --git a/sdccc/src/test/java/com/draeger/medical/sdccc/manipulation/precondition/impl/ManipulationPreconditionsTest.java b/sdccc/src/test/java/com/draeger/medical/sdccc/manipulation/precondition/impl/ManipulationPreconditionsTest.java index 199b5142..1da74afb 100644 --- a/sdccc/src/test/java/com/draeger/medical/sdccc/manipulation/precondition/impl/ManipulationPreconditionsTest.java +++ b/sdccc/src/test/java/com/draeger/medical/sdccc/manipulation/precondition/impl/ManipulationPreconditionsTest.java @@ -13,6 +13,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; @@ -24,6 +25,8 @@ import com.draeger.medical.sdccc.manipulation.Manipulations; import com.draeger.medical.sdccc.sdcri.testclient.TestClient; +import com.draeger.medical.sdccc.sdcri.testclient.TestClientUtil; +import com.draeger.medical.sdccc.tests.InjectorTestBase; import com.draeger.medical.sdccc.tests.test_util.InjectorUtil; import com.draeger.medical.sdccc.util.MdibBuilder; import com.draeger.medical.sdccc.util.TestRunObserver; @@ -32,6 +35,7 @@ import com.google.inject.Injector; import java.io.IOException; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; @@ -47,6 +51,7 @@ import org.mockito.Answers; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; +import org.mockito.stubbing.Answer; import org.somda.sdc.biceps.common.MdibEntity; import org.somda.sdc.biceps.common.access.MdibAccess; import org.somda.sdc.biceps.common.access.MdibAccessObservable; @@ -95,7 +100,6 @@ public class ManipulationPreconditionsTest { private static final String ALERT_SYSTEM_CONTEXT_HANDLE2 = "alerthandle2"; private static final String ALERT_CONDITION_HANDLE = "alertconditionhandle"; private static final String ALERT_CONDITION_HANDLE2 = "alertconditionhandle2"; - private static final String ALERT_SIGNAL_HANDLE = "alertSignalHandle"; private static final String AUD_ALERT_SIGNAL_HANDLE = "audAlertSignalHandle"; private static final String OTH_ALERT_SIGNAL_HANDLE = "othAlertSignalHandle"; private static final String TAN_ALERT_SIGNAL_HANDLE = "tanAlertSignalHandle"; @@ -188,7 +192,11 @@ void setUp() throws IOException { mockMdibAccess = mock(MdibAccess.class, Answers.RETURNS_DEEP_STUBS); mockTestClient = mock(TestClient.class, Answers.RETURNS_DEEP_STUBS); + final var clientInjector = TestClientUtil.createClientInjector(); when(mockTestClient.getSdcRemoteDevice()).thenReturn(mockDevice); + when(mockTestClient.isClientRunning()).thenReturn(true); + when(mockTestClient.getSdcRemoteDevice()).thenReturn(mockDevice); + when(mockTestClient.getInjector()).thenReturn(clientInjector); injector = InjectorUtil.setupInjector(new AbstractModule() { @Override @@ -199,6 +207,7 @@ protected void configure() { } }); + InjectorTestBase.setInjector(injector); testRunObserver = injector.getInstance(TestRunObserver.class); } @@ -1897,4 +1906,279 @@ void testSystemSignalActivationSomeFail() { assertFalse(ManipulationPreconditions.SystemSignalActivationManipulation.manipulation(injector)); } + + @Test + @DisplayName("DescriptionModificationAllWithParentChildRelationshipPrecondition correctly calls manipulation") + void testDescriptionModificationAllWithParentChildRelationshipPreconditionManipulation() { + + final var descriptor1Handle = "superHandle"; + final var descriptor2Handle = "handle;Süper;"; + final var parentDescriptorHandle = "parentHandle"; + final var childDescriptorHandle = "childHandle"; + + final var presenceMap = new HashMap<>(Map.of( + descriptor1Handle, false, + descriptor2Handle, true)); + + when(mockManipulations.getRemovableDescriptorsOfClass()) + .thenReturn(List.of(descriptor1Handle, descriptor2Handle)); + + when(mockManipulations.insertDescriptor(anyString())).thenAnswer((Answer) invocation -> { + presenceMap.put(invocation.getArgument(0), true); + return ResponseTypes.Result.RESULT_SUCCESS; + }); + when(mockManipulations.removeDescriptor(anyString())).thenAnswer((Answer) invocation -> { + presenceMap.put(invocation.getArgument(0), false); + return ResponseTypes.Result.RESULT_SUCCESS; + }); + when(mockManipulations.triggerDescriptorUpdate(anyList())).thenReturn(ResponseTypes.Result.RESULT_SUCCESS); + final MdibEntity mockEntityB2 = mock(MdibEntity.class); + when(mockEntityB2.getHandle()).thenReturn(parentDescriptorHandle); + when(mockEntityB2.getChildren()).thenReturn(List.of(childDescriptorHandle)); + when(mockTestClient.getSdcRemoteDevice().getMdibAccess().findEntitiesByType(any())) + .thenReturn(List.of(mockEntityB2)); + final MdibEntity mockEntityB3 = mock(MdibEntity.class); + when(mockTestClient.getSdcRemoteDevice().getMdibAccess().getEntity(any())) + .thenAnswer(args -> { + if (presenceMap.get((String) args.getArgument(0))) { + return Optional.of(mockEntityB3); + } else { + return Optional.empty(); + } + }); + + assertTrue( + ManipulationPreconditions.DescriptionModificationAllWithParentChildRelationshipPrecondition + .manipulation(injector)); + + final var insertCaptor = ArgumentCaptor.forClass(String.class); + final var removeCaptor = ArgumentCaptor.forClass(String.class); + final var handleCaptor = ArgumentCaptor.forClass(List.class); + verify(mockManipulations, times(1)).getRemovableDescriptorsOfClass(); + verify(mockManipulations, times(3)).insertDescriptor(insertCaptor.capture()); + verify(mockManipulations, times(2)).removeDescriptor(removeCaptor.capture()); + verify(mockManipulations, times(1)).triggerDescriptorUpdate(handleCaptor.capture()); + + assertEquals(2, handleCaptor.getAllValues().get(0).size()); + assertEquals(childDescriptorHandle, handleCaptor.getAllValues().get(0).get(0)); + assertEquals(parentDescriptorHandle, handleCaptor.getAllValues().get(0).get(1)); + + assertEquals( + 2, + insertCaptor.getAllValues().stream() + .filter(descriptor1Handle::equals) + .count()); + assertEquals( + 1, + insertCaptor.getAllValues().stream() + .filter(descriptor2Handle::equals) + .count()); + + assertEquals( + 1, + removeCaptor.getAllValues().stream() + .filter(descriptor1Handle::equals) + .count()); + assertEquals( + 1, + removeCaptor.getAllValues().stream() + .filter(descriptor2Handle::equals) + .count()); + } + + @Test + @DisplayName( + "DescriptionModificationAllWithParentChildRelationshipPrecondition when GetRemovableDescriptors failed.") + void + testDescriptionModificationAllWithParentChildRelationshipPreconditionManipulationGetRemovableDescriptorsFailed() { + + final var descriptor1Handle = "superHandle"; + final var descriptor2Handle = "handle;Süper;"; + final var parentDescriptorHandle = "parentHandle"; + final var childDescriptorHandle = "childHandle"; + + final var presenceMap = new HashMap<>(Map.of( + descriptor1Handle, false, + descriptor2Handle, true)); + + when(mockManipulations.getRemovableDescriptorsOfClass()) + .thenReturn(List.of()); // When the manipulation is NOT_SUPPORTED, an empty list is returned. + + when(mockManipulations.insertDescriptor(anyString())).thenAnswer((Answer) invocation -> { + presenceMap.put(invocation.getArgument(0), true); + return ResponseTypes.Result.RESULT_SUCCESS; + }); + when(mockManipulations.removeDescriptor(anyString())).thenAnswer((Answer) invocation -> { + presenceMap.put(invocation.getArgument(0), false); + return ResponseTypes.Result.RESULT_SUCCESS; + }); + when(mockManipulations.triggerDescriptorUpdate(anyList())).thenReturn(ResponseTypes.Result.RESULT_SUCCESS); + final MdibEntity mockEntityB = mock(MdibEntity.class); + when(mockEntityB.getHandle()).thenReturn(parentDescriptorHandle); + when(mockEntityB.getChildren()).thenReturn(List.of(childDescriptorHandle)); + when(mockTestClient.getSdcRemoteDevice().getMdibAccess().findEntitiesByType(any())) + .thenReturn(List.of(mockEntityB)); + final MdibEntity mockEntityB2 = mock(MdibEntity.class); + when(mockTestClient.getSdcRemoteDevice().getMdibAccess().getEntity(any())) + .thenAnswer(args -> { + if (presenceMap.get((String) args.getArgument(0))) { + return Optional.of(mockEntityB2); + } else { + return Optional.empty(); + } + }); + + assertTrue( + ManipulationPreconditions.DescriptionModificationAllWithParentChildRelationshipPrecondition + .manipulation(injector)); + + final var handleCaptor = ArgumentCaptor.forClass(List.class); + verify(mockManipulations, times(1)).getRemovableDescriptorsOfClass(); + verify(mockManipulations, times(0)).insertDescriptor(any()); + verify(mockManipulations, times(0)).removeDescriptor(any()); + verify(mockManipulations, times(1)).triggerDescriptorUpdate(handleCaptor.capture()); + + assertEquals(2, handleCaptor.getAllValues().get(0).size()); + assertEquals(childDescriptorHandle, handleCaptor.getAllValues().get(0).get(0)); + assertEquals(parentDescriptorHandle, handleCaptor.getAllValues().get(0).get(1)); + } + + @Test + @DisplayName( + "DescriptionModificationAllWithParentChildRelationshipPrecondition when TriggerDescriptorUpdate failed") + void testDescriptionModificationAllWithParentChildRelationshipPreconditionManipulationFailed2() { + + final var descriptor1Handle = "superHandle"; + final var descriptor2Handle = "handle;Süper;"; + final var parentDescriptorHandle = "parentHandle"; + final var childDescriptorHandle = "childHandle"; + + final var presenceMap = new HashMap<>(Map.of( + descriptor1Handle, false, + descriptor2Handle, true)); + + when(mockManipulations.getRemovableDescriptorsOfClass()) + .thenReturn(List.of(descriptor1Handle, descriptor2Handle)); + + when(mockManipulations.insertDescriptor(anyString())).thenAnswer((Answer) invocation -> { + presenceMap.put(invocation.getArgument(0), true); + return ResponseTypes.Result.RESULT_SUCCESS; + }); + when(mockManipulations.removeDescriptor(anyString())).thenAnswer((Answer) invocation -> { + presenceMap.put(invocation.getArgument(0), false); + return ResponseTypes.Result.RESULT_SUCCESS; + }); + when(mockManipulations.triggerDescriptorUpdate(anyList())).thenReturn(ResponseTypes.Result.RESULT_FAIL); + final MdibEntity mockEntityB = mock(MdibEntity.class); + when(mockEntityB.getHandle()).thenReturn(parentDescriptorHandle); + when(mockEntityB.getChildren()).thenReturn(List.of(childDescriptorHandle)); + when(mockTestClient.getSdcRemoteDevice().getMdibAccess().findEntitiesByType(any())) + .thenReturn(List.of(mockEntityB)); + final MdibEntity mockEntityB2 = mock(MdibEntity.class); + when(mockTestClient.getSdcRemoteDevice().getMdibAccess().getEntity(any())) + .thenAnswer(args -> { + if (presenceMap.get((String) args.getArgument(0))) { + return Optional.of(mockEntityB2); + } else { + return Optional.empty(); + } + }); + + assertTrue( + ManipulationPreconditions.DescriptionModificationAllWithParentChildRelationshipPrecondition + .manipulation(injector)); + + final var insertCaptor = ArgumentCaptor.forClass(String.class); + final var removeCaptor = ArgumentCaptor.forClass(String.class); + final var handleCaptor = ArgumentCaptor.forClass(List.class); + verify(mockManipulations, times(1)).getRemovableDescriptorsOfClass(); + verify(mockManipulations, times(3)).insertDescriptor(insertCaptor.capture()); + verify(mockManipulations, times(2)).removeDescriptor(removeCaptor.capture()); + verify(mockManipulations, times(1)).triggerDescriptorUpdate(handleCaptor.capture()); + + assertEquals(2, handleCaptor.getAllValues().get(0).size()); + assertEquals(childDescriptorHandle, handleCaptor.getAllValues().get(0).get(0)); + assertEquals(parentDescriptorHandle, handleCaptor.getAllValues().get(0).get(1)); + + assertEquals( + 2, + insertCaptor.getAllValues().stream() + .filter(descriptor1Handle::equals) + .count()); + assertEquals( + 1, + insertCaptor.getAllValues().stream() + .filter(descriptor2Handle::equals) + .count()); + + assertEquals( + 1, + removeCaptor.getAllValues().stream() + .filter(descriptor1Handle::equals) + .count()); + assertEquals( + 1, + removeCaptor.getAllValues().stream() + .filter(descriptor2Handle::equals) + .count()); + } + + @Test + @DisplayName("DescriptionModificationAllWithParentChildRelationshipPrecondition when both GetRemovableDescriptors " + + "and TriggerDescriptorUpdate fail") + void testDescriptionModificationAllWithParentChildRelationshipPreconditionManipulationFailed3() { + + final var descriptor1Handle = "superHandle"; + final var descriptor2Handle = "handle;Süper;"; + final var parentDescriptorHandle = "parentHandle"; + final var childDescriptorHandle = "childHandle"; + + final var presenceMap = new HashMap<>(Map.of( + descriptor1Handle, false, + descriptor2Handle, true)); + + when(mockManipulations.getRemovableDescriptorsOfClass()) + .thenReturn(List.of()); // When the manipulation returns RESULT_NOT_SUPPORTED, then an empty list + // is returned + + when(mockManipulations.insertDescriptor(anyString())).thenAnswer((Answer) invocation -> { + presenceMap.put(invocation.getArgument(0), true); + return ResponseTypes.Result.RESULT_SUCCESS; + }); + when(mockManipulations.removeDescriptor(anyString())).thenAnswer((Answer) invocation -> { + presenceMap.put(invocation.getArgument(0), false); + return ResponseTypes.Result.RESULT_SUCCESS; + }); + when(mockManipulations.triggerDescriptorUpdate(anyList())).thenReturn(ResponseTypes.Result.RESULT_FAIL); + final MdibEntity mockEntityB = mock(MdibEntity.class); + when(mockEntityB.getHandle()).thenReturn(parentDescriptorHandle); + when(mockEntityB.getChildren()).thenReturn(List.of(childDescriptorHandle)); + when(mockTestClient.getSdcRemoteDevice().getMdibAccess().findEntitiesByType(any())) + .thenReturn(List.of(mockEntityB)); + final MdibEntity mockEntityB2 = mock(MdibEntity.class); + when(mockTestClient.getSdcRemoteDevice().getMdibAccess().getEntity(any())) + .thenAnswer(args -> { + if (presenceMap.get((String) args.getArgument(0))) { + return Optional.of(mockEntityB2); + } else { + return Optional.empty(); + } + }); + + assertFalse( + ManipulationPreconditions.DescriptionModificationAllWithParentChildRelationshipPrecondition + .manipulation(injector)); + + final var insertCaptor = ArgumentCaptor.forClass(String.class); + final var removeCaptor = ArgumentCaptor.forClass(String.class); + final var handleCaptor = ArgumentCaptor.forClass(List.class); + verify(mockManipulations, times(1)).getRemovableDescriptorsOfClass(); + verify(mockManipulations, times(0)).insertDescriptor(insertCaptor.capture()); + verify(mockManipulations, times(0)).removeDescriptor(removeCaptor.capture()); + verify(mockManipulations, times(1)).triggerDescriptorUpdate(handleCaptor.capture()); + + assertEquals(2, handleCaptor.getAllValues().get(0).size()); + assertEquals(childDescriptorHandle, handleCaptor.getAllValues().get(0).get(0)); + assertEquals(parentDescriptorHandle, handleCaptor.getAllValues().get(0).get(1)); + } }