This is an automated email from the ASF dual-hosted git repository. sruehl pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-plc4x.git
The following commit(s) were added to refs/heads/master by this push: new 571338c [plc4j-ads] added some more tests for connections 571338c is described below commit 571338cebb411449f0f516ed6703f751a03b58e4 Author: Sebastian Rühl <sru...@apache.org> AuthorDate: Thu Nov 8 13:20:34 2018 +0100 [plc4j-ads] added some more tests for connections --- .../java/ads/connection/AdsTcpPlcConnection.java | 4 +- .../connection/AdsAbstractPlcConnectionTest.java | 352 +++++++++++---------- .../ads/connection/AdsTcpPlcConnectionTests.java | 151 +++++++-- 3 files changed, 320 insertions(+), 187 deletions(-) diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/connection/AdsTcpPlcConnection.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/connection/AdsTcpPlcConnection.java index 0f74c44..9a7b908 100644 --- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/connection/AdsTcpPlcConnection.java +++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/connection/AdsTcpPlcConnection.java @@ -147,7 +147,7 @@ public class AdsTcpPlcConnection extends AdsAbstractPlcConnection implements Plc .map(subscriptionPlcFieldEntry -> { String plcFieldName = subscriptionPlcFieldEntry.getKey(); SubscriptionPlcField subscriptionPlcField = subscriptionPlcFieldEntry.getValue(); - PlcField field = subscriptionPlcField.getPlcField(); + PlcField field = Objects.requireNonNull(subscriptionPlcField.getPlcField()); IndexGroup indexGroup; IndexOffset indexOffset; @@ -183,7 +183,7 @@ public class AdsTcpPlcConnection extends AdsAbstractPlcConnection implements Plc switch (subscriptionPlcField.getPlcSubscriptionType()) { case CYCLIC: transmissionMode = TransmissionMode.DefinedValues.ADSTRANS_SERVERCYCLE; - cycleTime = subscriptionPlcField.getDuration().orElseThrow(IllegalStateException::new).get(ChronoUnit.MILLIS); + cycleTime = TimeUnit.NANOSECONDS.toMillis(subscriptionPlcField.getDuration().orElseThrow(IllegalStateException::new).get(ChronoUnit.NANOS)); break; case CHANGE_OF_STATE: transmissionMode = TransmissionMode.DefinedValues.ADSTRANS_SERVERONCHA; diff --git a/plc4j/protocols/ads/src/test/java/org/apache/plc4x/java/ads/connection/AdsAbstractPlcConnectionTest.java b/plc4j/protocols/ads/src/test/java/org/apache/plc4x/java/ads/connection/AdsAbstractPlcConnectionTest.java index 9fe9648..c2f30cc 100644 --- a/plc4j/protocols/ads/src/test/java/org/apache/plc4x/java/ads/connection/AdsAbstractPlcConnectionTest.java +++ b/plc4j/protocols/ads/src/test/java/org/apache/plc4x/java/ads/connection/AdsAbstractPlcConnectionTest.java @@ -33,18 +33,18 @@ import org.apache.plc4x.java.ads.model.DirectAdsField; import org.apache.plc4x.java.ads.model.SymbolicAdsField; import org.apache.plc4x.java.api.exceptions.PlcRuntimeException; import org.apache.plc4x.java.api.messages.PlcFieldRequest; -import org.apache.plc4x.java.base.messages.PlcProprietaryResponse; import org.apache.plc4x.java.api.messages.PlcReadResponse; import org.apache.plc4x.java.api.messages.PlcWriteResponse; import org.apache.plc4x.java.base.connection.ChannelFactory; import org.apache.plc4x.java.base.messages.*; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.assertj.core.api.WithAssertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Answers; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.junit.jupiter.MockitoExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -54,12 +54,11 @@ import java.util.concurrent.*; import static org.apache.plc4x.java.base.util.Junit5Backport.assertThrows; import static org.junit.Assert.*; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; -@RunWith(MockitoJUnitRunner.class) @SuppressWarnings("unchecked") -public class AdsAbstractPlcConnectionTest { +@ExtendWith(MockitoExtension.class) +class AdsAbstractPlcConnectionTest implements WithAssertions { private static final Logger LOGGER = LoggerFactory.getLogger(AdsAbstractPlcConnectionTest.class); @@ -71,8 +70,8 @@ public class AdsAbstractPlcConnectionTest { @Mock(answer = Answers.RETURNS_DEEP_STUBS) private Channel channel; - @Before - public void setUp() throws Exception { + @BeforeEach + void setUp() throws Exception { SUT = new AdsAbstractPlcConnection(channelFactory, mock(AmsNetId.class), mock(AmsPort.class), mock(AmsNetId.class), mock(AmsPort.class)) { @Override protected ChannelHandler getChannelHandler(CompletableFuture<Void> sessionSetupCompleteFuture) { @@ -85,101 +84,98 @@ public class AdsAbstractPlcConnectionTest { SUT.connect(); } - @Test - public void lazyConstructor() { - AdsAbstractPlcConnection constructed = new AdsAbstractPlcConnection(channelFactory, mock(AmsNetId.class), mock(AmsPort.class)) { - @Override - protected ChannelHandler getChannelHandler(CompletableFuture<Void> sessionSetupCompleteFuture) { - return null; - } - }; - assertEquals(AdsAbstractPlcConnection.generateAMSNetId(), constructed.getSourceAmsNetId()); - assertEquals(AdsAbstractPlcConnection.generateAMSPort(), constructed.getSourceAmsPort()); - } - - @Test - public void getTargetAmsNetId() { - AmsNetId targetAmsNetId = SUT.getTargetAmsNetId(); - assertNotNull(targetAmsNetId); - } + @Nested + class Lifecycle { + @Test + void lazyConstructor() { + AdsAbstractPlcConnection constructed = new AdsAbstractPlcConnection(channelFactory, mock(AmsNetId.class), mock(AmsPort.class)) { + @Override + protected ChannelHandler getChannelHandler(CompletableFuture<Void> sessionSetupCompleteFuture) { + return null; + } + }; + assertEquals(AdsAbstractPlcConnection.generateAMSNetId(), constructed.getSourceAmsNetId()); + assertEquals(AdsAbstractPlcConnection.generateAMSPort(), constructed.getSourceAmsPort()); + } - @Test - public void getTargetAmsPort() { - AmsPort targetAmsPort = SUT.getTargetAmsPort(); - assertNotNull(targetAmsPort); + @Test + void close() throws Exception { + Map fieldMapping = (Map) FieldUtils.getDeclaredField(AdsAbstractPlcConnection.class, "fieldMapping", true).get(SUT); + fieldMapping.put(mock(SymbolicAdsField.class), mock(DirectAdsField.class)); + SUT.close(); + } } - @Test - public void getSourceAmsNetId() { - AmsNetId sourceAmsNetId = SUT.getSourceAmsNetId(); - assertNotNull(sourceAmsNetId); - } - @Test - public void getSourceAmsPort() { - AmsPort sourceAmsPort = SUT.getSourceAmsPort(); - assertNotNull(sourceAmsPort); - } + @Nested + class Communication { - @Test - public void read() { - CompletableFuture<PlcReadResponse> read = SUT.read(mock(InternalPlcReadRequest.class)); - assertNotNull(read); + @Test + void read() { + CompletableFuture<PlcReadResponse> read = SUT.read(mock(InternalPlcReadRequest.class)); + assertNotNull(read); - simulatePipelineError(() -> SUT.read(mock(InternalPlcReadRequest.class))); - } + simulatePipelineError(() -> SUT.read(mock(InternalPlcReadRequest.class))); + } - @Test - public void write() { - CompletableFuture<PlcWriteResponse> write = SUT.write(mock(InternalPlcWriteRequest.class)); - assertNotNull(write); + @Test + void write() { + CompletableFuture<PlcWriteResponse> write = SUT.write(mock(InternalPlcWriteRequest.class)); + assertNotNull(write); - simulatePipelineError(() -> SUT.write(mock(InternalPlcWriteRequest.class))); - } + simulatePipelineError(() -> SUT.write(mock(InternalPlcWriteRequest.class))); + } - @Test - public void send() { - CompletableFuture send = SUT.send(mock(InternalPlcProprietaryRequest.class)); - assertNotNull(send); + @Test + void send() { + CompletableFuture send = SUT.send(mock(InternalPlcProprietaryRequest.class)); + assertNotNull(send); - simulatePipelineError(() -> SUT.send(mock(InternalPlcProprietaryRequest.class))); - } + simulatePipelineError(() -> SUT.send(mock(InternalPlcProprietaryRequest.class))); + } - public void simulatePipelineError(FutureProducingTestRunnable futureProducingTestRunnable) { - ChannelFuture channelFuture = mock(ChannelFuture.class); - // Simulate error in the pipeline - when(channelFuture.addListener(any())).thenAnswer(invocation -> { - Future future = mock(Future.class); - when(future.isSuccess()).thenReturn(false); - when(future.cause()).thenReturn(new DummyException()); - GenericFutureListener genericFutureListener = invocation.getArgument(0); - genericFutureListener.operationComplete(future); - return mock(ChannelFuture.class); - }); - when(channel.writeAndFlush(any())).thenReturn(channelFuture); - assertThrows(DummyException.class, () -> { - CompletableFuture completableFuture = futureProducingTestRunnable.run(); - try { - completableFuture.get(3, TimeUnit.SECONDS); - fail("Should have thrown a ExecutionException"); - } catch (ExecutionException e) { - if (e.getCause() instanceof DummyException) { - throw (DummyException) e.getCause(); + void simulatePipelineError(FutureProducingTestRunnable futureProducingTestRunnable) { + ChannelFuture channelFuture = mock(ChannelFuture.class); + // Simulate error in the pipeline + when(channelFuture.addListener(any())).thenAnswer(invocation -> { + Future future = mock(Future.class); + when(future.isSuccess()).thenReturn(false); + when(future.cause()).thenReturn(new DummyException()); + GenericFutureListener genericFutureListener = invocation.getArgument(0); + genericFutureListener.operationComplete(future); + return mock(ChannelFuture.class); + }); + when(channel.writeAndFlush(any())).thenReturn(channelFuture); + assertThrows(DummyException.class, () -> { + CompletableFuture completableFuture = futureProducingTestRunnable.run(); + try { + completableFuture.get(3, TimeUnit.SECONDS); + fail("Should have thrown a ExecutionException"); + } catch (ExecutionException e) { + if (e.getCause() instanceof DummyException) { + throw (DummyException) e.getCause(); + } + throw e; } - throw e; - } - }); + }); + } } - @Test - public void mapFields() { - SUT.mapFields(mock(PlcFieldRequest.class)); - } + @Nested + class Symbolic { + + @BeforeEach + void setUp() { + SUT.clearMapping(); + } - @Test - public void mapField() { - // positive - { + @Test + void mapFields() { + SUT.mapFields(mock(PlcFieldRequest.class)); + } + + @Test + void mapSingleField() { when(channel.writeAndFlush(any(PlcRequestContainer.class))).then(invocation -> { PlcRequestContainer plcRequestContainer = invocation.getArgument(0); PlcProprietaryResponse plcProprietaryResponse = mock(InternalPlcProprietaryResponse.class, RETURNS_DEEP_STUBS); @@ -197,8 +193,9 @@ public class AdsAbstractPlcConnectionTest { SUT.clearMapping(); reset(channel); } - // negative - { + + @Test + void mapSingleFieldNegative() { when(channel.writeAndFlush(any(PlcRequestContainer.class))).then(invocation -> { PlcRequestContainer plcRequestContainer = invocation.getArgument(0); PlcProprietaryResponse plcProprietaryResponse = mock(InternalPlcProprietaryResponse.class, RETURNS_DEEP_STUBS); @@ -209,86 +206,117 @@ public class AdsAbstractPlcConnectionTest { return mock(ChannelFuture.class); }); - assertThrows(PlcRuntimeException.class, () -> SUT.mapFields(SymbolicAdsField.of("Main.byByte[0]"))); - verify(channel, times(0)).writeAndFlush(any(PlcRequestContainer.class)); + assertThatThrownBy(() -> SUT.mapFields(SymbolicAdsField.of("Main.byByte[0]:INT64"))) + .isInstanceOf(PlcRuntimeException.class) + .hasMessageMatching("Non error code received .*"); + verify(channel, times(1)).writeAndFlush(any(PlcRequestContainer.class)); SUT.clearMapping(); reset(channel); } } - @Test - public void generateAMSNetId() { - AmsNetId targetAmsNetId = AdsAbstractPlcConnection.generateAMSNetId(); - assertNotNull(targetAmsNetId); - } - @Test - public void generateAMSPort() { - AmsPort amsPort = AdsAbstractPlcConnection.generateAMSPort(); - assertNotNull(amsPort); - } + @Nested + class Misc { + @Test + void remainingMethods() { + assertThat(SUT.canRead()).isTrue(); + assertThat(SUT.canWrite()).isTrue(); + assertThat(SUT.readRequestBuilder()).isNotNull(); + assertThat(SUT.writeRequestBuilder()).isNotNull(); + } - @Test - public void close() throws Exception { - Map fieldMapping = (Map) FieldUtils.getDeclaredField(AdsAbstractPlcConnection.class, "fieldMapping", true).get(SUT); - fieldMapping.put(mock(SymbolicAdsField.class), mock(DirectAdsField.class)); - SUT.close(); - } + @Test + void testToString() { + String s = SUT.toString(); + assertNotNull(s); + } - @Test - public void getFromFuture() throws Exception { - runInThread(() -> { - CompletableFuture completableFuture = mock(CompletableFuture.class, RETURNS_DEEP_STUBS); - Object fromFuture = SUT.getFromFuture(completableFuture, 1); - assertNotNull(fromFuture); - }); - runInThread(() -> { - CompletableFuture completableFuture = mock(CompletableFuture.class, RETURNS_DEEP_STUBS); - when(completableFuture.get(anyLong(), any())).thenThrow(InterruptedException.class); - assertThrows(PlcRuntimeException.class, () -> SUT.getFromFuture(completableFuture, 1)); - }); - runInThread(() -> { - CompletableFuture completableFuture = mock(CompletableFuture.class, RETURNS_DEEP_STUBS); - when(completableFuture.get(anyLong(), any())).thenThrow(ExecutionException.class); - assertThrows(PlcRuntimeException.class, () -> SUT.getFromFuture(completableFuture, 1)); - }); - runInThread(() -> { - CompletableFuture completableFuture = mock(CompletableFuture.class, RETURNS_DEEP_STUBS); - when(completableFuture.get(anyLong(), any())).thenThrow(TimeoutException.class); - assertThrows(PlcRuntimeException.class, () -> SUT.getFromFuture(completableFuture, 1)); - }); - assertFalse("The current Thread should not be interrupted", Thread.currentThread().isInterrupted()); - } + @Test + void getTargetAmsNetId() { + AmsNetId targetAmsNetId = SUT.getTargetAmsNetId(); + assertNotNull(targetAmsNetId); + } - /** - * Runs tests steps in a dedicated {@link Thread} so a possible {@link InterruptedException} doesn't lead to a - * interrupt flag being set on the main Thread ({@link Thread#isInterrupted}). - * - * @param testRunnable a special {@link Runnable} which adds a {@code throws Exception} to the {@code run} signature. - * @throws InterruptedException when this {@link Thread} gets interrupted. - */ - public void runInThread(TestRunnable testRunnable) throws InterruptedException { - Thread thread = new Thread(() -> { - try { - testRunnable.run(); - } catch (Exception e) { - throw new RuntimeException(e); - } - }); - Queue<Throwable> uncaughtExceptions = new ConcurrentLinkedQueue<>(); - thread.setUncaughtExceptionHandler((t, e) -> uncaughtExceptions.add(e)); - thread.start(); - thread.join(); - if (!uncaughtExceptions.isEmpty()) { - uncaughtExceptions.forEach(throwable -> LOGGER.error("Assertion Error: Unexpected Exception", throwable)); - throw new AssertionError("Test failures. Check log"); + @Test + void getTargetAmsPort() { + AmsPort targetAmsPort = SUT.getTargetAmsPort(); + assertNotNull(targetAmsPort); + } + + @Test + void getSourceAmsNetId() { + AmsNetId sourceAmsNetId = SUT.getSourceAmsNetId(); + assertNotNull(sourceAmsNetId); + } + + @Test + void getSourceAmsPort() { + AmsPort sourceAmsPort = SUT.getSourceAmsPort(); + assertNotNull(sourceAmsPort); } - } - @Test - public void testToString() { - String s = SUT.toString(); - assertNotNull(s); + @Test + void generateAMSNetId() { + AmsNetId targetAmsNetId = AdsAbstractPlcConnection.generateAMSNetId(); + assertNotNull(targetAmsNetId); + } + + @Test + void generateAMSPort() { + AmsPort amsPort = AdsAbstractPlcConnection.generateAMSPort(); + assertNotNull(amsPort); + } + + @Test + void getFromFuture() throws Exception { + runInThread(() -> { + CompletableFuture completableFuture = mock(CompletableFuture.class, RETURNS_DEEP_STUBS); + Object fromFuture = SUT.getFromFuture(completableFuture, 1); + assertNotNull(fromFuture); + }); + runInThread(() -> { + CompletableFuture completableFuture = mock(CompletableFuture.class, RETURNS_DEEP_STUBS); + when(completableFuture.get(anyLong(), any())).thenThrow(InterruptedException.class); + assertThrows(PlcRuntimeException.class, () -> SUT.getFromFuture(completableFuture, 1)); + }); + runInThread(() -> { + CompletableFuture completableFuture = mock(CompletableFuture.class, RETURNS_DEEP_STUBS); + when(completableFuture.get(anyLong(), any())).thenThrow(ExecutionException.class); + assertThrows(PlcRuntimeException.class, () -> SUT.getFromFuture(completableFuture, 1)); + }); + runInThread(() -> { + CompletableFuture completableFuture = mock(CompletableFuture.class, RETURNS_DEEP_STUBS); + when(completableFuture.get(anyLong(), any())).thenThrow(TimeoutException.class); + assertThrows(PlcRuntimeException.class, () -> SUT.getFromFuture(completableFuture, 1)); + }); + assertFalse("The current Thread should not be interrupted", Thread.currentThread().isInterrupted()); + } + + /** + * Runs tests steps in a dedicated {@link Thread} so a possible {@link InterruptedException} doesn't lead to a + * interrupt flag being set on the main Thread ({@link Thread#isInterrupted}). + * + * @param testRunnable a special {@link Runnable} which adds a {@code throws Exception} to the {@code run} signature. + * @throws InterruptedException when this {@link Thread} gets interrupted. + */ + void runInThread(TestRunnable testRunnable) throws InterruptedException { + Thread thread = new Thread(() -> { + try { + testRunnable.run(); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + Queue<Throwable> uncaughtExceptions = new ConcurrentLinkedQueue<>(); + thread.setUncaughtExceptionHandler((t, e) -> uncaughtExceptions.add(e)); + thread.start(); + thread.join(); + if (!uncaughtExceptions.isEmpty()) { + uncaughtExceptions.forEach(throwable -> LOGGER.error("Assertion Error: Unexpected Exception", throwable)); + throw new AssertionError("Test failures. Check log"); + } + } } /** diff --git a/plc4j/protocols/ads/src/test/java/org/apache/plc4x/java/ads/connection/AdsTcpPlcConnectionTests.java b/plc4j/protocols/ads/src/test/java/org/apache/plc4x/java/ads/connection/AdsTcpPlcConnectionTests.java index 8c8fa01..b1025c9 100644 --- a/plc4j/protocols/ads/src/test/java/org/apache/plc4x/java/ads/connection/AdsTcpPlcConnectionTests.java +++ b/plc4j/protocols/ads/src/test/java/org/apache/plc4x/java/ads/connection/AdsTcpPlcConnectionTests.java @@ -20,35 +20,64 @@ under the License. package org.apache.plc4x.java.ads.connection; import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; import org.apache.commons.lang3.reflect.FieldUtils; +import org.apache.plc4x.java.ads.api.commands.AdsAddDeviceNotificationResponse; +import org.apache.plc4x.java.ads.api.commands.AdsDeleteDeviceNotificationResponse; +import org.apache.plc4x.java.ads.api.commands.AdsDeviceNotificationRequest; +import org.apache.plc4x.java.ads.api.commands.types.*; +import org.apache.plc4x.java.ads.api.generic.AmsHeader; import org.apache.plc4x.java.ads.api.generic.types.AmsNetId; import org.apache.plc4x.java.ads.api.generic.types.AmsPort; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.apache.plc4x.java.ads.model.AdsSubscriptionHandle; +import org.apache.plc4x.java.ads.model.DirectAdsField; +import org.apache.plc4x.java.ads.protocol.Plc4x2AdsProtocol; +import org.apache.plc4x.java.api.messages.PlcSubscriptionEvent; +import org.apache.plc4x.java.api.types.PlcSubscriptionType; +import org.apache.plc4x.java.base.messages.*; +import org.apache.plc4x.java.base.model.InternalPlcConsumerRegistration; +import org.apache.plc4x.java.base.model.SubscriptionPlcField; +import org.assertj.core.api.WithAssertions; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; import java.net.InetAddress; +import java.time.Duration; +import java.time.temporal.ChronoUnit; +import java.util.Collections; +import java.util.LinkedHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.RETURNS_DEEP_STUBS; -import static org.mockito.Mockito.mock; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; -public class AdsTcpPlcConnectionTests { +@ExtendWith(MockitoExtension.class) +class AdsTcpPlcConnectionTests implements WithAssertions { - private static final Logger LOGGER = LoggerFactory.getLogger(AdsTcpPlcConnectionTests.class); + AdsTcpPlcConnection SUT; - private AdsTcpPlcConnection SUT; + @Mock + Channel channelMock; - private Channel channelMock; + @Mock + PlcSubscriber plcSubscriber; - private ExecutorService executorService; + ExecutorService executorService; - @Before - public void setUp() throws Exception { + @BeforeEach + void setUp() throws Exception { SUT = AdsTcpPlcConnection.of(InetAddress.getByName("localhost"), AmsNetId.of("0.0.0.0.0.0"), AmsPort.of(13)); // TODO: Refactor this to use the TestChannelFactory instead. channelMock = mock(Channel.class, RETURNS_DEEP_STUBS); @@ -56,20 +85,96 @@ public class AdsTcpPlcConnectionTests { executorService = Executors.newFixedThreadPool(10); } - @After - public void tearDown() { + @AfterEach + void tearDown() { executorService.shutdownNow(); SUT = null; } - @Test - public void initialState() { - assertEquals(SUT.getTargetAmsNetId().toString(), "0.0.0.0.0.0"); - assertEquals(SUT.getTargetAmsPort().toString(), "13"); + @Nested + class Lifecycle { + @Test + void initialState() { + assertEquals(SUT.getTargetAmsNetId().toString(), "0.0.0.0.0.0"); + assertEquals(SUT.getTargetAmsPort().toString(), "13"); + } } - @Test - public void implementMeTestNewAndMissingMethods() { - // TODO: implement me + @Nested + class Subscription { + @Test + void subscribe() { + when(channelMock.writeAndFlush(any(PlcRequestContainer.class))).then(invocation -> { + PlcRequestContainer plcRequestContainer = invocation.getArgument(0); + PlcProprietaryResponse plcProprietaryResponse = mock(InternalPlcProprietaryResponse.class, RETURNS_DEEP_STUBS); + AdsAddDeviceNotificationResponse adsAddDeviceNotificationResponse = mock(AdsAddDeviceNotificationResponse.class, RETURNS_DEEP_STUBS); + when(adsAddDeviceNotificationResponse.getResult()).thenReturn(Result.of(0)); + when(adsAddDeviceNotificationResponse.getNotificationHandle()).thenReturn(NotificationHandle.of(1)); + when(plcProprietaryResponse.getResponse()).thenReturn(adsAddDeviceNotificationResponse); + plcRequestContainer.getResponseFuture().complete(plcProprietaryResponse); + return mock(ChannelFuture.class); + }); + + SUT.subscribe(new DefaultPlcSubscriptionRequest( + plcSubscriber, + new LinkedHashMap<>( + Collections.singletonMap("field1", + new SubscriptionPlcField(PlcSubscriptionType.CYCLIC, DirectAdsField.of("0/0:BOOL"), Duration.of(1, ChronoUnit.SECONDS))) + ) + )); + } + + @Test + void unsubscribe() { + when(channelMock.writeAndFlush(any(PlcRequestContainer.class))).then(invocation -> { + PlcRequestContainer plcRequestContainer = invocation.getArgument(0); + PlcProprietaryResponse plcProprietaryResponse = mock(InternalPlcProprietaryResponse.class, RETURNS_DEEP_STUBS); + AdsDeleteDeviceNotificationResponse adsDeleteDeviceNotificationResponse = mock(AdsDeleteDeviceNotificationResponse.class, RETURNS_DEEP_STUBS); + when(adsDeleteDeviceNotificationResponse.getResult()).thenReturn(Result.of(0)); + when(plcProprietaryResponse.getResponse()).thenReturn(adsDeleteDeviceNotificationResponse); + plcRequestContainer.getResponseFuture().complete(plcProprietaryResponse); + return mock(ChannelFuture.class); + }); + + SUT.unsubscribe(new DefaultPlcUnsubscriptionRequest(plcSubscriber, + Collections.singletonList(new AdsSubscriptionHandle(plcSubscriber, NotificationHandle.of(1)))) + ); + } + } + + @Nested + class Registration { + @Captor + ArgumentCaptor<Consumer<AdsDeviceNotificationRequest>> consumerArgumentCaptor; + + @Test + void register() throws Exception { + Plc4x2AdsProtocol plc4x2AdsProtocol = mock(Plc4x2AdsProtocol.class); + when(channelMock.pipeline().get(Plc4x2AdsProtocol.class)).thenReturn(plc4x2AdsProtocol); + + AtomicReference<PlcSubscriptionEvent> plcSubscriptionEventAtomicReference = new AtomicReference<>(); + SUT.register(plcSubscriptionEventAtomicReference::set); + verify(plc4x2AdsProtocol).addConsumer(consumerArgumentCaptor.capture()); + + consumerArgumentCaptor.getValue().accept(AdsDeviceNotificationRequest.of(mock(AmsHeader.class), Length.of(1), Stamps.of(1), Collections.singletonList(AdsStampHeader.of(TimeStamp.of(1), Collections.singletonList(AdsNotificationSample.of(NotificationHandle.of(1), Data.of("Hello World!"))))))); + TimeUnit.MILLISECONDS.sleep(100); + assertThat(plcSubscriptionEventAtomicReference).isNotNull(); + } + + @Test + void unregister() { + SUT.unregister(mock(InternalPlcConsumerRegistration.class)); + } } + + @Nested + class Misc { + @Test + void remainingMethods() { + assertThat(SUT.canSubscribe()).isTrue(); + assertThat(SUT.subscriptionRequestBuilder()).isNotNull(); + assertThat(SUT.unsubscriptionRequestBuilder()).isNotNull(); + } + } + } \ No newline at end of file