/*
 * Copyright 2014-2021 Real Logic Limited.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.aeron;

import io.aeron.driver.MediaDriver;
import io.aeron.driver.ThreadingMode;
import io.aeron.exceptions.ChannelEndpointException;
import io.aeron.exceptions.RegistrationException;
import io.aeron.status.ChannelEndpointStatus;
import io.aeron.test.Tests;
import io.aeron.test.driver.MediaDriverTestWatcher;
import io.aeron.test.driver.TestMediaDriver;
import org.agrona.ErrorHandler;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
import org.junit.jupiter.api.extension.RegisterExtension;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;

@Timeout(10)
class AsyncResourceTest
{
    private static final int STREAM_ID = 7777;
    private static final String AERON_IPC = "aeron:ipc";

    @RegisterExtension
    final MediaDriverTestWatcher testWatcher = new MediaDriverTestWatcher();

    @Test
    void shouldAddAsyncPublications()
    {
        final Aeron.Context clientCtx = new Aeron.Context()
            .errorHandler(Tests::onError);

        final MediaDriver.Context driverCtx = new MediaDriver.Context()
            .errorHandler(Tests::onError)
            .dirDeleteOnStart(true)
            .threadingMode(ThreadingMode.SHARED);

        try (
            TestMediaDriver ignore = TestMediaDriver.launch(driverCtx, testWatcher);
            Aeron aeron = Aeron.connect(clientCtx))
        {
            final long registrationIdOne = aeron.asyncAddPublication(AERON_IPC, STREAM_ID);
            final long registrationIdTwo = aeron.asyncAddExclusivePublication(AERON_IPC, STREAM_ID);

            ConcurrentPublication publicationOne;
            while (null == (publicationOne = aeron.getPublication(registrationIdOne)))
            {
                Tests.yield();
            }

            ExclusivePublication publicationTwo;
            while (null == (publicationTwo = aeron.getExclusivePublication(registrationIdTwo)))
            {
                Tests.yield();
            }

            assertFalse(aeron.isCommandActive(registrationIdOne));
            assertFalse(aeron.isCommandActive(registrationIdTwo));
            assertFalse(aeron.hasActiveCommands());

            assertNotNull(publicationOne);
            assertNotNull(publicationTwo);
        }
        finally
        {
            driverCtx.deleteDirectory();
        }
    }

    @Test
    void shouldDetectInvalidUri()
    {
        final ErrorHandler mockClientErrorHandler = mock(ErrorHandler.class);
        final Aeron.Context clientCtx = new Aeron.Context()
            .errorHandler(mockClientErrorHandler);

        final ErrorHandler mockDriverErrorHandler = mock(ErrorHandler.class);
        final MediaDriver.Context driverCtx = new MediaDriver.Context()
            .errorHandler(mockDriverErrorHandler)
            .dirDeleteOnStart(true)
            .threadingMode(ThreadingMode.SHARED);

        try (
            TestMediaDriver ignore = TestMediaDriver.launch(driverCtx, testWatcher);
            Aeron aeron = Aeron.connect(clientCtx))
        {
            final long registrationId = aeron.asyncAddPublication("invalid" + AERON_IPC, STREAM_ID);

            verify(mockClientErrorHandler, timeout(5000)).onError(any(RegistrationException.class));

            assertFalse(aeron.isCommandActive(registrationId));
            assertFalse(aeron.hasActiveCommands());
        }
        finally
        {
            driverCtx.deleteDirectory();
        }
    }

    @Test
    void shouldDetectUnknownHost()
    {
        final ErrorHandler mockClientErrorHandler = mock(ErrorHandler.class);
        final Aeron.Context clientCtx = new Aeron.Context()
            .errorHandler(mockClientErrorHandler);

        final ErrorHandler mockDriverErrorHandler = mock(ErrorHandler.class);
        final MediaDriver.Context driverCtx = new MediaDriver.Context()
            .errorHandler(mockDriverErrorHandler)
            .dirDeleteOnStart(true)
            .threadingMode(ThreadingMode.SHARED);

        try (
            TestMediaDriver ignore = TestMediaDriver.launch(driverCtx, testWatcher);
            Aeron aeron = Aeron.connect(clientCtx))
        {
            final long registrationId = aeron.asyncAddPublication("aeron:udp?endpoint=wibble:1234", STREAM_ID);

            verify(mockClientErrorHandler, timeout(5000)).onError(any(RegistrationException.class));

            assertFalse(aeron.isCommandActive(registrationId));
            assertFalse(aeron.hasActiveCommands());
        }
        finally
        {
            driverCtx.deleteDirectory();
        }
    }

    @Test
    void shouldDetectChannelEndpointError()
    {
        final ErrorHandler mockClientErrorHandler = mock(ErrorHandler.class);
        final Aeron.Context clientCtx = new Aeron.Context()
            .errorHandler(mockClientErrorHandler);

        final ErrorHandler mockDriverErrorHandler = mock(ErrorHandler.class);
        final MediaDriver.Context driverCtx = new MediaDriver.Context()
            .errorHandler(mockDriverErrorHandler)
            .dirDeleteOnStart(true)
            .threadingMode(ThreadingMode.DEDICATED);

        try (
            TestMediaDriver ignore = TestMediaDriver.launch(driverCtx, testWatcher);
            Aeron aeron = Aeron.connect(clientCtx))
        {
            final String channel = "aeron:udp?control=localhost:34567|endpoint=localhost:2345";
            final Publication publicationOne = aeron.addPublication(channel + "6", STREAM_ID);

            while (publicationOne.channelStatus() == ChannelEndpointStatus.INITIALIZING)
            {
                Tests.yield();
            }

            final long registrationId = aeron.asyncAddPublication(channel + "7", STREAM_ID);

            ConcurrentPublication publicationTwo;
            while (null == (publicationTwo = aeron.getPublication(registrationId)))
            {
                Tests.yield();
            }

            assertFalse(aeron.isCommandActive(registrationId));
            assertFalse(aeron.hasActiveCommands());

            verify(mockClientErrorHandler, timeout(5000)).onError(any(ChannelEndpointException.class));

            assertEquals(ChannelEndpointStatus.ERRORED, publicationTwo.channelStatus());
        }
        finally
        {
            driverCtx.deleteDirectory();
        }
    }
}

@@ -722,128 +722,29 @@
 ons.
-ChannelEndpointException;%0Aimport io.aeron.exceptions.RegistrationException;%0Aimport io.aeron.status.ChannelEndpointStatus
+RegistrationException
 ;%0Aim
@@ -5112,1742 +5112,7 @@
    %7D
-%0A%0A    @Test%0A    void shouldDetectChannelEndpointError()%0A    %7B%0A        final ErrorHandler mockClientErrorHandler = mock(ErrorHandler.class);%0A        final Aeron.Context clientCtx = new Aeron.Context()%0A            .errorHandler(mockClientErrorHandler);%0A%0A        final ErrorHandler mockDriverErrorHandler = mock(ErrorHandler.class);%0A        final MediaDriver.Context driverCtx = new MediaDriver.Context()%0A            .errorHandler(mockDriverErrorHandler)%0A            .dirDeleteOnStart(true)%0A            .threadingMode(ThreadingMode.DEDICATED);%0A%0A        try (%0A            TestMediaDriver ignore = TestMediaDriver.launch(driverCtx, testWatcher);%0A            Aeron aeron = Aeron.connect(clientCtx))%0A        %7B%0A            final String channel = %22aeron:udp?control=localhost:34567%7Cendpoint=localhost:2345%22;%0A            final Publication publicationOne = aeron.addPublication(channel + %226%22, STREAM_ID);%0A%0A            while (publicationOne.channelStatus() == ChannelEndpointStatus.INITIALIZING)%0A            %7B%0A                Tests.yield();%0A            %7D%0A%0A            final long registrationId = aeron.asyncAddPublication(channel + %227%22, STREAM_ID);%0A%0A            ConcurrentPublication publicationTwo;%0A            while (null == (publicationTwo = aeron.getPublication(registrationId)))%0A            %7B%0A                Tests.yield();%0A            %7D%0A%0A            assertFalse(aeron.isCommandActive(registrationId));%0A            assertFalse(aeron.hasActiveCommands());%0A%0A            verify(mockClientErrorHandler, timeout(5000)).onError(any(ChannelEndpointException.class));%0A%0A            assertEquals(ChannelEndpointStatus.ERRORED, publicationTwo.channelStatus());%0A        %7D%0A        finally%0A        %7B%0A            driverCtx.deleteDirectory();%0A        %7D%0A    %7D
 %0A%7D%0A

Remove channel endpoint error test given different behaviour of the C driver.