From 0082d83a96e507f1cfc5a7420cd5848dcd3e5e94 Mon Sep 17 00:00:00 2001 From: Tristan Chuine Date: Wed, 6 Aug 2025 23:27:52 +0200 Subject: [PATCH 01/11] Add tests for `ElementUtils.operationalLimitsGroupToMapDataCurrentLimits` --- .../network/map/utils/ElementUtilsTest.java | 155 ++++++++++++++++++ .../network/map/utils/MockUtils.java | 43 +++++ 2 files changed, 198 insertions(+) create mode 100644 src/test/java/org/gridsuite/network/map/utils/ElementUtilsTest.java create mode 100644 src/test/java/org/gridsuite/network/map/utils/MockUtils.java diff --git a/src/test/java/org/gridsuite/network/map/utils/ElementUtilsTest.java b/src/test/java/org/gridsuite/network/map/utils/ElementUtilsTest.java new file mode 100644 index 00000000..e86d2b83 --- /dev/null +++ b/src/test/java/org/gridsuite/network/map/utils/ElementUtilsTest.java @@ -0,0 +1,155 @@ +package org.gridsuite.network.map.utils; + +import com.powsybl.iidm.network.OperationalLimitsGroup; +import org.assertj.core.api.WithAssertions; +import org.assertj.core.api.junit.jupiter.SoftAssertionsExtension; +import org.gridsuite.network.map.dto.common.CurrentLimitsData; +import org.gridsuite.network.map.dto.common.TemporaryLimitData; +import org.gridsuite.network.map.dto.utils.ElementUtils; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +@ExtendWith({ SoftAssertionsExtension.class, MockitoExtension.class }) +class ElementUtilsTest implements WithAssertions { + /** Tests for {@link ElementUtils#operationalLimitsGroupToMapDataCurrentLimits(OperationalLimitsGroup)} */ + @Nested + @DisplayName("fn operationalLimitsGroupToMapDataCurrentLimits(…)") + class OperationalLimitsGroupToMapDataCurrentLimits { + private Collection mocks = new ArrayList<>(); + + @AfterEach + void setDown() { + if (!mocks.isEmpty()) { + Mockito.verifyNoMoreInteractions(mocks.toArray(Object[]::new)); + mocks.clear(); + } + } + + /* All cases possibles: + * null -> null + * operationalLimitsGroup(currentLimits=empty, *=any) -> null + * operationalLimitsGroup(currentLimits={PermanentLimit=NaN, TemporaryLimits=null, *=any}, *=any) -> null + * operationalLimitsGroup(currentLimits={PermanentLimit=NaN, TemporaryLimits=[], *=any}, *=any) -> null + * operationalLimitsGroup(currentLimits={permanentLimit=NaN, temporaryLimits=[any], *=any}, *=any) -> (id=*, applicability=null, permanentLimit=null, temporaryLimits=[any]) + * operationalLimitsGroup(currentLimits={PermanentLimit=_non_NaN_, TemporaryLimits=null, *=any}, *=any) -> (id=*, applicability=null, permanentLimit=*, temporaryLimits=null) + * operationalLimitsGroup(currentLimits={PermanentLimit=_non_NaN_, TemporaryLimits=[], *=any}, *=any) -> (id=*, applicability=null, permanentLimit=*, temporaryLimits=null) + * operationalLimitsGroup(currentLimits={PermanentLimit=_non_NaN_, TemporaryLimits=[any], *=any}, *=any) -> (id=*, applicability=null, permanentLimit=*, temporaryLimits=[any]) + */ + + @Test + void nullInput() { + assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(null)).isNull(); + } + + @Test + void emptyCurrentLimits() { + final var mock = MockUtils.mockOperationalLimitsGroup("id", null); + mocks.add(mock); + assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(mock)).isNull(); + Mockito.verify(mock).getCurrentLimits(); + } + + @Test + void nanPermLimitAndNullTempLimit() { + final var clMock = MockUtils.mockCurrentLimits(Double.NaN, null); + mocks.add(clMock); + final var olgMock = MockUtils.mockOperationalLimitsGroup("id", clMock); + mocks.add(olgMock); + assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(olgMock)).isNull(); + Mockito.verify(olgMock, Mockito.times(2)).getCurrentLimits(); + Mockito.verify(olgMock).getId(); + Mockito.verify(clMock).getPermanentLimit(); + Mockito.verify(clMock).getTemporaryLimits(); + } + + @Test + void nanPermLimitAndEmptyTempLimit() { + final var clMock = MockUtils.mockCurrentLimits(Double.NaN, List.of()); + mocks.add(clMock); + final var olgMock = MockUtils.mockOperationalLimitsGroup("id", clMock); + mocks.add(olgMock); + assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(olgMock)).isNull(); + Mockito.verify(olgMock, Mockito.times(2)).getCurrentLimits(); + Mockito.verify(olgMock).getId(); + Mockito.verify(clMock).getPermanentLimit(); + Mockito.verify(clMock).getTemporaryLimits(); + } + + @Test + void nanPermLimitAndNonEmptyTempLimit() { + final var tlMock = MockUtils.mockTemporaryLimits(123, "testLimit", 456.789); + mocks.add(tlMock); + final var clMock = MockUtils.mockCurrentLimits(Double.NaN, List.of(tlMock)); + mocks.add(clMock); + final var olgMock = MockUtils.mockOperationalLimitsGroup("my id", clMock); + mocks.add(olgMock); + assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(olgMock)).isEqualTo(CurrentLimitsData.builder() + .id("my id").applicability(null).permanentLimit(null).temporaryLimits(List.of(TemporaryLimitData.builder() + .acceptableDuration(123).name("testLimit").value(456.789).build())).build()); + Mockito.verify(olgMock, Mockito.times(2)).getCurrentLimits(); + Mockito.verify(olgMock).getId(); + Mockito.verify(clMock).getPermanentLimit(); + Mockito.verify(clMock, Mockito.times(2)).getTemporaryLimits(); + Mockito.verify(tlMock, Mockito.times(2)).getAcceptableDuration(); + Mockito.verify(tlMock).getName(); + Mockito.verify(tlMock, Mockito.times(2)).getValue(); + } + + @Test + void nonNanPermLimitAndNullTempLimit() { + final var clMock = MockUtils.mockCurrentLimits(0.123, null); + mocks.add(clMock); + final var olgMock = MockUtils.mockOperationalLimitsGroup("my id", clMock); + mocks.add(olgMock); + assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(olgMock)).isEqualTo(CurrentLimitsData.builder() + .id("my id").applicability(null).permanentLimit(0.123).temporaryLimits(null).build()); + Mockito.verify(olgMock, Mockito.times(2)).getCurrentLimits(); + Mockito.verify(olgMock).getId(); + Mockito.verify(clMock, Mockito.times(2)).getPermanentLimit(); + Mockito.verify(clMock).getTemporaryLimits(); + } + + @Test + void nonNanPermLimitAndEmptyTempLimit() { + final var clMock = MockUtils.mockCurrentLimits(0.123, List.of()); + mocks.add(clMock); + final var olgMock = MockUtils.mockOperationalLimitsGroup("my id", clMock); + mocks.add(olgMock); + assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(olgMock)).isEqualTo(CurrentLimitsData.builder() + .id("my id").applicability(null).permanentLimit(0.123).temporaryLimits(null).build()); + Mockito.verify(olgMock, Mockito.times(2)).getCurrentLimits(); + Mockito.verify(olgMock).getId(); + Mockito.verify(clMock, Mockito.times(2)).getPermanentLimit(); + Mockito.verify(clMock).getTemporaryLimits(); + } + + @Test + void nonNanPermLimitAndNonEmptyTempLimit() { + final var tlMock = MockUtils.mockTemporaryLimits(123, "testLimit", 456.789); + mocks.add(tlMock); + final var clMock = MockUtils.mockCurrentLimits(0.0, List.of(tlMock)); + mocks.add(clMock); + final var olgMock = MockUtils.mockOperationalLimitsGroup("my id", clMock); + mocks.add(olgMock); + assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(olgMock)).isEqualTo(CurrentLimitsData.builder() + .id("my id").applicability(null).permanentLimit(0.0).temporaryLimits(List.of(TemporaryLimitData.builder() + .acceptableDuration(123).name("testLimit").value(456.789).build())).build()); + Mockito.verify(olgMock, Mockito.times(2)).getCurrentLimits(); + Mockito.verify(olgMock).getId(); + Mockito.verify(clMock, Mockito.times(2)).getPermanentLimit(); + Mockito.verify(clMock, Mockito.times(2)).getTemporaryLimits(); + Mockito.verify(tlMock, Mockito.times(2)).getAcceptableDuration(); + Mockito.verify(tlMock).getName(); + Mockito.verify(tlMock, Mockito.times(2)).getValue(); + } + } +} diff --git a/src/test/java/org/gridsuite/network/map/utils/MockUtils.java b/src/test/java/org/gridsuite/network/map/utils/MockUtils.java new file mode 100644 index 00000000..77a4e276 --- /dev/null +++ b/src/test/java/org/gridsuite/network/map/utils/MockUtils.java @@ -0,0 +1,43 @@ +package org.gridsuite.network.map.utils; + +import com.powsybl.iidm.network.CurrentLimits; +import com.powsybl.iidm.network.LoadingLimits.TemporaryLimit; +import com.powsybl.iidm.network.OperationalLimitsGroup; +import org.mockito.Mockito; +import org.mockito.quality.Strictness; + +import java.util.List; +import java.util.Optional; + +public final class MockUtils { + private MockUtils() { + throw new InstantiationError("Utility Class cannot be instantiated."); + } + + public static TemporaryLimit mockTemporaryLimits(final int acceptableDuration, final String name, final double value) { + TemporaryLimit mock = Mockito.mock(TemporaryLimit.class); + Mockito.when(mock.getAcceptableDuration()).thenReturn(acceptableDuration); + Mockito.when(mock.getName()).thenReturn(name); + Mockito.when(mock.getValue()).thenReturn(value); + return mock; + } + + public static CurrentLimits mockCurrentLimits(double permanentLimit, List temporaryLimits) { + CurrentLimits mock = Mockito.mock(CurrentLimits.class); + //Mockito.when(mock.getLimitType()).thenCallRealMethod(); + Mockito.when(mock.getPermanentLimit()).thenReturn(permanentLimit); + Mockito.when(mock.getTemporaryLimits()).thenReturn(temporaryLimits); + return mock; + } + + public static OperationalLimitsGroup mockOperationalLimitsGroup(final String id, double permanentLimit, List temporaryLimits) { + return mockOperationalLimitsGroup(id, mockCurrentLimits(permanentLimit, temporaryLimits)); + } + + public static OperationalLimitsGroup mockOperationalLimitsGroup(final String id, final CurrentLimits cl) { + OperationalLimitsGroup mock = Mockito.mock(OperationalLimitsGroup.class, Mockito.withSettings().strictness(Strictness.LENIENT)); + Mockito.when(mock.getId()).thenReturn(id); + Mockito.when(mock.getCurrentLimits()).thenReturn(Optional.ofNullable(cl)); + return mock; + } +} From 03d22800d591561c49562cfa5d41bd63e641e256 Mon Sep 17 00:00:00 2001 From: Tristan Chuine Date: Wed, 6 Aug 2025 23:35:30 +0200 Subject: [PATCH 02/11] Add test for `ElementUtils.mergeCurrentLimits` --- .../network/map/utils/ElementUtilsTest.java | 141 +++++++++++++++++- 1 file changed, 135 insertions(+), 6 deletions(-) diff --git a/src/test/java/org/gridsuite/network/map/utils/ElementUtilsTest.java b/src/test/java/org/gridsuite/network/map/utils/ElementUtilsTest.java index e86d2b83..b7fddbaa 100644 --- a/src/test/java/org/gridsuite/network/map/utils/ElementUtilsTest.java +++ b/src/test/java/org/gridsuite/network/map/utils/ElementUtilsTest.java @@ -1,9 +1,11 @@ package org.gridsuite.network.map.utils; import com.powsybl.iidm.network.OperationalLimitsGroup; +import org.assertj.core.api.SoftAssertions; import org.assertj.core.api.WithAssertions; import org.assertj.core.api.junit.jupiter.SoftAssertionsExtension; import org.gridsuite.network.map.dto.common.CurrentLimitsData; +import org.gridsuite.network.map.dto.common.CurrentLimitsData.Applicability; import org.gridsuite.network.map.dto.common.TemporaryLimitData; import org.gridsuite.network.map.dto.utils.ElementUtils; import org.junit.jupiter.api.AfterEach; @@ -11,12 +13,18 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; +import java.util.stream.Stream; @ExtendWith({ SoftAssertionsExtension.class, MockitoExtension.class }) class ElementUtilsTest implements WithAssertions { @@ -93,8 +101,8 @@ void nanPermLimitAndNonEmptyTempLimit() { final var olgMock = MockUtils.mockOperationalLimitsGroup("my id", clMock); mocks.add(olgMock); assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(olgMock)).isEqualTo(CurrentLimitsData.builder() - .id("my id").applicability(null).permanentLimit(null).temporaryLimits(List.of(TemporaryLimitData.builder() - .acceptableDuration(123).name("testLimit").value(456.789).build())).build()); + .id("my id").applicability(null).permanentLimit(null).temporaryLimits(List.of(TemporaryLimitData.builder() + .acceptableDuration(123).name("testLimit").value(456.789).build())).build()); Mockito.verify(olgMock, Mockito.times(2)).getCurrentLimits(); Mockito.verify(olgMock).getId(); Mockito.verify(clMock).getPermanentLimit(); @@ -111,7 +119,7 @@ void nonNanPermLimitAndNullTempLimit() { final var olgMock = MockUtils.mockOperationalLimitsGroup("my id", clMock); mocks.add(olgMock); assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(olgMock)).isEqualTo(CurrentLimitsData.builder() - .id("my id").applicability(null).permanentLimit(0.123).temporaryLimits(null).build()); + .id("my id").applicability(null).permanentLimit(0.123).temporaryLimits(null).build()); Mockito.verify(olgMock, Mockito.times(2)).getCurrentLimits(); Mockito.verify(olgMock).getId(); Mockito.verify(clMock, Mockito.times(2)).getPermanentLimit(); @@ -125,7 +133,7 @@ void nonNanPermLimitAndEmptyTempLimit() { final var olgMock = MockUtils.mockOperationalLimitsGroup("my id", clMock); mocks.add(olgMock); assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(olgMock)).isEqualTo(CurrentLimitsData.builder() - .id("my id").applicability(null).permanentLimit(0.123).temporaryLimits(null).build()); + .id("my id").applicability(null).permanentLimit(0.123).temporaryLimits(null).build()); Mockito.verify(olgMock, Mockito.times(2)).getCurrentLimits(); Mockito.verify(olgMock).getId(); Mockito.verify(clMock, Mockito.times(2)).getPermanentLimit(); @@ -141,8 +149,8 @@ void nonNanPermLimitAndNonEmptyTempLimit() { final var olgMock = MockUtils.mockOperationalLimitsGroup("my id", clMock); mocks.add(olgMock); assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(olgMock)).isEqualTo(CurrentLimitsData.builder() - .id("my id").applicability(null).permanentLimit(0.0).temporaryLimits(List.of(TemporaryLimitData.builder() - .acceptableDuration(123).name("testLimit").value(456.789).build())).build()); + .id("my id").applicability(null).permanentLimit(0.0).temporaryLimits(List.of(TemporaryLimitData.builder() + .acceptableDuration(123).name("testLimit").value(456.789).build())).build()); Mockito.verify(olgMock, Mockito.times(2)).getCurrentLimits(); Mockito.verify(olgMock).getId(); Mockito.verify(clMock, Mockito.times(2)).getPermanentLimit(); @@ -152,4 +160,125 @@ void nonNanPermLimitAndNonEmptyTempLimit() { Mockito.verify(tlMock, Mockito.times(2)).getValue(); } } + + /** Tests for {@link ElementUtils#mergeCurrentLimits(Collection, Collection, Consumer)} */ + @Nested + @DisplayName("fn mergeCurrentLimits(…, …, …)") + class MergeCurrentLimitsTest { + @ParameterizedTest(name = ParameterizedTest.INDEX_PLACEHOLDER) + @MethodSource("mergeCurrentLimitsTestData") + void shouldNotThrow( + final Collection olg1, + final Collection olg2, + final List expected) { + AtomicReference> results = new AtomicReference<>(); + ElementUtils.mergeCurrentLimits(olg1, olg2, results::set); + assertThat(results.get()).as("Result").isEqualTo(expected); + } + + private static Stream mergeCurrentLimitsTestData() { + return Stream.of( + Arguments.of(List.of( + MockUtils.mockOperationalLimitsGroup("group1", 220.0, List.of(MockUtils.mockTemporaryLimits(100, "temporary1", 50.0), MockUtils.mockTemporaryLimits(150, "temporary2", 70.0))) + ), List.of( + MockUtils.mockOperationalLimitsGroup("group1", 220.0, List.of(MockUtils.mockTemporaryLimits(100, "temporary1", 50.0), MockUtils.mockTemporaryLimits(150, "temporary2", 70.0))) + ), List.of( + CurrentLimitsData.builder().id("group1").permanentLimit(220.0).temporaryLimits(List.of( + TemporaryLimitData.builder().acceptableDuration(100).name("temporary1").value(50.0).build(), + TemporaryLimitData.builder().acceptableDuration(150).name("temporary2").value(70.0).build() + )).applicability(Applicability.EQUIPMENT).build() + )), + Arguments.of(List.of(), List.of( + MockUtils.mockOperationalLimitsGroup("group1", 220.0, List.of(MockUtils.mockTemporaryLimits(100, "temporary1", 50.0), MockUtils.mockTemporaryLimits(150, "temporary2", 70.0))) + ), List.of( + CurrentLimitsData.builder().id("group1").permanentLimit(220.0).temporaryLimits(List.of( + TemporaryLimitData.builder().acceptableDuration(100).name("temporary1").value(50.0).build(), + TemporaryLimitData.builder().acceptableDuration(150).name("temporary2").value(70.0).build() + )).applicability(Applicability.SIDE2).build() + )), + Arguments.of(List.of( + MockUtils.mockOperationalLimitsGroup("group1", 220.0, List.of(MockUtils.mockTemporaryLimits(100, "temporary1", 50.0), MockUtils.mockTemporaryLimits(150, "temporary2", 70.0))) + ), List.of(), List.of( + CurrentLimitsData.builder().id("group1").permanentLimit(220.0).temporaryLimits(List.of( + TemporaryLimitData.builder().acceptableDuration(100).name("temporary1").value(50.0).build(), + TemporaryLimitData.builder().acceptableDuration(150).name("temporary2").value(70.0).build() + )).applicability(Applicability.SIDE1).build() + )), + // TODO get two dto but because hasLimit() condition always false + Arguments.of(List.of( + MockUtils.mockOperationalLimitsGroup("group1", 220.0, List.of()) + ), List.of( + MockUtils.mockOperationalLimitsGroup("group1", 220.0, List.of(MockUtils.mockTemporaryLimits(100, "temporary1", 50.0), MockUtils.mockTemporaryLimits(150, "temporary2", 70.0))) + ), List.of( + CurrentLimitsData.builder().id("group1").permanentLimit(220.0).temporaryLimits(List.of( + TemporaryLimitData.builder().acceptableDuration(100).name("temporary1").value(50.0).build(), + TemporaryLimitData.builder().acceptableDuration(150).name("temporary2").value(70.0).build() + )).applicability(Applicability.SIDE2).build() + )), + // TODO java.lang.NullPointerException: Cannot invoke "org.gridsuite.network.map.dto.common.CurrentLimitsData.getId()" because "limitsData" is null + Arguments.of(List.of( + MockUtils.mockOperationalLimitsGroup("group1", Double.NaN, List.of()) + ), List.of( + MockUtils.mockOperationalLimitsGroup("group1", 220.0, List.of(MockUtils.mockTemporaryLimits(100, "temporary1", 50.0), MockUtils.mockTemporaryLimits(150, "temporary2", 70.0))) + ), List.of( + CurrentLimitsData.builder().id("group1").permanentLimit(220.0).temporaryLimits(List.of( + TemporaryLimitData.builder().acceptableDuration(100).name("temporary1").value(50.0).build(), + TemporaryLimitData.builder().acceptableDuration(150).name("temporary2").value(70.0).build() + )).applicability(Applicability.SIDE2).build() + )), + // TODO java.lang.NullPointerException: Cannot invoke "java.lang.Double.doubleValue()" because "this.permanentLimit" is null + Arguments.of(List.of( + MockUtils.mockOperationalLimitsGroup("group1", Double.NaN, List.of(MockUtils.mockTemporaryLimits(100, "temporary1", 50.0), MockUtils.mockTemporaryLimits(150, "temporary2", 70.0))) + ), List.of( + MockUtils.mockOperationalLimitsGroup("group1", 220.0, List.of(MockUtils.mockTemporaryLimits(100, "temporary1", 50.0), MockUtils.mockTemporaryLimits(150, "temporary2", 70.0))) + ), List.of( + CurrentLimitsData.builder().id("group1").permanentLimit(220.0).temporaryLimits(List.of( + TemporaryLimitData.builder().acceptableDuration(100).name("temporary1").value(50.0).build(), + TemporaryLimitData.builder().acceptableDuration(150).name("temporary2").value(70.0).build() + )).applicability(Applicability.SIDE2).build() + )), + Arguments.of(List.of( + MockUtils.mockOperationalLimitsGroup("group1", 220.0, List.of(MockUtils.mockTemporaryLimits(100, "temporary1", 50.0), MockUtils.mockTemporaryLimits(150, "temporary2", 70.0))) + ), List.of( + MockUtils.mockOperationalLimitsGroup("group1", 220.0, List.of()) + ), List.of( + CurrentLimitsData.builder().id("group1").permanentLimit(220.0).temporaryLimits(List.of( + TemporaryLimitData.builder().acceptableDuration(100).name("temporary1").value(50.0).build(), + TemporaryLimitData.builder().acceptableDuration(150).name("temporary2").value(70.0).build() + )).applicability(Applicability.SIDE1).build() + )), + Arguments.of(List.of( + MockUtils.mockOperationalLimitsGroup("group1", 220.0, List.of(MockUtils.mockTemporaryLimits(100, "temporary1", 50.0), MockUtils.mockTemporaryLimits(150, "temporary2", 70.0))) + ), List.of( + MockUtils.mockOperationalLimitsGroup("group1", Double.NaN, List.of()) + ), List.of( + CurrentLimitsData.builder().id("group1").permanentLimit(220.0).temporaryLimits(List.of( + TemporaryLimitData.builder().acceptableDuration(100).name("temporary1").value(50.0).build(), + TemporaryLimitData.builder().acceptableDuration(150).name("temporary2").value(70.0).build() + )).applicability(Applicability.SIDE1).build() + )), + Arguments.of(List.of( + MockUtils.mockOperationalLimitsGroup("group1", 220.0, List.of(MockUtils.mockTemporaryLimits(100, "temporary1", 50.0), MockUtils.mockTemporaryLimits(150, "temporary2", 70.0))) + ), List.of( + MockUtils.mockOperationalLimitsGroup("group1", Double.NaN, List.of(MockUtils.mockTemporaryLimits(100, "temporary1", 50.0), MockUtils.mockTemporaryLimits(150, "temporary2", 70.0))) + ), List.of( + CurrentLimitsData.builder().id("group1").permanentLimit(220.0).temporaryLimits(List.of( + TemporaryLimitData.builder().acceptableDuration(100).name("temporary1").value(50.0).build(), + TemporaryLimitData.builder().acceptableDuration(150).name("temporary2").value(70.0).build() + )).applicability(Applicability.SIDE1).build() + )) + ); + } + + @Test + void shouldThrowOnNanLimit(final SoftAssertions softly) { + final var l1 = List.of(MockUtils.mockOperationalLimitsGroup("group1", Double.NaN, List.of(MockUtils.mockTemporaryLimits(100, "temporary1", 50.0), MockUtils.mockTemporaryLimits(150, "temporary2", 70.0)))); + final var l2 = List.of(MockUtils.mockOperationalLimitsGroup("group1", 220.0, List.of(MockUtils.mockTemporaryLimits(100, "temporary1", 50.0), MockUtils.mockTemporaryLimits(150, "temporary2", 70.0)))); + AtomicReference> results = new AtomicReference<>(); + softly.assertThatNullPointerException().isThrownBy(() -> ElementUtils.mergeCurrentLimits(l1, l2, results::set)); + softly.assertThatNullPointerException().isThrownBy(() -> ElementUtils.mergeCurrentLimits(l2, l1, results::set)); + } + + // TODO what to do when one side has duplicate ID? + } } From 2f206ba6d15f7e58fbba97dda20bc39bec903cab Mon Sep 17 00:00:00 2001 From: Tristan Chuine Date: Thu, 7 Aug 2025 21:44:42 +0200 Subject: [PATCH 03/11] replace mockito by classes easier to debug --- .../network/map/utils/ElementUtilsTest.java | 120 ++++++------ .../gridsuite/network/map/utils/MockDto.java | 175 ++++++++++++++++++ .../network/map/utils/MockUtils.java | 43 ----- 3 files changed, 241 insertions(+), 97 deletions(-) create mode 100644 src/test/java/org/gridsuite/network/map/utils/MockDto.java delete mode 100644 src/test/java/org/gridsuite/network/map/utils/MockUtils.java diff --git a/src/test/java/org/gridsuite/network/map/utils/ElementUtilsTest.java b/src/test/java/org/gridsuite/network/map/utils/ElementUtilsTest.java index b7fddbaa..2605e860 100644 --- a/src/test/java/org/gridsuite/network/map/utils/ElementUtilsTest.java +++ b/src/test/java/org/gridsuite/network/map/utils/ElementUtilsTest.java @@ -8,6 +8,9 @@ import org.gridsuite.network.map.dto.common.CurrentLimitsData.Applicability; import org.gridsuite.network.map.dto.common.TemporaryLimitData; import org.gridsuite.network.map.dto.utils.ElementUtils; +import org.gridsuite.network.map.utils.MockDto.CurrentLimitsDtoTest; +import org.gridsuite.network.map.utils.MockDto.OperationalLimitsGroupDtoTest; +import org.gridsuite.network.map.utils.MockDto.TemporaryLimitDtoTest; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; @@ -22,6 +25,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Optional; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.stream.Stream; @@ -60,7 +64,7 @@ void nullInput() { @Test void emptyCurrentLimits() { - final var mock = MockUtils.mockOperationalLimitsGroup("id", null); + final var mock = Mockito.spy(new OperationalLimitsGroupDtoTest("id", Optional.empty())); mocks.add(mock); assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(mock)).isNull(); Mockito.verify(mock).getCurrentLimits(); @@ -68,42 +72,41 @@ void emptyCurrentLimits() { @Test void nanPermLimitAndNullTempLimit() { - final var clMock = MockUtils.mockCurrentLimits(Double.NaN, null); + final var clMock = Mockito.spy(new CurrentLimitsDtoTest(Double.NaN, null)); mocks.add(clMock); - final var olgMock = MockUtils.mockOperationalLimitsGroup("id", clMock); + final var olgMock = Mockito.spy(new OperationalLimitsGroupDtoTest("id", clMock)); mocks.add(olgMock); assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(olgMock)).isNull(); - Mockito.verify(olgMock, Mockito.times(2)).getCurrentLimits(); - Mockito.verify(olgMock).getId(); + Mockito.verify(olgMock, Mockito.times(1)).getCurrentLimits(); Mockito.verify(clMock).getPermanentLimit(); Mockito.verify(clMock).getTemporaryLimits(); } @Test void nanPermLimitAndEmptyTempLimit() { - final var clMock = MockUtils.mockCurrentLimits(Double.NaN, List.of()); + final var clMock = Mockito.spy(new CurrentLimitsDtoTest(Double.NaN, List.of())); mocks.add(clMock); - final var olgMock = MockUtils.mockOperationalLimitsGroup("id", clMock); + final var olgMock = Mockito.spy(new OperationalLimitsGroupDtoTest("id", clMock)); mocks.add(olgMock); assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(olgMock)).isNull(); - Mockito.verify(olgMock, Mockito.times(2)).getCurrentLimits(); - Mockito.verify(olgMock).getId(); + Mockito.verify(olgMock, Mockito.times(1)).getCurrentLimits(); Mockito.verify(clMock).getPermanentLimit(); Mockito.verify(clMock).getTemporaryLimits(); } @Test void nanPermLimitAndNonEmptyTempLimit() { - final var tlMock = MockUtils.mockTemporaryLimits(123, "testLimit", 456.789); + final var tlMock = Mockito.spy(new TemporaryLimitDtoTest("testLimit", 456.789, 123)); mocks.add(tlMock); - final var clMock = MockUtils.mockCurrentLimits(Double.NaN, List.of(tlMock)); + final var clMock = Mockito.spy(new CurrentLimitsDtoTest(Double.NaN, List.of(tlMock))); mocks.add(clMock); - final var olgMock = MockUtils.mockOperationalLimitsGroup("my id", clMock); + final var olgMock = Mockito.spy(new OperationalLimitsGroupDtoTest("my id", clMock)); mocks.add(olgMock); - assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(olgMock)).isEqualTo(CurrentLimitsData.builder() - .id("my id").applicability(null).permanentLimit(null).temporaryLimits(List.of(TemporaryLimitData.builder() - .acceptableDuration(123).name("testLimit").value(456.789).build())).build()); - Mockito.verify(olgMock, Mockito.times(2)).getCurrentLimits(); + assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(olgMock)) + .extracting(CurrentLimitsData.CurrentLimitsDataBuilder::build) + .isEqualTo(CurrentLimitsData.builder().id("my id").applicability(null).permanentLimit(null).temporaryLimits( + List.of(TemporaryLimitData.builder().acceptableDuration(123).name("testLimit").value(456.789).build())).build()); + Mockito.verify(olgMock, Mockito.times(1)).getCurrentLimits(); Mockito.verify(olgMock).getId(); Mockito.verify(clMock).getPermanentLimit(); Mockito.verify(clMock, Mockito.times(2)).getTemporaryLimits(); @@ -114,13 +117,14 @@ void nanPermLimitAndNonEmptyTempLimit() { @Test void nonNanPermLimitAndNullTempLimit() { - final var clMock = MockUtils.mockCurrentLimits(0.123, null); + final var clMock = Mockito.spy(new CurrentLimitsDtoTest(0.123, null)); mocks.add(clMock); - final var olgMock = MockUtils.mockOperationalLimitsGroup("my id", clMock); + final var olgMock = Mockito.spy(new OperationalLimitsGroupDtoTest("my id", clMock)); mocks.add(olgMock); - assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(olgMock)).isEqualTo(CurrentLimitsData.builder() - .id("my id").applicability(null).permanentLimit(0.123).temporaryLimits(null).build()); - Mockito.verify(olgMock, Mockito.times(2)).getCurrentLimits(); + assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(olgMock)) + .extracting(CurrentLimitsData.CurrentLimitsDataBuilder::build) + .isEqualTo(CurrentLimitsData.builder().id("my id").applicability(null).permanentLimit(0.123).temporaryLimits(null).build()); + Mockito.verify(olgMock, Mockito.times(1)).getCurrentLimits(); Mockito.verify(olgMock).getId(); Mockito.verify(clMock, Mockito.times(2)).getPermanentLimit(); Mockito.verify(clMock).getTemporaryLimits(); @@ -128,13 +132,14 @@ void nonNanPermLimitAndNullTempLimit() { @Test void nonNanPermLimitAndEmptyTempLimit() { - final var clMock = MockUtils.mockCurrentLimits(0.123, List.of()); + final var clMock = Mockito.spy(new CurrentLimitsDtoTest(0.123, List.of())); mocks.add(clMock); - final var olgMock = MockUtils.mockOperationalLimitsGroup("my id", clMock); + final var olgMock = Mockito.spy(new OperationalLimitsGroupDtoTest("my id", clMock)); mocks.add(olgMock); - assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(olgMock)).isEqualTo(CurrentLimitsData.builder() - .id("my id").applicability(null).permanentLimit(0.123).temporaryLimits(null).build()); - Mockito.verify(olgMock, Mockito.times(2)).getCurrentLimits(); + assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(olgMock)) + .extracting(CurrentLimitsData.CurrentLimitsDataBuilder::build) + .isEqualTo(CurrentLimitsData.builder().id("my id").applicability(null).permanentLimit(0.123).temporaryLimits(null).build()); + Mockito.verify(olgMock, Mockito.times(1)).getCurrentLimits(); Mockito.verify(olgMock).getId(); Mockito.verify(clMock, Mockito.times(2)).getPermanentLimit(); Mockito.verify(clMock).getTemporaryLimits(); @@ -142,16 +147,17 @@ void nonNanPermLimitAndEmptyTempLimit() { @Test void nonNanPermLimitAndNonEmptyTempLimit() { - final var tlMock = MockUtils.mockTemporaryLimits(123, "testLimit", 456.789); + final var tlMock = Mockito.spy(new TemporaryLimitDtoTest("testLimit", 456.789, 123)); mocks.add(tlMock); - final var clMock = MockUtils.mockCurrentLimits(0.0, List.of(tlMock)); + final var clMock = Mockito.spy(new CurrentLimitsDtoTest(0.0, List.of(tlMock))); mocks.add(clMock); - final var olgMock = MockUtils.mockOperationalLimitsGroup("my id", clMock); + final var olgMock = Mockito.spy(new OperationalLimitsGroupDtoTest("my id", clMock)); mocks.add(olgMock); - assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(olgMock)).isEqualTo(CurrentLimitsData.builder() - .id("my id").applicability(null).permanentLimit(0.0).temporaryLimits(List.of(TemporaryLimitData.builder() - .acceptableDuration(123).name("testLimit").value(456.789).build())).build()); - Mockito.verify(olgMock, Mockito.times(2)).getCurrentLimits(); + assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(olgMock)) + .extracting(CurrentLimitsData.CurrentLimitsDataBuilder::build) + .isEqualTo(CurrentLimitsData.builder().id("my id").applicability(null).permanentLimit(0.0).temporaryLimits( + List.of(TemporaryLimitData.builder().acceptableDuration(123).name("testLimit").value(456.789).build())).build()); + Mockito.verify(olgMock, Mockito.times(1)).getCurrentLimits(); Mockito.verify(olgMock).getId(); Mockito.verify(clMock, Mockito.times(2)).getPermanentLimit(); Mockito.verify(clMock, Mockito.times(2)).getTemporaryLimits(); @@ -178,89 +184,95 @@ void shouldNotThrow( private static Stream mergeCurrentLimitsTestData() { return Stream.of( + // 1 Arguments.of(List.of( - MockUtils.mockOperationalLimitsGroup("group1", 220.0, List.of(MockUtils.mockTemporaryLimits(100, "temporary1", 50.0), MockUtils.mockTemporaryLimits(150, "temporary2", 70.0))) + new OperationalLimitsGroupDtoTest("group1", new CurrentLimitsDtoTest(220.0, List.of(new TemporaryLimitDtoTest("temporary1", 50.0, 100), new TemporaryLimitDtoTest("temporary2", 70.0, 150)))) ), List.of( - MockUtils.mockOperationalLimitsGroup("group1", 220.0, List.of(MockUtils.mockTemporaryLimits(100, "temporary1", 50.0), MockUtils.mockTemporaryLimits(150, "temporary2", 70.0))) + new OperationalLimitsGroupDtoTest("group1", new CurrentLimitsDtoTest(220.0, List.of(new TemporaryLimitDtoTest("temporary1", 50.0, 100), new TemporaryLimitDtoTest("temporary2", 70.0, 150)))) ), List.of( CurrentLimitsData.builder().id("group1").permanentLimit(220.0).temporaryLimits(List.of( TemporaryLimitData.builder().acceptableDuration(100).name("temporary1").value(50.0).build(), TemporaryLimitData.builder().acceptableDuration(150).name("temporary2").value(70.0).build() )).applicability(Applicability.EQUIPMENT).build() )), + // 2 Arguments.of(List.of(), List.of( - MockUtils.mockOperationalLimitsGroup("group1", 220.0, List.of(MockUtils.mockTemporaryLimits(100, "temporary1", 50.0), MockUtils.mockTemporaryLimits(150, "temporary2", 70.0))) + new OperationalLimitsGroupDtoTest("group1", new CurrentLimitsDtoTest(220.0, List.of(new TemporaryLimitDtoTest("temporary1", 50.0, 100), new TemporaryLimitDtoTest("temporary2", 70.0, 150)))) ), List.of( CurrentLimitsData.builder().id("group1").permanentLimit(220.0).temporaryLimits(List.of( TemporaryLimitData.builder().acceptableDuration(100).name("temporary1").value(50.0).build(), TemporaryLimitData.builder().acceptableDuration(150).name("temporary2").value(70.0).build() )).applicability(Applicability.SIDE2).build() )), + // 3 Arguments.of(List.of( - MockUtils.mockOperationalLimitsGroup("group1", 220.0, List.of(MockUtils.mockTemporaryLimits(100, "temporary1", 50.0), MockUtils.mockTemporaryLimits(150, "temporary2", 70.0))) + new OperationalLimitsGroupDtoTest("group1", new CurrentLimitsDtoTest(220.0, List.of(new TemporaryLimitDtoTest("temporary1", 50.0, 100), new TemporaryLimitDtoTest("temporary2", 70.0, 150)))) ), List.of(), List.of( CurrentLimitsData.builder().id("group1").permanentLimit(220.0).temporaryLimits(List.of( TemporaryLimitData.builder().acceptableDuration(100).name("temporary1").value(50.0).build(), TemporaryLimitData.builder().acceptableDuration(150).name("temporary2").value(70.0).build() )).applicability(Applicability.SIDE1).build() )), - // TODO get two dto but because hasLimit() condition always false + // 4 Arguments.of(List.of( - MockUtils.mockOperationalLimitsGroup("group1", 220.0, List.of()) + new OperationalLimitsGroupDtoTest("group1", new CurrentLimitsDtoTest(220.0, List.of())) ), List.of( - MockUtils.mockOperationalLimitsGroup("group1", 220.0, List.of(MockUtils.mockTemporaryLimits(100, "temporary1", 50.0), MockUtils.mockTemporaryLimits(150, "temporary2", 70.0))) + new OperationalLimitsGroupDtoTest("group1", new CurrentLimitsDtoTest(220.0, List.of(new TemporaryLimitDtoTest("temporary1", 50.0, 100), new TemporaryLimitDtoTest("temporary2", 70.0, 150)))) ), List.of( CurrentLimitsData.builder().id("group1").permanentLimit(220.0).temporaryLimits(List.of( TemporaryLimitData.builder().acceptableDuration(100).name("temporary1").value(50.0).build(), TemporaryLimitData.builder().acceptableDuration(150).name("temporary2").value(70.0).build() )).applicability(Applicability.SIDE2).build() )), - // TODO java.lang.NullPointerException: Cannot invoke "org.gridsuite.network.map.dto.common.CurrentLimitsData.getId()" because "limitsData" is null + // 5 Arguments.of(List.of( - MockUtils.mockOperationalLimitsGroup("group1", Double.NaN, List.of()) + new OperationalLimitsGroupDtoTest("group1", new CurrentLimitsDtoTest(Double.NaN, List.of())) ), List.of( - MockUtils.mockOperationalLimitsGroup("group1", 220.0, List.of(MockUtils.mockTemporaryLimits(100, "temporary1", 50.0), MockUtils.mockTemporaryLimits(150, "temporary2", 70.0))) + new OperationalLimitsGroupDtoTest("group1", new CurrentLimitsDtoTest(220.0, List.of(new TemporaryLimitDtoTest("temporary1", 50.0, 100), new TemporaryLimitDtoTest("temporary2", 70.0, 150)))) ), List.of( CurrentLimitsData.builder().id("group1").permanentLimit(220.0).temporaryLimits(List.of( TemporaryLimitData.builder().acceptableDuration(100).name("temporary1").value(50.0).build(), TemporaryLimitData.builder().acceptableDuration(150).name("temporary2").value(70.0).build() )).applicability(Applicability.SIDE2).build() )), - // TODO java.lang.NullPointerException: Cannot invoke "java.lang.Double.doubleValue()" because "this.permanentLimit" is null + // 6 Arguments.of(List.of( - MockUtils.mockOperationalLimitsGroup("group1", Double.NaN, List.of(MockUtils.mockTemporaryLimits(100, "temporary1", 50.0), MockUtils.mockTemporaryLimits(150, "temporary2", 70.0))) + new OperationalLimitsGroupDtoTest("group1", new CurrentLimitsDtoTest(Double.NaN, List.of(new TemporaryLimitDtoTest("temporary1", 50.0, 100), new TemporaryLimitDtoTest("temporary2", 70.0, 150)))) ), List.of( - MockUtils.mockOperationalLimitsGroup("group1", 220.0, List.of(MockUtils.mockTemporaryLimits(100, "temporary1", 50.0), MockUtils.mockTemporaryLimits(150, "temporary2", 70.0))) + new OperationalLimitsGroupDtoTest("group1", new CurrentLimitsDtoTest(220.0, List.of(new TemporaryLimitDtoTest("temporary1", 50.0, 100), new TemporaryLimitDtoTest("temporary2", 70.0, 150)))) ), List.of( CurrentLimitsData.builder().id("group1").permanentLimit(220.0).temporaryLimits(List.of( TemporaryLimitData.builder().acceptableDuration(100).name("temporary1").value(50.0).build(), TemporaryLimitData.builder().acceptableDuration(150).name("temporary2").value(70.0).build() )).applicability(Applicability.SIDE2).build() )), + // 7 Arguments.of(List.of( - MockUtils.mockOperationalLimitsGroup("group1", 220.0, List.of(MockUtils.mockTemporaryLimits(100, "temporary1", 50.0), MockUtils.mockTemporaryLimits(150, "temporary2", 70.0))) + new OperationalLimitsGroupDtoTest("group1", new CurrentLimitsDtoTest(220.0, List.of(new TemporaryLimitDtoTest("temporary1", 50.0, 100), new TemporaryLimitDtoTest("temporary2", 70.0, 150)))) ), List.of( - MockUtils.mockOperationalLimitsGroup("group1", 220.0, List.of()) + new OperationalLimitsGroupDtoTest("group1", new CurrentLimitsDtoTest(220.0, List.of())) ), List.of( CurrentLimitsData.builder().id("group1").permanentLimit(220.0).temporaryLimits(List.of( TemporaryLimitData.builder().acceptableDuration(100).name("temporary1").value(50.0).build(), TemporaryLimitData.builder().acceptableDuration(150).name("temporary2").value(70.0).build() )).applicability(Applicability.SIDE1).build() )), + // 8 Arguments.of(List.of( - MockUtils.mockOperationalLimitsGroup("group1", 220.0, List.of(MockUtils.mockTemporaryLimits(100, "temporary1", 50.0), MockUtils.mockTemporaryLimits(150, "temporary2", 70.0))) + new OperationalLimitsGroupDtoTest("group1", new CurrentLimitsDtoTest(220.0, List.of(new TemporaryLimitDtoTest("temporary1", 50.0, 100), new TemporaryLimitDtoTest("temporary2", 70.0, 150)))) ), List.of( - MockUtils.mockOperationalLimitsGroup("group1", Double.NaN, List.of()) + new OperationalLimitsGroupDtoTest("group1", new CurrentLimitsDtoTest(Double.NaN, List.of())) ), List.of( CurrentLimitsData.builder().id("group1").permanentLimit(220.0).temporaryLimits(List.of( TemporaryLimitData.builder().acceptableDuration(100).name("temporary1").value(50.0).build(), TemporaryLimitData.builder().acceptableDuration(150).name("temporary2").value(70.0).build() )).applicability(Applicability.SIDE1).build() )), + // 9 Arguments.of(List.of( - MockUtils.mockOperationalLimitsGroup("group1", 220.0, List.of(MockUtils.mockTemporaryLimits(100, "temporary1", 50.0), MockUtils.mockTemporaryLimits(150, "temporary2", 70.0))) + new OperationalLimitsGroupDtoTest("group1", new CurrentLimitsDtoTest(220.0, List.of(new TemporaryLimitDtoTest("temporary1", 50.0, 100), new TemporaryLimitDtoTest("temporary2", 70.0, 150)))) ), List.of( - MockUtils.mockOperationalLimitsGroup("group1", Double.NaN, List.of(MockUtils.mockTemporaryLimits(100, "temporary1", 50.0), MockUtils.mockTemporaryLimits(150, "temporary2", 70.0))) + new OperationalLimitsGroupDtoTest("group1", new CurrentLimitsDtoTest(Double.NaN, List.of(new TemporaryLimitDtoTest("temporary1", 50.0, 100), new TemporaryLimitDtoTest("temporary2", 70.0, 150)))) ), List.of( CurrentLimitsData.builder().id("group1").permanentLimit(220.0).temporaryLimits(List.of( TemporaryLimitData.builder().acceptableDuration(100).name("temporary1").value(50.0).build(), @@ -272,8 +284,8 @@ private static Stream mergeCurrentLimitsTestData() { @Test void shouldThrowOnNanLimit(final SoftAssertions softly) { - final var l1 = List.of(MockUtils.mockOperationalLimitsGroup("group1", Double.NaN, List.of(MockUtils.mockTemporaryLimits(100, "temporary1", 50.0), MockUtils.mockTemporaryLimits(150, "temporary2", 70.0)))); - final var l2 = List.of(MockUtils.mockOperationalLimitsGroup("group1", 220.0, List.of(MockUtils.mockTemporaryLimits(100, "temporary1", 50.0), MockUtils.mockTemporaryLimits(150, "temporary2", 70.0)))); + final Collection l1 = List.of(new OperationalLimitsGroupDtoTest("group1", new CurrentLimitsDtoTest(Double.NaN, List.of(new TemporaryLimitDtoTest("temporary1", 50.0, 100), new TemporaryLimitDtoTest("temporary2", 70.0, 150))))); + final Collection l2 = List.of(new OperationalLimitsGroupDtoTest("group1", new CurrentLimitsDtoTest(220.0, List.of(new TemporaryLimitDtoTest("temporary1", 50.0, 100), new TemporaryLimitDtoTest("temporary2", 70.0, 150))))); AtomicReference> results = new AtomicReference<>(); softly.assertThatNullPointerException().isThrownBy(() -> ElementUtils.mergeCurrentLimits(l1, l2, results::set)); softly.assertThatNullPointerException().isThrownBy(() -> ElementUtils.mergeCurrentLimits(l2, l1, results::set)); diff --git a/src/test/java/org/gridsuite/network/map/utils/MockDto.java b/src/test/java/org/gridsuite/network/map/utils/MockDto.java new file mode 100644 index 00000000..4ee57e7e --- /dev/null +++ b/src/test/java/org/gridsuite/network/map/utils/MockDto.java @@ -0,0 +1,175 @@ +package org.gridsuite.network.map.utils; + +import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.LoadingLimits.TemporaryLimit; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.experimental.Accessors; +import org.apache.commons.lang3.NotImplementedException; + +import java.util.Collection; +import java.util.Optional; +import java.util.Set; + +public final class MockDto { + private MockDto() { + throw new InstantiationError("Utility Class cannot be instantiated."); + } + + @Data + @AllArgsConstructor + public static class TemporaryLimitDtoTest implements TemporaryLimit { + private String name; + private double value; + private int acceptableDuration; + + @Override + public boolean isFictitious() { + throw new NotImplementedException("Not supported yet."); + } + } + + @Data + @AllArgsConstructor + public static class CurrentLimitsDtoTest implements CurrentLimits { + @Accessors(chain = true) + private double permanentLimit; + private Collection temporaryLimits; + + @Override + public TemporaryLimit getTemporaryLimit(int acceptableDuration) { + throw new NotImplementedException("Not supported yet."); + } + + @Override + public double getTemporaryLimitValue(int acceptableDuration) { + throw new NotImplementedException("Not supported yet."); + } + + @Override + public LoadingLimits setTemporaryLimitValue(int acceptableDuration, double temporaryLimitValue) { + throw new NotImplementedException("Not supported yet."); + } + + @Override + public void remove() { + throw new NotImplementedException("Not supported yet."); + } + + @Override + public LimitType getLimitType() { + throw new NotImplementedException("Not supported yet."); + } + } + + @Data + @AllArgsConstructor + public static class OperationalLimitsGroupDtoTest implements OperationalLimitsGroup { + private String id; + private Optional currentLimits; + + public OperationalLimitsGroupDtoTest(String id, CurrentLimits currentLimits) { + this(id, Optional.ofNullable(currentLimits)); + } + + @Override + public Optional getActivePowerLimits() { + throw new NotImplementedException("Not supported yet."); + } + + @Override + public Optional getApparentPowerLimits() { + throw new NotImplementedException("Not supported yet."); + } + + @Override + public CurrentLimitsAdder newCurrentLimits() { + throw new NotImplementedException("Not supported yet."); + } + + @Override + public ActivePowerLimitsAdder newActivePowerLimits() { + throw new NotImplementedException("Not supported yet."); + } + + @Override + public ApparentPowerLimitsAdder newApparentPowerLimits() { + throw new NotImplementedException("Not supported yet."); + } + + @Override + public CurrentLimitsAdder newCurrentLimits(CurrentLimits currentLimits) { + throw new NotImplementedException("Not supported yet."); + } + + @Override + public ActivePowerLimitsAdder newActivePowerLimits(ActivePowerLimits activePowerLimits) { + throw new NotImplementedException("Not supported yet."); + } + + @Override + public ApparentPowerLimitsAdder newApparentPowerLimits(ApparentPowerLimits apparentPowerLimits) { + throw new NotImplementedException("Not supported yet."); + } + + @Override + public void removeCurrentLimits() { + throw new NotImplementedException("Not supported yet."); + } + + @Override + public void removeActivePowerLimits() { + throw new NotImplementedException("Not supported yet."); + } + + @Override + public void removeApparentPowerLimits() { + throw new NotImplementedException("Not supported yet."); + } + + @Override + public boolean isEmpty() { + throw new NotImplementedException("Not supported yet."); + } + + @Override + public boolean hasProperty() { + throw new NotImplementedException("Not supported yet."); + } + + @Override + public boolean hasProperty(String key) { + throw new NotImplementedException("Not supported yet."); + } + + @Override + public String getProperty(String key) { + throw new NotImplementedException("Not supported yet."); + } + + @Override + public String getProperty(String key, String defaultValue) { + throw new NotImplementedException("Not supported yet."); + } + + @Override + public String setProperty(String key, String value) { + throw new NotImplementedException("Not supported yet."); + } + + @Override + public boolean removeProperty(String key) { + throw new NotImplementedException("Not supported yet."); + } + + @Override + public Set getPropertyNames() { + throw new NotImplementedException("Not supported yet."); + } + + @Override + public Network getNetwork() { + throw new NotImplementedException("Not supported yet."); + } + } +} diff --git a/src/test/java/org/gridsuite/network/map/utils/MockUtils.java b/src/test/java/org/gridsuite/network/map/utils/MockUtils.java deleted file mode 100644 index 77a4e276..00000000 --- a/src/test/java/org/gridsuite/network/map/utils/MockUtils.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.gridsuite.network.map.utils; - -import com.powsybl.iidm.network.CurrentLimits; -import com.powsybl.iidm.network.LoadingLimits.TemporaryLimit; -import com.powsybl.iidm.network.OperationalLimitsGroup; -import org.mockito.Mockito; -import org.mockito.quality.Strictness; - -import java.util.List; -import java.util.Optional; - -public final class MockUtils { - private MockUtils() { - throw new InstantiationError("Utility Class cannot be instantiated."); - } - - public static TemporaryLimit mockTemporaryLimits(final int acceptableDuration, final String name, final double value) { - TemporaryLimit mock = Mockito.mock(TemporaryLimit.class); - Mockito.when(mock.getAcceptableDuration()).thenReturn(acceptableDuration); - Mockito.when(mock.getName()).thenReturn(name); - Mockito.when(mock.getValue()).thenReturn(value); - return mock; - } - - public static CurrentLimits mockCurrentLimits(double permanentLimit, List temporaryLimits) { - CurrentLimits mock = Mockito.mock(CurrentLimits.class); - //Mockito.when(mock.getLimitType()).thenCallRealMethod(); - Mockito.when(mock.getPermanentLimit()).thenReturn(permanentLimit); - Mockito.when(mock.getTemporaryLimits()).thenReturn(temporaryLimits); - return mock; - } - - public static OperationalLimitsGroup mockOperationalLimitsGroup(final String id, double permanentLimit, List temporaryLimits) { - return mockOperationalLimitsGroup(id, mockCurrentLimits(permanentLimit, temporaryLimits)); - } - - public static OperationalLimitsGroup mockOperationalLimitsGroup(final String id, final CurrentLimits cl) { - OperationalLimitsGroup mock = Mockito.mock(OperationalLimitsGroup.class, Mockito.withSettings().strictness(Strictness.LENIENT)); - Mockito.when(mock.getId()).thenReturn(id); - Mockito.when(mock.getCurrentLimits()).thenReturn(Optional.ofNullable(cl)); - return mock; - } -} From 43766c2b9a5e222430193c5c013f56322302b66e Mon Sep 17 00:00:00 2001 From: Tristan Chuine Date: Thu, 7 Aug 2025 21:40:45 +0200 Subject: [PATCH 04/11] Add tests to show problem --- .../network/map/utils/ElementUtilsTest.java | 45 ++++++++++++++----- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/src/test/java/org/gridsuite/network/map/utils/ElementUtilsTest.java b/src/test/java/org/gridsuite/network/map/utils/ElementUtilsTest.java index 2605e860..733543f3 100644 --- a/src/test/java/org/gridsuite/network/map/utils/ElementUtilsTest.java +++ b/src/test/java/org/gridsuite/network/map/utils/ElementUtilsTest.java @@ -1,24 +1,24 @@ package org.gridsuite.network.map.utils; +import com.powsybl.iidm.network.LoadingLimits; +import com.powsybl.iidm.network.LoadingLimits.TemporaryLimit; import com.powsybl.iidm.network.OperationalLimitsGroup; +import org.assertj.core.api.InstanceOfAssertFactories; import org.assertj.core.api.SoftAssertions; import org.assertj.core.api.WithAssertions; import org.assertj.core.api.junit.jupiter.SoftAssertionsExtension; import org.gridsuite.network.map.dto.common.CurrentLimitsData; import org.gridsuite.network.map.dto.common.CurrentLimitsData.Applicability; +import org.gridsuite.network.map.dto.common.CurrentLimitsData.CurrentLimitsDataBuilder; import org.gridsuite.network.map.dto.common.TemporaryLimitData; import org.gridsuite.network.map.dto.utils.ElementUtils; import org.gridsuite.network.map.utils.MockDto.CurrentLimitsDtoTest; import org.gridsuite.network.map.utils.MockDto.OperationalLimitsGroupDtoTest; import org.gridsuite.network.map.utils.MockDto.TemporaryLimitDtoTest; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.*; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.*; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; @@ -103,7 +103,7 @@ void nanPermLimitAndNonEmptyTempLimit() { final var olgMock = Mockito.spy(new OperationalLimitsGroupDtoTest("my id", clMock)); mocks.add(olgMock); assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(olgMock)) - .extracting(CurrentLimitsData.CurrentLimitsDataBuilder::build) + .extracting(CurrentLimitsDataBuilder::build) .isEqualTo(CurrentLimitsData.builder().id("my id").applicability(null).permanentLimit(null).temporaryLimits( List.of(TemporaryLimitData.builder().acceptableDuration(123).name("testLimit").value(456.789).build())).build()); Mockito.verify(olgMock, Mockito.times(1)).getCurrentLimits(); @@ -122,7 +122,7 @@ void nonNanPermLimitAndNullTempLimit() { final var olgMock = Mockito.spy(new OperationalLimitsGroupDtoTest("my id", clMock)); mocks.add(olgMock); assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(olgMock)) - .extracting(CurrentLimitsData.CurrentLimitsDataBuilder::build) + .extracting(CurrentLimitsDataBuilder::build) .isEqualTo(CurrentLimitsData.builder().id("my id").applicability(null).permanentLimit(0.123).temporaryLimits(null).build()); Mockito.verify(olgMock, Mockito.times(1)).getCurrentLimits(); Mockito.verify(olgMock).getId(); @@ -137,7 +137,7 @@ void nonNanPermLimitAndEmptyTempLimit() { final var olgMock = Mockito.spy(new OperationalLimitsGroupDtoTest("my id", clMock)); mocks.add(olgMock); assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(olgMock)) - .extracting(CurrentLimitsData.CurrentLimitsDataBuilder::build) + .extracting(CurrentLimitsDataBuilder::build) .isEqualTo(CurrentLimitsData.builder().id("my id").applicability(null).permanentLimit(0.123).temporaryLimits(null).build()); Mockito.verify(olgMock, Mockito.times(1)).getCurrentLimits(); Mockito.verify(olgMock).getId(); @@ -154,7 +154,7 @@ void nonNanPermLimitAndNonEmptyTempLimit() { final var olgMock = Mockito.spy(new OperationalLimitsGroupDtoTest("my id", clMock)); mocks.add(olgMock); assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(olgMock)) - .extracting(CurrentLimitsData.CurrentLimitsDataBuilder::build) + .extracting(CurrentLimitsDataBuilder::build) .isEqualTo(CurrentLimitsData.builder().id("my id").applicability(null).permanentLimit(0.0).temporaryLimits( List.of(TemporaryLimitData.builder().acceptableDuration(123).name("testLimit").value(456.789).build())).build()); Mockito.verify(olgMock, Mockito.times(1)).getCurrentLimits(); @@ -171,6 +171,31 @@ void nonNanPermLimitAndNonEmptyTempLimit() { @Nested @DisplayName("fn mergeCurrentLimits(…, …, …)") class MergeCurrentLimitsTest { + /** + * This test check that after that the two input collections has been converted with {@link ElementUtils#operationalLimitsGroupToMapDataCurrentLimits(OperationalLimitsGroup)} + * that {@link CurrentLimitsData#hasLimits()} work as intended. + */ + @Order(1) + @ParameterizedTest + @MethodSource("testDtoHasLimitAfterMapData") + void testDtoHasLimitAfterMapped(final boolean expected, final double pl, final Collection tl) { + assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(new OperationalLimitsGroupDtoTest("id", new CurrentLimitsDtoTest(pl, tl)))).as("DTO builder") + .extracting(CurrentLimitsDataBuilder::build).as("DTO") + .extracting(CurrentLimitsData::hasLimits, InstanceOfAssertFactories.BOOLEAN).as("hasLimits") + .isEqualTo(expected); + } + + private static Stream testDtoHasLimitAfterMapData() { + return Stream.of( + Arguments.of(false, Double.NaN, null), + Arguments.of(false, Double.NaN, List.of()), + Arguments.of(true, Double.NaN, List.of(new TemporaryLimitDtoTest("tl", 1.2, 123))), + Arguments.of(true, 0.123, null), + Arguments.of(true, 0.123, List.of()), + Arguments.of(true, 0.123, List.of(new TemporaryLimitDtoTest("tl", 1.2, 123))) + ); + } + @ParameterizedTest(name = ParameterizedTest.INDEX_PLACEHOLDER) @MethodSource("mergeCurrentLimitsTestData") void shouldNotThrow( From 7f44d60845cfabf86dc76cd49a3c31ce233e0d9d Mon Sep 17 00:00:00 2001 From: Tristan Chuine Date: Thu, 7 Aug 2025 21:48:06 +0200 Subject: [PATCH 05/11] fix cherry-pick --- .../network/map/utils/ElementUtilsTest.java | 33 +++++++------------ 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/src/test/java/org/gridsuite/network/map/utils/ElementUtilsTest.java b/src/test/java/org/gridsuite/network/map/utils/ElementUtilsTest.java index 733543f3..132dbd45 100644 --- a/src/test/java/org/gridsuite/network/map/utils/ElementUtilsTest.java +++ b/src/test/java/org/gridsuite/network/map/utils/ElementUtilsTest.java @@ -1,6 +1,5 @@ package org.gridsuite.network.map.utils; -import com.powsybl.iidm.network.LoadingLimits; import com.powsybl.iidm.network.LoadingLimits.TemporaryLimit; import com.powsybl.iidm.network.OperationalLimitsGroup; import org.assertj.core.api.InstanceOfAssertFactories; @@ -9,7 +8,6 @@ import org.assertj.core.api.junit.jupiter.SoftAssertionsExtension; import org.gridsuite.network.map.dto.common.CurrentLimitsData; import org.gridsuite.network.map.dto.common.CurrentLimitsData.Applicability; -import org.gridsuite.network.map.dto.common.CurrentLimitsData.CurrentLimitsDataBuilder; import org.gridsuite.network.map.dto.common.TemporaryLimitData; import org.gridsuite.network.map.dto.utils.ElementUtils; import org.gridsuite.network.map.utils.MockDto.CurrentLimitsDtoTest; @@ -18,7 +16,8 @@ import org.junit.jupiter.api.*; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.*; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; @@ -103,7 +102,6 @@ void nanPermLimitAndNonEmptyTempLimit() { final var olgMock = Mockito.spy(new OperationalLimitsGroupDtoTest("my id", clMock)); mocks.add(olgMock); assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(olgMock)) - .extracting(CurrentLimitsDataBuilder::build) .isEqualTo(CurrentLimitsData.builder().id("my id").applicability(null).permanentLimit(null).temporaryLimits( List.of(TemporaryLimitData.builder().acceptableDuration(123).name("testLimit").value(456.789).build())).build()); Mockito.verify(olgMock, Mockito.times(1)).getCurrentLimits(); @@ -122,7 +120,6 @@ void nonNanPermLimitAndNullTempLimit() { final var olgMock = Mockito.spy(new OperationalLimitsGroupDtoTest("my id", clMock)); mocks.add(olgMock); assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(olgMock)) - .extracting(CurrentLimitsDataBuilder::build) .isEqualTo(CurrentLimitsData.builder().id("my id").applicability(null).permanentLimit(0.123).temporaryLimits(null).build()); Mockito.verify(olgMock, Mockito.times(1)).getCurrentLimits(); Mockito.verify(olgMock).getId(); @@ -137,7 +134,6 @@ void nonNanPermLimitAndEmptyTempLimit() { final var olgMock = Mockito.spy(new OperationalLimitsGroupDtoTest("my id", clMock)); mocks.add(olgMock); assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(olgMock)) - .extracting(CurrentLimitsDataBuilder::build) .isEqualTo(CurrentLimitsData.builder().id("my id").applicability(null).permanentLimit(0.123).temporaryLimits(null).build()); Mockito.verify(olgMock, Mockito.times(1)).getCurrentLimits(); Mockito.verify(olgMock).getId(); @@ -154,7 +150,6 @@ void nonNanPermLimitAndNonEmptyTempLimit() { final var olgMock = Mockito.spy(new OperationalLimitsGroupDtoTest("my id", clMock)); mocks.add(olgMock); assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(olgMock)) - .extracting(CurrentLimitsDataBuilder::build) .isEqualTo(CurrentLimitsData.builder().id("my id").applicability(null).permanentLimit(0.0).temporaryLimits( List.of(TemporaryLimitData.builder().acceptableDuration(123).name("testLimit").value(456.789).build())).build()); Mockito.verify(olgMock, Mockito.times(1)).getCurrentLimits(); @@ -179,29 +174,25 @@ class MergeCurrentLimitsTest { @ParameterizedTest @MethodSource("testDtoHasLimitAfterMapData") void testDtoHasLimitAfterMapped(final boolean expected, final double pl, final Collection tl) { - assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(new OperationalLimitsGroupDtoTest("id", new CurrentLimitsDtoTest(pl, tl)))).as("DTO builder") - .extracting(CurrentLimitsDataBuilder::build).as("DTO") - .extracting(CurrentLimitsData::hasLimits, InstanceOfAssertFactories.BOOLEAN).as("hasLimits") - .isEqualTo(expected); + assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(new OperationalLimitsGroupDtoTest("id", new CurrentLimitsDtoTest(pl, tl)))).as("DTO") + .extracting(CurrentLimitsData::hasLimits, InstanceOfAssertFactories.BOOLEAN).as("hasLimits") + .isEqualTo(expected); } private static Stream testDtoHasLimitAfterMapData() { return Stream.of( - Arguments.of(false, Double.NaN, null), - Arguments.of(false, Double.NaN, List.of()), - Arguments.of(true, Double.NaN, List.of(new TemporaryLimitDtoTest("tl", 1.2, 123))), - Arguments.of(true, 0.123, null), - Arguments.of(true, 0.123, List.of()), - Arguments.of(true, 0.123, List.of(new TemporaryLimitDtoTest("tl", 1.2, 123))) + Arguments.of(false, Double.NaN, null), + Arguments.of(false, Double.NaN, List.of()), + Arguments.of(true, Double.NaN, List.of(new TemporaryLimitDtoTest("tl", 1.2, 123))), + Arguments.of(true, 0.123, null), + Arguments.of(true, 0.123, List.of()), + Arguments.of(true, 0.123, List.of(new TemporaryLimitDtoTest("tl", 1.2, 123))) ); } @ParameterizedTest(name = ParameterizedTest.INDEX_PLACEHOLDER) @MethodSource("mergeCurrentLimitsTestData") - void shouldNotThrow( - final Collection olg1, - final Collection olg2, - final List expected) { + void shouldNotThrow(final Collection olg1, final Collection olg2, final List expected) { AtomicReference> results = new AtomicReference<>(); ElementUtils.mergeCurrentLimits(olg1, olg2, results::set); assertThat(results.get()).as("Result").isEqualTo(expected); From 15f4fee2385cd717f0bdc81b0d71249f55c0abb5 Mon Sep 17 00:00:00 2001 From: Tristan Chuine Date: Mon, 11 Aug 2025 15:21:57 +0200 Subject: [PATCH 06/11] fix tests of `operationalLimitsGroupToMapDataCurrentLimits()` --- .../network/map/utils/ElementUtilsTest.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/test/java/org/gridsuite/network/map/utils/ElementUtilsTest.java b/src/test/java/org/gridsuite/network/map/utils/ElementUtilsTest.java index 132dbd45..64ee3b26 100644 --- a/src/test/java/org/gridsuite/network/map/utils/ElementUtilsTest.java +++ b/src/test/java/org/gridsuite/network/map/utils/ElementUtilsTest.java @@ -76,7 +76,8 @@ void nanPermLimitAndNullTempLimit() { final var olgMock = Mockito.spy(new OperationalLimitsGroupDtoTest("id", clMock)); mocks.add(olgMock); assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(olgMock)).isNull(); - Mockito.verify(olgMock, Mockito.times(1)).getCurrentLimits(); + Mockito.verify(olgMock, Mockito.times(2)).getCurrentLimits(); + Mockito.verify(olgMock).getId(); Mockito.verify(clMock).getPermanentLimit(); Mockito.verify(clMock).getTemporaryLimits(); } @@ -88,7 +89,8 @@ void nanPermLimitAndEmptyTempLimit() { final var olgMock = Mockito.spy(new OperationalLimitsGroupDtoTest("id", clMock)); mocks.add(olgMock); assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(olgMock)).isNull(); - Mockito.verify(olgMock, Mockito.times(1)).getCurrentLimits(); + Mockito.verify(olgMock, Mockito.times(2)).getCurrentLimits(); + Mockito.verify(olgMock).getId(); Mockito.verify(clMock).getPermanentLimit(); Mockito.verify(clMock).getTemporaryLimits(); } @@ -104,7 +106,7 @@ void nanPermLimitAndNonEmptyTempLimit() { assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(olgMock)) .isEqualTo(CurrentLimitsData.builder().id("my id").applicability(null).permanentLimit(null).temporaryLimits( List.of(TemporaryLimitData.builder().acceptableDuration(123).name("testLimit").value(456.789).build())).build()); - Mockito.verify(olgMock, Mockito.times(1)).getCurrentLimits(); + Mockito.verify(olgMock, Mockito.times(2)).getCurrentLimits(); Mockito.verify(olgMock).getId(); Mockito.verify(clMock).getPermanentLimit(); Mockito.verify(clMock, Mockito.times(2)).getTemporaryLimits(); @@ -121,7 +123,7 @@ void nonNanPermLimitAndNullTempLimit() { mocks.add(olgMock); assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(olgMock)) .isEqualTo(CurrentLimitsData.builder().id("my id").applicability(null).permanentLimit(0.123).temporaryLimits(null).build()); - Mockito.verify(olgMock, Mockito.times(1)).getCurrentLimits(); + Mockito.verify(olgMock, Mockito.times(2)).getCurrentLimits(); Mockito.verify(olgMock).getId(); Mockito.verify(clMock, Mockito.times(2)).getPermanentLimit(); Mockito.verify(clMock).getTemporaryLimits(); @@ -135,7 +137,7 @@ void nonNanPermLimitAndEmptyTempLimit() { mocks.add(olgMock); assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(olgMock)) .isEqualTo(CurrentLimitsData.builder().id("my id").applicability(null).permanentLimit(0.123).temporaryLimits(null).build()); - Mockito.verify(olgMock, Mockito.times(1)).getCurrentLimits(); + Mockito.verify(olgMock, Mockito.times(2)).getCurrentLimits(); Mockito.verify(olgMock).getId(); Mockito.verify(clMock, Mockito.times(2)).getPermanentLimit(); Mockito.verify(clMock).getTemporaryLimits(); @@ -152,7 +154,7 @@ void nonNanPermLimitAndNonEmptyTempLimit() { assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(olgMock)) .isEqualTo(CurrentLimitsData.builder().id("my id").applicability(null).permanentLimit(0.0).temporaryLimits( List.of(TemporaryLimitData.builder().acceptableDuration(123).name("testLimit").value(456.789).build())).build()); - Mockito.verify(olgMock, Mockito.times(1)).getCurrentLimits(); + Mockito.verify(olgMock, Mockito.times(2)).getCurrentLimits(); Mockito.verify(olgMock).getId(); Mockito.verify(clMock, Mockito.times(2)).getPermanentLimit(); Mockito.verify(clMock, Mockito.times(2)).getTemporaryLimits(); From 1245adb93100462abc4e08ed7be505b1ed931d84 Mon Sep 17 00:00:00 2001 From: Tristan Chuine Date: Mon, 11 Aug 2025 15:24:54 +0200 Subject: [PATCH 07/11] remove dead code in conditions --- .../map/dto/common/CurrentLimitsData.java | 5 --- .../network/map/dto/utils/ElementUtils.java | 26 +++++---------- .../network/map/utils/ElementUtilsTest.java | 33 +++---------------- 3 files changed, 13 insertions(+), 51 deletions(-) diff --git a/src/main/java/org/gridsuite/network/map/dto/common/CurrentLimitsData.java b/src/main/java/org/gridsuite/network/map/dto/common/CurrentLimitsData.java index b9b199fb..9bcf23c4 100644 --- a/src/main/java/org/gridsuite/network/map/dto/common/CurrentLimitsData.java +++ b/src/main/java/org/gridsuite/network/map/dto/common/CurrentLimitsData.java @@ -10,7 +10,6 @@ import lombok.Builder; import lombok.EqualsAndHashCode; import lombok.Getter; -import org.springframework.util.CollectionUtils; import java.util.List; import java.util.Objects; @@ -42,10 +41,6 @@ public enum Applicability { SIDE2, } - public boolean hasLimits() { - return permanentLimit != null && !Double.isNaN(permanentLimit) || !CollectionUtils.isEmpty(temporaryLimits); - } - public boolean limitsEquals(CurrentLimitsData other) { return Objects.equals(permanentLimit, other.permanentLimit) && Objects.equals(temporaryLimits, other.temporaryLimits); diff --git a/src/main/java/org/gridsuite/network/map/dto/utils/ElementUtils.java b/src/main/java/org/gridsuite/network/map/dto/utils/ElementUtils.java index fcd02bbf..eb2cb287 100644 --- a/src/main/java/org/gridsuite/network/map/dto/utils/ElementUtils.java +++ b/src/main/java/org/gridsuite/network/map/dto/utils/ElementUtils.java @@ -119,7 +119,7 @@ public static void mergeCurrentLimits(Collection operati // combine 2 sides in one list - // simple case : one of the arrays are empty + // simple case: one of the arrays are empty if (currentLimitsData2.isEmpty() && !currentLimitsData1.isEmpty()) { mergedLimitsData.addAll(currentLimitsData1); build.accept(mergedLimitsData); @@ -134,30 +134,22 @@ public static void mergeCurrentLimits(Collection operati // more complex case for (CurrentLimitsData limitsData : currentLimitsData1) { Optional l2 = currentLimitsData2.stream().filter(l -> l.getId().equals(limitsData.getId())).findFirst(); - if (l2.isPresent()) { CurrentLimitsData limitsData2 = l2.get(); - // Only side one has limits - if (limitsData.hasLimits() && !limitsData2.hasLimits()) { + // both sides have limits and limits are equals + if (limitsData.limitsEquals(limitsData2)) { + mergedLimitsData.add(copyCurrentLimitsData(limitsData, EQUIPMENT)); + // both sides have limits and are different: create 2 different limit sets + } else { + // Side 1 mergedLimitsData.add(limitsData); - // only side two has limits - } else if (limitsData2.hasLimits() && !limitsData.hasLimits()) { + // Side 2 mergedLimitsData.add(limitsData2); - } else { - // both sides have limits and limits are equals - if (limitsData.limitsEquals(limitsData2)) { - mergedLimitsData.add(copyCurrentLimitsData(limitsData, EQUIPMENT)); - // both side have limits and they are different : create 2 different limit sets - } else { - // Side 1 - mergedLimitsData.add(limitsData); - // Side 2 - mergedLimitsData.add(limitsData2); - } } // remove processed limits from side 2 currentLimitsData2.remove(l2.get()); } else { + // only one side has limits mergedLimitsData.add(limitsData); } } diff --git a/src/test/java/org/gridsuite/network/map/utils/ElementUtilsTest.java b/src/test/java/org/gridsuite/network/map/utils/ElementUtilsTest.java index 64ee3b26..0724e3a0 100644 --- a/src/test/java/org/gridsuite/network/map/utils/ElementUtilsTest.java +++ b/src/test/java/org/gridsuite/network/map/utils/ElementUtilsTest.java @@ -1,8 +1,6 @@ package org.gridsuite.network.map.utils; -import com.powsybl.iidm.network.LoadingLimits.TemporaryLimit; import com.powsybl.iidm.network.OperationalLimitsGroup; -import org.assertj.core.api.InstanceOfAssertFactories; import org.assertj.core.api.SoftAssertions; import org.assertj.core.api.WithAssertions; import org.assertj.core.api.junit.jupiter.SoftAssertionsExtension; @@ -13,7 +11,10 @@ import org.gridsuite.network.map.utils.MockDto.CurrentLimitsDtoTest; import org.gridsuite.network.map.utils.MockDto.OperationalLimitsGroupDtoTest; import org.gridsuite.network.map.utils.MockDto.TemporaryLimitDtoTest; -import org.junit.jupiter.api.*; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -168,30 +169,6 @@ void nonNanPermLimitAndNonEmptyTempLimit() { @Nested @DisplayName("fn mergeCurrentLimits(…, …, …)") class MergeCurrentLimitsTest { - /** - * This test check that after that the two input collections has been converted with {@link ElementUtils#operationalLimitsGroupToMapDataCurrentLimits(OperationalLimitsGroup)} - * that {@link CurrentLimitsData#hasLimits()} work as intended. - */ - @Order(1) - @ParameterizedTest - @MethodSource("testDtoHasLimitAfterMapData") - void testDtoHasLimitAfterMapped(final boolean expected, final double pl, final Collection tl) { - assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(new OperationalLimitsGroupDtoTest("id", new CurrentLimitsDtoTest(pl, tl)))).as("DTO") - .extracting(CurrentLimitsData::hasLimits, InstanceOfAssertFactories.BOOLEAN).as("hasLimits") - .isEqualTo(expected); - } - - private static Stream testDtoHasLimitAfterMapData() { - return Stream.of( - Arguments.of(false, Double.NaN, null), - Arguments.of(false, Double.NaN, List.of()), - Arguments.of(true, Double.NaN, List.of(new TemporaryLimitDtoTest("tl", 1.2, 123))), - Arguments.of(true, 0.123, null), - Arguments.of(true, 0.123, List.of()), - Arguments.of(true, 0.123, List.of(new TemporaryLimitDtoTest("tl", 1.2, 123))) - ); - } - @ParameterizedTest(name = ParameterizedTest.INDEX_PLACEHOLDER) @MethodSource("mergeCurrentLimitsTestData") void shouldNotThrow(final Collection olg1, final Collection olg2, final List expected) { @@ -308,7 +285,5 @@ void shouldThrowOnNanLimit(final SoftAssertions softly) { softly.assertThatNullPointerException().isThrownBy(() -> ElementUtils.mergeCurrentLimits(l1, l2, results::set)); softly.assertThatNullPointerException().isThrownBy(() -> ElementUtils.mergeCurrentLimits(l2, l1, results::set)); } - - // TODO what to do when one side has duplicate ID? } } From 4fe98eacfe7296e596adc0a9a9d290cc57394074 Mon Sep 17 00:00:00 2001 From: Tristan Chuine Date: Mon, 11 Aug 2025 15:31:28 +0200 Subject: [PATCH 08/11] Handle nullable values --- .../network/map/dto/utils/ElementUtils.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/gridsuite/network/map/dto/utils/ElementUtils.java b/src/main/java/org/gridsuite/network/map/dto/utils/ElementUtils.java index eb2cb287..7dae13ce 100644 --- a/src/main/java/org/gridsuite/network/map/dto/utils/ElementUtils.java +++ b/src/main/java/org/gridsuite/network/map/dto/utils/ElementUtils.java @@ -11,16 +11,13 @@ import com.powsybl.iidm.network.extensions.*; import com.powsybl.math.graph.TraversalType; import org.gridsuite.network.map.dto.common.*; +import org.gridsuite.network.map.dto.common.CurrentLimitsData.Applicability; import org.gridsuite.network.map.dto.definition.extension.*; import org.gridsuite.network.map.dto.definition.threewindingstransformer.ThreeWindingsTransformerTabInfos; +import org.springframework.lang.Nullable; import org.springframework.util.CollectionUtils; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import java.util.*; import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.Collectors; @@ -93,7 +90,7 @@ public static void buildCurrentLimits(Collection current } } - private static CurrentLimitsData copyCurrentLimitsData(CurrentLimitsData currentLimitsData, CurrentLimitsData.Applicability applicability) { + private static CurrentLimitsData copyCurrentLimitsData(CurrentLimitsData currentLimitsData, Applicability applicability) { return CurrentLimitsData.builder() .id(currentLimitsData.getId()) .applicability(applicability) @@ -111,11 +108,13 @@ public static void mergeCurrentLimits(Collection operati // Build temporary limit from side 1 and 2 List currentLimitsData1 = operationalLimitsGroups1.stream() - .map(currentLimitsData -> - ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(currentLimitsData, SIDE1)).toList(); + .map(currentLimitsData -> ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(currentLimitsData, SIDE1)) + .filter(Objects::nonNull) + .toList(); ArrayList currentLimitsData2 = new ArrayList<>(operationalLimitsGroups2.stream() - .map(currentLimitsData -> - ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(currentLimitsData, SIDE2)).toList()); + .map(currentLimitsData -> ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(currentLimitsData, SIDE2)) + .filter(Objects::nonNull) + .toList()); // combine 2 sides in one list @@ -253,7 +252,8 @@ public static CurrentLimitsData operationalLimitsGroupToMapDataCurrentLimits(Ope return operationalLimitsGroupToMapDataCurrentLimits(operationalLimitsGroup, null); } - public static CurrentLimitsData operationalLimitsGroupToMapDataCurrentLimits(OperationalLimitsGroup operationalLimitsGroup, CurrentLimitsData.Applicability applicability) { + @Nullable + public static CurrentLimitsData operationalLimitsGroupToMapDataCurrentLimits(OperationalLimitsGroup operationalLimitsGroup, Applicability applicability) { if (operationalLimitsGroup == null || operationalLimitsGroup.getCurrentLimits().isEmpty()) { return null; } From 08aa059738ef444fb5797c16df9f4d4ae92bebb0 Mon Sep 17 00:00:00 2001 From: Tristan Chuine Date: Mon, 11 Aug 2025 19:02:14 +0200 Subject: [PATCH 09/11] Refactor DTO build and `OperationalLimitsGroupToMapDataCurrentLimits` tests --- .../network/map/utils/ElementUtilsTest.java | 137 ++++++++---------- .../gridsuite/network/map/utils/MockDto.java | 85 +++++++++++ 2 files changed, 147 insertions(+), 75 deletions(-) diff --git a/src/test/java/org/gridsuite/network/map/utils/ElementUtilsTest.java b/src/test/java/org/gridsuite/network/map/utils/ElementUtilsTest.java index 0724e3a0..ba134788 100644 --- a/src/test/java/org/gridsuite/network/map/utils/ElementUtilsTest.java +++ b/src/test/java/org/gridsuite/network/map/utils/ElementUtilsTest.java @@ -1,6 +1,10 @@ package org.gridsuite.network.map.utils; import com.powsybl.iidm.network.OperationalLimitsGroup; +import jakarta.annotation.Nullable; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.tuple.Triple; import org.assertj.core.api.SoftAssertions; import org.assertj.core.api.WithAssertions; import org.assertj.core.api.junit.jupiter.SoftAssertionsExtension; @@ -11,10 +15,7 @@ import org.gridsuite.network.map.utils.MockDto.CurrentLimitsDtoTest; import org.gridsuite.network.map.utils.MockDto.OperationalLimitsGroupDtoTest; import org.gridsuite.network.map.utils.MockDto.TemporaryLimitDtoTest; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.*; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -25,7 +26,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; -import java.util.Optional; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.stream.Stream; @@ -36,7 +36,7 @@ class ElementUtilsTest implements WithAssertions { @Nested @DisplayName("fn operationalLimitsGroupToMapDataCurrentLimits(…)") class OperationalLimitsGroupToMapDataCurrentLimits { - private Collection mocks = new ArrayList<>(); + private final List mocks = new ArrayList<>(); @AfterEach void setDown() { @@ -57,111 +57,98 @@ void setDown() { * operationalLimitsGroup(currentLimits={PermanentLimit=_non_NaN_, TemporaryLimits=[any], *=any}, *=any) -> (id=*, applicability=null, permanentLimit=*, temporaryLimits=[any]) */ + @SafeVarargs + private CurrentLimitsData testResult(final String id, @Nullable final Double permanentLimit, @Nullable final Triple... temporaryLimits) { + final var dtoMocks = MockDto.buildOperationalLimitsGroup(true, id, permanentLimit, temporaryLimits); + final TemporaryLimitDtoTest tlMock = CollectionUtils.isEmpty(dtoMocks.getRight()) ? null : dtoMocks.getRight().getFirst(); + if (tlMock != null) { + mocks.add(tlMock); + } + final CurrentLimitsDtoTest clMock = dtoMocks.getMiddle(); + if (clMock != null) { + mocks.add(clMock); + } + final OperationalLimitsGroupDtoTest olgMock = dtoMocks.getLeft(); + if (olgMock != null) { + mocks.add(olgMock); + } + final CurrentLimitsData result = ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(olgMock); + if (id != null) { + Mockito.verify(olgMock, Mockito.times(permanentLimit == null ? 1 : 2)).getCurrentLimits(); + if (permanentLimit != null) { + Mockito.verify(olgMock).getId(); + Mockito.verify(clMock, Mockito.times(Double.isNaN(permanentLimit) ? 1 : 2)).getPermanentLimit(); + Mockito.verify(clMock, Mockito.times(ArrayUtils.isEmpty(temporaryLimits) ? 1 : 2)).getTemporaryLimits(); + if (ArrayUtils.isNotEmpty(temporaryLimits)) { + Mockito.verify(tlMock).getName(); + Mockito.verify(tlMock, Mockito.times(2)).getValue(); + Mockito.verify(tlMock, Mockito.times(2)).getAcceptableDuration(); + } + } + } + return result; + } + @Test + @Order(1) void nullInput() { - assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(null)).isNull(); + // null -> null + assertThat(testResult(null, null, (Triple[]) null)).isNull(); } @Test + @Order(2) void emptyCurrentLimits() { - final var mock = Mockito.spy(new OperationalLimitsGroupDtoTest("id", Optional.empty())); - mocks.add(mock); - assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(mock)).isNull(); - Mockito.verify(mock).getCurrentLimits(); + // OLG("id", null) -> null + assertThat(testResult("id", null /*,new Triple[0]*/)).isNull(); } @Test + @Order(3) void nanPermLimitAndNullTempLimit() { - final var clMock = Mockito.spy(new CurrentLimitsDtoTest(Double.NaN, null)); - mocks.add(clMock); - final var olgMock = Mockito.spy(new OperationalLimitsGroupDtoTest("id", clMock)); - mocks.add(olgMock); - assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(olgMock)).isNull(); - Mockito.verify(olgMock, Mockito.times(2)).getCurrentLimits(); - Mockito.verify(olgMock).getId(); - Mockito.verify(clMock).getPermanentLimit(); - Mockito.verify(clMock).getTemporaryLimits(); + // OLG("id", CL(NaN, null)) -> null + assertThat(testResult("id", Double.NaN, (Triple[]) null)).isNull(); } @Test + @Order(4) void nanPermLimitAndEmptyTempLimit() { - final var clMock = Mockito.spy(new CurrentLimitsDtoTest(Double.NaN, List.of())); - mocks.add(clMock); - final var olgMock = Mockito.spy(new OperationalLimitsGroupDtoTest("id", clMock)); - mocks.add(olgMock); - assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(olgMock)).isNull(); - Mockito.verify(olgMock, Mockito.times(2)).getCurrentLimits(); - Mockito.verify(olgMock).getId(); - Mockito.verify(clMock).getPermanentLimit(); - Mockito.verify(clMock).getTemporaryLimits(); + // OLG("id", CL(NaN, [])) -> null + assertThat(testResult("id", Double.NaN /*,new Triple[0]*/)).isNull(); } @Test + @Order(5) void nanPermLimitAndNonEmptyTempLimit() { - final var tlMock = Mockito.spy(new TemporaryLimitDtoTest("testLimit", 456.789, 123)); - mocks.add(tlMock); - final var clMock = Mockito.spy(new CurrentLimitsDtoTest(Double.NaN, List.of(tlMock))); - mocks.add(clMock); - final var olgMock = Mockito.spy(new OperationalLimitsGroupDtoTest("my id", clMock)); - mocks.add(olgMock); - assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(olgMock)) + // OLG("my id", CL(Nan, [TL("testLimit", 456.789, 123)])) -> CLD("my id", null, [TLD("testLimit", 456.789, 123)], null) + assertThat(testResult("my id", Double.NaN, Triple.of("testLimit", 456.789, 123))) .isEqualTo(CurrentLimitsData.builder().id("my id").applicability(null).permanentLimit(null).temporaryLimits( List.of(TemporaryLimitData.builder().acceptableDuration(123).name("testLimit").value(456.789).build())).build()); - Mockito.verify(olgMock, Mockito.times(2)).getCurrentLimits(); - Mockito.verify(olgMock).getId(); - Mockito.verify(clMock).getPermanentLimit(); - Mockito.verify(clMock, Mockito.times(2)).getTemporaryLimits(); - Mockito.verify(tlMock, Mockito.times(2)).getAcceptableDuration(); - Mockito.verify(tlMock).getName(); - Mockito.verify(tlMock, Mockito.times(2)).getValue(); } @Test + @Order(6) void nonNanPermLimitAndNullTempLimit() { - final var clMock = Mockito.spy(new CurrentLimitsDtoTest(0.123, null)); - mocks.add(clMock); - final var olgMock = Mockito.spy(new OperationalLimitsGroupDtoTest("my id", clMock)); - mocks.add(olgMock); - assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(olgMock)) + // OLG("my id", CL(0.123, null)) -> CLD("my id", 0.123, null, null) + assertThat(testResult("my id", 0.123, (Triple[]) null)) .isEqualTo(CurrentLimitsData.builder().id("my id").applicability(null).permanentLimit(0.123).temporaryLimits(null).build()); - Mockito.verify(olgMock, Mockito.times(2)).getCurrentLimits(); - Mockito.verify(olgMock).getId(); - Mockito.verify(clMock, Mockito.times(2)).getPermanentLimit(); - Mockito.verify(clMock).getTemporaryLimits(); } @Test + @Order(7) void nonNanPermLimitAndEmptyTempLimit() { - final var clMock = Mockito.spy(new CurrentLimitsDtoTest(0.123, List.of())); - mocks.add(clMock); - final var olgMock = Mockito.spy(new OperationalLimitsGroupDtoTest("my id", clMock)); - mocks.add(olgMock); - assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(olgMock)) + // OLG("my id", CL(0.123, [])) -> CLD("my id", 0.123, [], null) + assertThat(testResult("my id", 0.123 /*,new Triple[0]*/)) .isEqualTo(CurrentLimitsData.builder().id("my id").applicability(null).permanentLimit(0.123).temporaryLimits(null).build()); - Mockito.verify(olgMock, Mockito.times(2)).getCurrentLimits(); - Mockito.verify(olgMock).getId(); - Mockito.verify(clMock, Mockito.times(2)).getPermanentLimit(); - Mockito.verify(clMock).getTemporaryLimits(); } @Test + @Order(8) void nonNanPermLimitAndNonEmptyTempLimit() { - final var tlMock = Mockito.spy(new TemporaryLimitDtoTest("testLimit", 456.789, 123)); - mocks.add(tlMock); - final var clMock = Mockito.spy(new CurrentLimitsDtoTest(0.0, List.of(tlMock))); - mocks.add(clMock); - final var olgMock = Mockito.spy(new OperationalLimitsGroupDtoTest("my id", clMock)); - mocks.add(olgMock); - assertThat(ElementUtils.operationalLimitsGroupToMapDataCurrentLimits(olgMock)) + // OLG("my id", CL(0.0, [TL("testLimit", 456.789, 123)])) -> CLD("my id", 0.0, [TLD("testLimit", 456.789, 123)], null) + assertThat(testResult("my id", 0.0, Triple.of("testLimit", 456.789, 123))) .isEqualTo(CurrentLimitsData.builder().id("my id").applicability(null).permanentLimit(0.0).temporaryLimits( List.of(TemporaryLimitData.builder().acceptableDuration(123).name("testLimit").value(456.789).build())).build()); - Mockito.verify(olgMock, Mockito.times(2)).getCurrentLimits(); - Mockito.verify(olgMock).getId(); - Mockito.verify(clMock, Mockito.times(2)).getPermanentLimit(); - Mockito.verify(clMock, Mockito.times(2)).getTemporaryLimits(); - Mockito.verify(tlMock, Mockito.times(2)).getAcceptableDuration(); - Mockito.verify(tlMock).getName(); - Mockito.verify(tlMock, Mockito.times(2)).getValue(); } } diff --git a/src/test/java/org/gridsuite/network/map/utils/MockDto.java b/src/test/java/org/gridsuite/network/map/utils/MockDto.java index 4ee57e7e..c022dc37 100644 --- a/src/test/java/org/gridsuite/network/map/utils/MockDto.java +++ b/src/test/java/org/gridsuite/network/map/utils/MockDto.java @@ -2,20 +2,101 @@ import com.powsybl.iidm.network.*; import com.powsybl.iidm.network.LoadingLimits.TemporaryLimit; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import lombok.AllArgsConstructor; import lombok.Data; import lombok.experimental.Accessors; import org.apache.commons.lang3.NotImplementedException; +import org.apache.commons.lang3.function.TriConsumer; +import org.apache.commons.lang3.tuple.Triple; +import org.mockito.Mockito; import java.util.Collection; +import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; public final class MockDto { private MockDto() { throw new InstantiationError("Utility Class cannot be instantiated."); } + /** @see #buildOperationalLimitsGroup(TriConsumer, String, Double, List) */ + @Nonnull + @SafeVarargs + public static Triple> buildOperationalLimitsGroup( + final boolean mockAndPeek, + @Nullable final String id, + @Nullable final Double permanentLimit, + @Nullable final Triple... temporaryLimits) { + final AtomicReference> tlMock = new AtomicReference<>(); + final AtomicReference clMock = new AtomicReference<>(); + final OperationalLimitsGroupDtoTest dto = buildOperationalLimitsGroup(!mockAndPeek ? null : (olg, cl, tls) -> { + tlMock.set(tls); + clMock.set(cl); + }, id, permanentLimit, temporaryLimits); + return Triple.of(dto, clMock.get(), tlMock.get()); + } + + /** @see #buildOperationalLimitsGroup(TriConsumer, String, Double, List) */ + @Nullable + @SafeVarargs + public static OperationalLimitsGroupDtoTest buildOperationalLimitsGroup( + @Nullable final TriConsumer> mockAndPeek, + @Nullable final String id, + @Nullable final Double permanentLimit, + @Nullable final Triple... temporaryLimits) { + return buildOperationalLimitsGroup(mockAndPeek, id, permanentLimit, temporaryLimits == null ? null : List.of(temporaryLimits)); + } + + /** + * Build the different cases of {@link OperationalLimitsGroup}: + * + * + * + * + * + * + *
id permanentLimittemporaryLimitsReturn
null * * null
<string>null * new OLG(id, null)
<string><double>null new OLG(id, new CL(permanentLimits, null))
<string><double>[*] new OLG(id, new CL(permanentLimits, temporaryLimits::map(new TL(...)))
+ * @param mockAndPeek If is not {@code null}, the dto will be {@link Mockito#spy(Object) mocked} and the consumer will be called, else just return the dto. + * @return The DTO, possibly mocked. + * @see TemporaryLimitDtoTest + * @see CurrentLimitsDtoTest + * @see OperationalLimitsGroupDtoTest + */ + @Nullable + public static OperationalLimitsGroupDtoTest buildOperationalLimitsGroup( + @Nullable final TriConsumer> mockAndPeek, + @Nullable final String id, + @Nullable final Double permanentLimit, + @Nullable final List> temporaryLimits) { + OperationalLimitsGroupDtoTest oDto = null; + CurrentLimitsDtoTest cDto = null; + List tDtos = null; + if (id != null) { + if (permanentLimit != null) { + tDtos = temporaryLimits == null ? null : temporaryLimits.stream() + .map(t -> new TemporaryLimitDtoTest(t.getLeft(), t.getMiddle(), t.getRight())) + .map(tl -> mockAndPeek == null ? tl : Mockito.spy(tl)) + .toList(); + cDto = CurrentLimitsDtoTest.build(permanentLimit, tDtos); + if (mockAndPeek != null) { + cDto = Mockito.spy(cDto); + } + } + oDto = new OperationalLimitsGroupDtoTest(id, cDto); + if (mockAndPeek != null) { + oDto = Mockito.spy(oDto); + } + } + if (mockAndPeek != null) { + mockAndPeek.accept(oDto, cDto, tDtos); + } + return oDto; + } + @Data @AllArgsConstructor public static class TemporaryLimitDtoTest implements TemporaryLimit { @@ -36,6 +117,10 @@ public static class CurrentLimitsDtoTest implements CurrentLimits { private double permanentLimit; private Collection temporaryLimits; + public static CurrentLimitsDtoTest build(double permanentLimit, @Nullable Collection temporaryLimits) { + return new CurrentLimitsDtoTest(permanentLimit, temporaryLimits == null ? null : List.copyOf(temporaryLimits)); // generic collections are invariant + } + @Override public TemporaryLimit getTemporaryLimit(int acceptableDuration) { throw new NotImplementedException("Not supported yet."); From 98db2af6d6adc8a178433af074e03acb84043559 Mon Sep 17 00:00:00 2001 From: Tristan Chuine Date: Mon, 11 Aug 2025 22:52:30 +0200 Subject: [PATCH 10/11] Refactor & add test of/to `MergeCurrentLimitsTest` --- .../map/dto/common/CurrentLimitsData.java | 2 + .../map/dto/common/TemporaryLimitData.java | 2 + .../network/map/utils/ElementUtilsTest.java | 165 +++++++----------- 3 files changed, 66 insertions(+), 103 deletions(-) diff --git a/src/main/java/org/gridsuite/network/map/dto/common/CurrentLimitsData.java b/src/main/java/org/gridsuite/network/map/dto/common/CurrentLimitsData.java index 9bcf23c4..c90e606b 100644 --- a/src/main/java/org/gridsuite/network/map/dto/common/CurrentLimitsData.java +++ b/src/main/java/org/gridsuite/network/map/dto/common/CurrentLimitsData.java @@ -10,6 +10,7 @@ import lombok.Builder; import lombok.EqualsAndHashCode; import lombok.Getter; +import lombok.ToString; import java.util.List; import java.util.Objects; @@ -21,6 +22,7 @@ @Builder @Getter @EqualsAndHashCode +@ToString public class CurrentLimitsData { // may be null in case we just need the selected limit set and don't really need its name/id @JsonInclude(JsonInclude.Include.NON_NULL) diff --git a/src/main/java/org/gridsuite/network/map/dto/common/TemporaryLimitData.java b/src/main/java/org/gridsuite/network/map/dto/common/TemporaryLimitData.java index 3c5e1a74..8c0436d2 100644 --- a/src/main/java/org/gridsuite/network/map/dto/common/TemporaryLimitData.java +++ b/src/main/java/org/gridsuite/network/map/dto/common/TemporaryLimitData.java @@ -10,6 +10,7 @@ import lombok.Builder; import lombok.EqualsAndHashCode; import lombok.Getter; +import lombok.ToString; /** * @author David Braquart @@ -17,6 +18,7 @@ @Builder @Getter @EqualsAndHashCode +@ToString public class TemporaryLimitData { private String name; diff --git a/src/test/java/org/gridsuite/network/map/utils/ElementUtilsTest.java b/src/test/java/org/gridsuite/network/map/utils/ElementUtilsTest.java index ba134788..e7cab1aa 100644 --- a/src/test/java/org/gridsuite/network/map/utils/ElementUtilsTest.java +++ b/src/test/java/org/gridsuite/network/map/utils/ElementUtilsTest.java @@ -1,11 +1,9 @@ package org.gridsuite.network.map.utils; import com.powsybl.iidm.network.OperationalLimitsGroup; -import jakarta.annotation.Nullable; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.tuple.Triple; -import org.assertj.core.api.SoftAssertions; import org.assertj.core.api.WithAssertions; import org.assertj.core.api.junit.jupiter.SoftAssertionsExtension; import org.gridsuite.network.map.dto.common.CurrentLimitsData; @@ -23,11 +21,14 @@ import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; +import javax.annotation.Nullable; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Optional; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; +import java.util.function.Function; import java.util.stream.Stream; @ExtendWith({ SoftAssertionsExtension.class, MockitoExtension.class }) @@ -156,121 +157,79 @@ void nonNanPermLimitAndNonEmptyTempLimit() { @Nested @DisplayName("fn mergeCurrentLimits(…, …, …)") class MergeCurrentLimitsTest { + private static OperationalLimitsGroupDtoTest buildParam(final boolean withPL, final boolean withTL) { + if (!withTL) { + return MockDto.buildOperationalLimitsGroup(null, "group1", withPL ? 220.0 : Double.NaN); + } + return MockDto.buildOperationalLimitsGroup(null, "group1", withPL ? 220.0 : Double.NaN, Triple.of("temporary1", 50.0, 100), Triple.of("temporary2", 70.0, 150)); + } + + private static CurrentLimitsData buildResult(final boolean withPL, final boolean withTL, @Nullable final Boolean isSide1or2) { + return CurrentLimitsData.builder().id("group1").permanentLimit(withPL ? 220.0 : null).temporaryLimits(!withTL ? null : List.of( + TemporaryLimitData.builder().acceptableDuration(100).name("temporary1").value(50.0).build(), + TemporaryLimitData.builder().acceptableDuration(150).name("temporary2").value(70.0).build() + )).applicability(isSide1or2 == null ? Applicability.EQUIPMENT : (isSide1or2 ? Applicability.SIDE1 : Applicability.SIDE2)) + .build(); + } + @ParameterizedTest(name = ParameterizedTest.INDEX_PLACEHOLDER) - @MethodSource("mergeCurrentLimitsTestData") + @MethodSource({"mergeCurrentLimitsTestData", "mergeCurrentLimitsTestDataSwappable"}) // test al the combinations possible for the inputs void shouldNotThrow(final Collection olg1, final Collection olg2, final List expected) { + System.out.print("L1: "); + System.out.println(olg1); + System.out.print("L2: "); + System.out.println(olg2); + System.out.print("Expect: "); + System.out.println(expected); AtomicReference> results = new AtomicReference<>(); ElementUtils.mergeCurrentLimits(olg1, olg2, results::set); assertThat(results.get()).as("Result").isEqualTo(expected); } private static Stream mergeCurrentLimitsTestData() { + // particular cases (p1&p2 the same if swapped) return Stream.of( - // 1 - Arguments.of(List.of( - new OperationalLimitsGroupDtoTest("group1", new CurrentLimitsDtoTest(220.0, List.of(new TemporaryLimitDtoTest("temporary1", 50.0, 100), new TemporaryLimitDtoTest("temporary2", 70.0, 150)))) - ), List.of( - new OperationalLimitsGroupDtoTest("group1", new CurrentLimitsDtoTest(220.0, List.of(new TemporaryLimitDtoTest("temporary1", 50.0, 100), new TemporaryLimitDtoTest("temporary2", 70.0, 150)))) - ), List.of( - CurrentLimitsData.builder().id("group1").permanentLimit(220.0).temporaryLimits(List.of( - TemporaryLimitData.builder().acceptableDuration(100).name("temporary1").value(50.0).build(), - TemporaryLimitData.builder().acceptableDuration(150).name("temporary2").value(70.0).build() - )).applicability(Applicability.EQUIPMENT).build() - )), - // 2 - Arguments.of(List.of(), List.of( - new OperationalLimitsGroupDtoTest("group1", new CurrentLimitsDtoTest(220.0, List.of(new TemporaryLimitDtoTest("temporary1", 50.0, 100), new TemporaryLimitDtoTest("temporary2", 70.0, 150)))) - ), List.of( - CurrentLimitsData.builder().id("group1").permanentLimit(220.0).temporaryLimits(List.of( - TemporaryLimitData.builder().acceptableDuration(100).name("temporary1").value(50.0).build(), - TemporaryLimitData.builder().acceptableDuration(150).name("temporary2").value(70.0).build() - )).applicability(Applicability.SIDE2).build() - )), + // 1 - same limits on both sides for an id + Arguments.of(List.of(buildParam(true, true)), List.of(buildParam(true, true)), List.of(buildResult(true, true, null))), + // 2 - with no limits on either side for an id + Arguments.of(List.of(buildParam(false, false)), List.of(buildParam(false, false)), null), // 3 - Arguments.of(List.of( - new OperationalLimitsGroupDtoTest("group1", new CurrentLimitsDtoTest(220.0, List.of(new TemporaryLimitDtoTest("temporary1", 50.0, 100), new TemporaryLimitDtoTest("temporary2", 70.0, 150)))) - ), List.of(), List.of( - CurrentLimitsData.builder().id("group1").permanentLimit(220.0).temporaryLimits(List.of( - TemporaryLimitData.builder().acceptableDuration(100).name("temporary1").value(50.0).build(), - TemporaryLimitData.builder().acceptableDuration(150).name("temporary2").value(70.0).build() - )).applicability(Applicability.SIDE1).build() - )), + Arguments.of(List.of(buildParam(true, false)), List.of(buildParam(true, false)), List.of(buildResult(true, false, null))), // 4 - Arguments.of(List.of( - new OperationalLimitsGroupDtoTest("group1", new CurrentLimitsDtoTest(220.0, List.of())) - ), List.of( - new OperationalLimitsGroupDtoTest("group1", new CurrentLimitsDtoTest(220.0, List.of(new TemporaryLimitDtoTest("temporary1", 50.0, 100), new TemporaryLimitDtoTest("temporary2", 70.0, 150)))) - ), List.of( - CurrentLimitsData.builder().id("group1").permanentLimit(220.0).temporaryLimits(List.of( - TemporaryLimitData.builder().acceptableDuration(100).name("temporary1").value(50.0).build(), - TemporaryLimitData.builder().acceptableDuration(150).name("temporary2").value(70.0).build() - )).applicability(Applicability.SIDE2).build() - )), - // 5 - Arguments.of(List.of( - new OperationalLimitsGroupDtoTest("group1", new CurrentLimitsDtoTest(Double.NaN, List.of())) - ), List.of( - new OperationalLimitsGroupDtoTest("group1", new CurrentLimitsDtoTest(220.0, List.of(new TemporaryLimitDtoTest("temporary1", 50.0, 100), new TemporaryLimitDtoTest("temporary2", 70.0, 150)))) - ), List.of( - CurrentLimitsData.builder().id("group1").permanentLimit(220.0).temporaryLimits(List.of( - TemporaryLimitData.builder().acceptableDuration(100).name("temporary1").value(50.0).build(), - TemporaryLimitData.builder().acceptableDuration(150).name("temporary2").value(70.0).build() - )).applicability(Applicability.SIDE2).build() - )), - // 6 - Arguments.of(List.of( - new OperationalLimitsGroupDtoTest("group1", new CurrentLimitsDtoTest(Double.NaN, List.of(new TemporaryLimitDtoTest("temporary1", 50.0, 100), new TemporaryLimitDtoTest("temporary2", 70.0, 150)))) - ), List.of( - new OperationalLimitsGroupDtoTest("group1", new CurrentLimitsDtoTest(220.0, List.of(new TemporaryLimitDtoTest("temporary1", 50.0, 100), new TemporaryLimitDtoTest("temporary2", 70.0, 150)))) - ), List.of( - CurrentLimitsData.builder().id("group1").permanentLimit(220.0).temporaryLimits(List.of( - TemporaryLimitData.builder().acceptableDuration(100).name("temporary1").value(50.0).build(), - TemporaryLimitData.builder().acceptableDuration(150).name("temporary2").value(70.0).build() - )).applicability(Applicability.SIDE2).build() - )), - // 7 - Arguments.of(List.of( - new OperationalLimitsGroupDtoTest("group1", new CurrentLimitsDtoTest(220.0, List.of(new TemporaryLimitDtoTest("temporary1", 50.0, 100), new TemporaryLimitDtoTest("temporary2", 70.0, 150)))) - ), List.of( - new OperationalLimitsGroupDtoTest("group1", new CurrentLimitsDtoTest(220.0, List.of())) - ), List.of( - CurrentLimitsData.builder().id("group1").permanentLimit(220.0).temporaryLimits(List.of( - TemporaryLimitData.builder().acceptableDuration(100).name("temporary1").value(50.0).build(), - TemporaryLimitData.builder().acceptableDuration(150).name("temporary2").value(70.0).build() - )).applicability(Applicability.SIDE1).build() - )), - // 8 - Arguments.of(List.of( - new OperationalLimitsGroupDtoTest("group1", new CurrentLimitsDtoTest(220.0, List.of(new TemporaryLimitDtoTest("temporary1", 50.0, 100), new TemporaryLimitDtoTest("temporary2", 70.0, 150)))) - ), List.of( - new OperationalLimitsGroupDtoTest("group1", new CurrentLimitsDtoTest(Double.NaN, List.of())) - ), List.of( - CurrentLimitsData.builder().id("group1").permanentLimit(220.0).temporaryLimits(List.of( - TemporaryLimitData.builder().acceptableDuration(100).name("temporary1").value(50.0).build(), - TemporaryLimitData.builder().acceptableDuration(150).name("temporary2").value(70.0).build() - )).applicability(Applicability.SIDE1).build() - )), - // 9 - Arguments.of(List.of( - new OperationalLimitsGroupDtoTest("group1", new CurrentLimitsDtoTest(220.0, List.of(new TemporaryLimitDtoTest("temporary1", 50.0, 100), new TemporaryLimitDtoTest("temporary2", 70.0, 150)))) - ), List.of( - new OperationalLimitsGroupDtoTest("group1", new CurrentLimitsDtoTest(Double.NaN, List.of(new TemporaryLimitDtoTest("temporary1", 50.0, 100), new TemporaryLimitDtoTest("temporary2", 70.0, 150)))) - ), List.of( - CurrentLimitsData.builder().id("group1").permanentLimit(220.0).temporaryLimits(List.of( - TemporaryLimitData.builder().acceptableDuration(100).name("temporary1").value(50.0).build(), - TemporaryLimitData.builder().acceptableDuration(150).name("temporary2").value(70.0).build() - )).applicability(Applicability.SIDE1).build() - )) + Arguments.of(List.of(buildParam(false, true)), List.of(buildParam(false, true)), List.of(buildResult(false, true, null))) ); } - @Test - void shouldThrowOnNanLimit(final SoftAssertions softly) { - final Collection l1 = List.of(new OperationalLimitsGroupDtoTest("group1", new CurrentLimitsDtoTest(Double.NaN, List.of(new TemporaryLimitDtoTest("temporary1", 50.0, 100), new TemporaryLimitDtoTest("temporary2", 70.0, 150))))); - final Collection l2 = List.of(new OperationalLimitsGroupDtoTest("group1", new CurrentLimitsDtoTest(220.0, List.of(new TemporaryLimitDtoTest("temporary1", 50.0, 100), new TemporaryLimitDtoTest("temporary2", 70.0, 150))))); - AtomicReference> results = new AtomicReference<>(); - softly.assertThatNullPointerException().isThrownBy(() -> ElementUtils.mergeCurrentLimits(l1, l2, results::set)); - softly.assertThatNullPointerException().isThrownBy(() -> ElementUtils.mergeCurrentLimits(l2, l1, results::set)); + private static Stream mergeCurrentLimitsTestDataSwappable() { + // cases with param1 and param2 swappable + // will map [l;r]->[(l,r)=s1, (r,l)=s2], so the pair is implicitly for case with side1 as result + return Stream.>>of( + // 5&6 - id present only on one side + Triple.of(null, buildParam(true, true), s2 -> buildResult(true, true, s2)), + // 7&8 + Triple.of(null, buildParam(true, false), s2 -> buildResult(true, false, s2)), + // 9&10 + Triple.of(null, buildParam(false, true), s2 -> buildResult(false, true, s2)), + // 10&11 - after dto conversion, the same as test n°2 + Triple.of(null, buildParam(false, false), s2 -> null), + // 12&13 -- TODO have 2 results on side2 + Triple.of(buildParam(true, true), buildParam(true, false), s2 -> null), + // 14&15 -- TODO have 2 results on both sides + Triple.of(buildParam(true, true), buildParam(false, true), s2 -> null), + // 16&17 -- TODO have 2 results on side1 + Triple.of(buildParam(true, true), buildParam(false, false), s2 -> buildResult(true, true, !s2)), + // 18&19 + Triple.of(buildParam(true, false), buildParam(false, true), s2 -> buildResult(true, true, !s2)), + // 20&21 -- TODO have 2 results on side2 + Triple.of(buildParam(true, false), buildParam(false, false), s2 -> buildResult(true, false, !s2)), + // 22&23 + Triple.of(buildParam(false, true), buildParam(false, false), s2 -> buildResult(false, true, !s2)) + ).mapMulti(((args, argsTests) -> { + final var arg1 = args.getLeft() == null ? List.of() : List.of(args.getLeft()); + final var arg2 = args.getMiddle() == null ? List.of() : List.of(args.getMiddle()); + argsTests.accept(Arguments.of(arg1, arg2, Optional.ofNullable(args.getRight().apply(false)).map(List::of).orElse(null))); + argsTests.accept(Arguments.of(arg2, arg1, Optional.ofNullable(args.getRight().apply(true)).map(List::of).orElse(null))); + })); } } } From 3f2d329642cd55de2f5e1d71aefdcf40cd6d1d5b Mon Sep 17 00:00:00 2001 From: Tristan Chuine Date: Tue, 12 Aug 2025 15:48:54 +0200 Subject: [PATCH 11/11] Finalize tests --- .../network/map/utils/ElementUtilsTest.java | 55 ++++++++++--------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/src/test/java/org/gridsuite/network/map/utils/ElementUtilsTest.java b/src/test/java/org/gridsuite/network/map/utils/ElementUtilsTest.java index e7cab1aa..ab4bcd7c 100644 --- a/src/test/java/org/gridsuite/network/map/utils/ElementUtilsTest.java +++ b/src/test/java/org/gridsuite/network/map/utils/ElementUtilsTest.java @@ -173,7 +173,7 @@ private static CurrentLimitsData buildResult(final boolean withPL, final boolean } @ParameterizedTest(name = ParameterizedTest.INDEX_PLACEHOLDER) - @MethodSource({"mergeCurrentLimitsTestData", "mergeCurrentLimitsTestDataSwappable"}) // test al the combinations possible for the inputs + @MethodSource({"mergeCurrentLimitsTestData", "mergeCurrentLimitsTestDataSwappable", "mergeCurrentLimitsTestDataNotMergeable"}) // test al the combinations possible for the inputs void shouldNotThrow(final Collection olg1, final Collection olg2, final List expected) { System.out.print("L1: "); System.out.println(olg1); @@ -201,35 +201,40 @@ private static Stream mergeCurrentLimitsTestData() { } private static Stream mergeCurrentLimitsTestDataSwappable() { - // cases with param1 and param2 swappable - // will map [l;r]->[(l,r)=s1, (r,l)=s2], so the pair is implicitly for case with side1 as result + // cases with param1 and param2 swappable + // will map [l;r]->[(l,r)=s1, (r,l)=s2], so the pair is implicitly for case with side1 as result return Stream.>>of( - // 5&6 - id present only on one side - Triple.of(null, buildParam(true, true), s2 -> buildResult(true, true, s2)), - // 7&8 - Triple.of(null, buildParam(true, false), s2 -> buildResult(true, false, s2)), - // 9&10 - Triple.of(null, buildParam(false, true), s2 -> buildResult(false, true, s2)), - // 10&11 - after dto conversion, the same as test n°2 - Triple.of(null, buildParam(false, false), s2 -> null), - // 12&13 -- TODO have 2 results on side2 - Triple.of(buildParam(true, true), buildParam(true, false), s2 -> null), - // 14&15 -- TODO have 2 results on both sides - Triple.of(buildParam(true, true), buildParam(false, true), s2 -> null), - // 16&17 -- TODO have 2 results on side1 - Triple.of(buildParam(true, true), buildParam(false, false), s2 -> buildResult(true, true, !s2)), - // 18&19 - Triple.of(buildParam(true, false), buildParam(false, true), s2 -> buildResult(true, true, !s2)), - // 20&21 -- TODO have 2 results on side2 - Triple.of(buildParam(true, false), buildParam(false, false), s2 -> buildResult(true, false, !s2)), - // 22&23 - Triple.of(buildParam(false, true), buildParam(false, false), s2 -> buildResult(false, true, !s2)) - ).mapMulti(((args, argsTests) -> { + // 5 & 6 & 7 & 8 - id present only on one side, because OLG() with no limit is converted to null + Triple.of(buildParam(true, true), null, s2 -> buildResult(true, true, !s2)), + Triple.of(buildParam(true, true), buildParam(false, false), s2 -> buildResult(true, true, !s2)), + // 9 & 10 & 11 & 12 - id present only on one side, because OLG() with no limit is converted to null + Triple.of(buildParam(true, false), null, s2 -> buildResult(true, false, !s2)), + Triple.of(buildParam(true, false), buildParam(false, false), s2 -> buildResult(true, false, !s2)), + // 9 & 10 - id present only on one side, because OLG() with no limit is converted to null + Triple.of(buildParam(false, true), null, s2 -> buildResult(false, true, !s2)), + Triple.of(buildParam(false, true), buildParam(false, false), s2 -> buildResult(false, true, !s2)), + // 11 & 12 - after dto conversion, the same as test n°2 + Triple.of(buildParam(false, false), null, s2 -> null) + ).mapMulti((args, argsTests) -> { final var arg1 = args.getLeft() == null ? List.of() : List.of(args.getLeft()); final var arg2 = args.getMiddle() == null ? List.of() : List.of(args.getMiddle()); argsTests.accept(Arguments.of(arg1, arg2, Optional.ofNullable(args.getRight().apply(false)).map(List::of).orElse(null))); argsTests.accept(Arguments.of(arg2, arg1, Optional.ofNullable(args.getRight().apply(true)).map(List::of).orElse(null))); - })); + }); + } + + private static Stream mergeCurrentLimitsTestDataNotMergeable() { + return Stream.of( + // 13 & 14 + Arguments.of(List.of(buildParam(true, true)), List.of(buildParam(true, false)), + List.of(buildResult(true, true, true), buildResult(true, false, false))), + // 15 & 16 + Arguments.of(List.of(buildParam(true, true)), List.of(buildParam(false, true)), + List.of(buildResult(true, true, true), buildResult(false, true, false))), + // 17 & 18 + Arguments.of(List.of(buildParam(true, false)), List.of(buildParam(false, true)), + List.of(buildResult(true, false, true), buildResult(false, true, false))) + ); } } }