@AfterMethod
  public void cleanUp(Method method) throws InterruptedException {
    // A previous test didn't call onComplete on the processor, manual clean up
    if (driverManager.getCounter() > 0) {

      Thread.sleep(1000);

      if (driverManager.getCounter() > 0) {
        System.err.println(
            "Possibly method " + method.getName() + " didn't call onComplete on processor");

        for (AeronProcessor processor : processors) {
          processor.shutdown();
          TestSubscriber.await(5L, "processor didn't terminate", processor::isTerminated);
        }

        TestSubscriber.await(
            5L,
            "Embedded Media driver wasn't shutdown properly",
            () -> driverManager.getCounter() == 0);
      }
    }

    processors.clear();
  }
  @Override
  public Processor<Buffer, Buffer> createIdentityProcessor(int bufferSize) {
    for (Iterator<AeronProcessor> it = processors.iterator(); it.hasNext(); ) {
      AeronProcessor processor = it.next();
      if (processor.isTerminated()) {
        it.remove();
      } else {
        processor.shutdown();
      }
    }

    AeronProcessor processor = AeronProcessor.create(createContext(streamId += 10));
    processors.add(processor);
    return processor;
  }
  @AfterMethod
  public void cleanUp(Method method) throws InterruptedException {
    // A previous test didn't call onComplete on the processor, manual clean up
    EmbeddedMediaDriverManager driverManager = EmbeddedMediaDriverManager.getInstance();
    if (driverManager.getCounter() > 0) {

      Thread.sleep(1000);

      if (driverManager.getCounter() > 0) {
        System.err.println(
            "Possibly method " + method.getName() + " didn't call onComplete on processor");

        processor.onComplete();

        Thread.sleep(1000);

        if (driverManager.getCounter() > 0) {
          System.err.println(
              "Manual termination of processor after method "
                  + method.getName()
                  + " didn't shutdown Media driver");
        }
      }
    }
    processor = null;
  }
  @Test
  public void testWorksWithTwoSubscribersViaEmitter() throws InterruptedException {
    AeronProcessor processor = AeronProcessor.create(createContext());
    Flux.just(Buffer.wrap("Live"), Buffer.wrap("Hard"), Buffer.wrap("Die"), Buffer.wrap("Harder"))
        .subscribe(processor);

    FluxProcessor<Buffer, Buffer> emitter = EmitterProcessor.create();
    processor.subscribe(emitter);

    TestSubscriber<String> subscriber1 = TestSubscriber.create();
    Buffer.bufferToString(emitter).subscribe(subscriber1);

    TestSubscriber<String> subscriber2 = TestSubscriber.create();
    Buffer.bufferToString(emitter).subscribe(subscriber2);

    subscriber1.awaitAndAssertNextValues("Live", "Hard", "Die", "Harder").assertComplete();
    subscriber2.awaitAndAssertNextValues("Live", "Hard", "Die", "Harder").assertComplete();
  }
  @Test
  @Ignore
  public void testCompleteShutdownsProcessorWithNoSubscribers() {
    AeronProcessor processor = AeronProcessor.create(createContext());

    Publisher<Buffer> publisher = Subscriber::onComplete;

    publisher.subscribe(processor);
  }
  @Test
  public void testExceptionWithNullMessageIsHandled() throws InterruptedException {
    AeronProcessor processor = AeronProcessor.create(createContext());

    TestSubscriber<String> subscriber = TestSubscriber.create();
    Buffer.bufferToString(processor).subscribe(subscriber);

    Flux<Buffer> sourceStream = Flux.error(new RuntimeException());
    sourceStream.subscribe(processor);

    subscriber.await(TIMEOUT).assertErrorWith(t -> assertThat(t.getMessage(), is("")));
  }
  @Test
  public void testCancelsUpstreamSubscriptionWhenLastSubscriptionIsCancelledAndAutoCancel()
      throws InterruptedException {
    AeronProcessor processor = AeronProcessor.create(createContext().autoCancel(true));

    final CountDownLatch subscriptionCancelledLatch = new CountDownLatch(1);
    Publisher<Buffer> dataPublisher =
        new Publisher<Buffer>() {
          @Override
          public void subscribe(Subscriber<? super Buffer> subscriber) {
            subscriber.onSubscribe(
                new Subscription() {
                  @Override
                  public void request(long n) {
                    System.out.println("Requested: " + n);
                  }

                  @Override
                  public void cancel() {
                    System.out.println("Upstream subscription cancelled");
                    subscriptionCancelledLatch.countDown();
                  }
                });
          }
        };
    dataPublisher.subscribe(processor);

    TestSubscriber<String> client = TestSubscriber.create();

    Buffer.bufferToString(processor).subscribe(client);

    processor.onNext(Buffer.wrap("Hello"));

    client.awaitAndAssertNextValues("Hello").cancel();

    assertTrue(
        "Subscription wasn't cancelled",
        subscriptionCancelledLatch.await(TIMEOUT.getSeconds(), TimeUnit.SECONDS));
  }
  @Test
  public void testCompleteSignalIsReceived() throws InterruptedException {
    AeronProcessor processor = AeronProcessor.create(createContext());
    Flux.just(Buffer.wrap("One"), Buffer.wrap("Two"), Buffer.wrap("Three")).subscribe(processor);

    TestSubscriber<String> subscriber = TestSubscriber.create(0);
    Buffer.bufferToString(processor).subscribe(subscriber);

    subscriber.request(1);
    subscriber.awaitAndAssertNextValues("One");

    subscriber.request(1);
    subscriber.awaitAndAssertNextValues("Two");

    subscriber.request(1);
    subscriber.awaitAndAssertNextValues("Three").assertComplete();
  }
  @Test
  public void testClientReceivesException() throws InterruptedException {
    AeronProcessor processor = AeronProcessor.create(createContext());

    // as error is delivered on a different channelId compared to signal
    // its delivery could shutdown the processor before the processor subscriber
    // receives signal
    Flux.concat(
            Flux.just(Buffer.wrap("Item")),
            Flux.error(new RuntimeException("Something went wrong")))
        .subscribe(processor);

    TestSubscriber<String> subscriber = TestSubscriber.create();
    Buffer.bufferToString(processor).subscribe(subscriber);

    subscriber
        .await(TIMEOUT)
        .assertErrorWith(t -> assertThat(t.getMessage(), is("Something went wrong")));
  }
  @Test
  public void testRemotePublisherReceivesErrorBeforeProcessorIsShutdown()
      throws InterruptedException {
    AeronProcessor processor = AeronProcessor.create(createContext());

    Flux.<Buffer>error(new Exception("Oops!")).subscribe(processor);

    TestSubscriber<String> subscriber = TestSubscriber.create(0);
    Buffer.bufferToString(processor).subscribe(subscriber);

    AeronFlux remotePublisher = new AeronFlux(createContext());
    TestSubscriber<String> remoteSubscriber = TestSubscriber.create(0);
    Buffer.bufferToString(remotePublisher).subscribe(remoteSubscriber);

    subscriber.request(1);
    remoteSubscriber.request(1);

    subscriber.await(TIMEOUT).assertError();
    remoteSubscriber.await(TIMEOUT).assertError();
  }
  @Test
  public void testRemotePublisherReceivesCompleteBeforeProcessorIsShutdown()
      throws InterruptedException {
    AeronProcessor processor = AeronProcessor.create(createContext());

    Flux.just(Buffer.wrap("Live")).subscribe(processor);

    TestSubscriber<String> subscriber = TestSubscriber.create(0);
    Buffer.bufferToString(processor).subscribe(subscriber);

    AeronFlux remotePublisher = new AeronFlux(createContext());
    TestSubscriber<String> remoteSubscriber = TestSubscriber.create(0);
    Buffer.bufferToString(remotePublisher).subscribe(remoteSubscriber);

    subscriber.request(1);
    remoteSubscriber.request(1);

    subscriber.awaitAndAssertNextValues("Live").assertComplete();
    remoteSubscriber.awaitAndAssertNextValues("Live").assertComplete();
  }
  @Override
  public Processor<Buffer, Buffer> createIdentityProcessor(int bufferSize) {
    counter += 4;
    int streamId = STREAM_ID + counter;

    processor =
        AeronProcessor.builder()
            .name("processor")
            .autoCancel(true)
            .launchEmbeddedMediaDriver(true)
            .channel(CHANNEL)
            .streamId(streamId)
            .errorStreamId(streamId + 1)
            .commandRequestStreamId(streamId + 2)
            .commandReplyStreamId(streamId + 3)
            .publicationLingerTimeoutMillis(50)
            .publicationTimeoutMillis(250)
            .create();

    return processor;
  }
  @Test
  public void testNextSignalIsReceived() throws InterruptedException {
    AeronProcessor processor = AeronProcessor.create(createContext());
    TestSubscriber<String> subscriber = TestSubscriber.create(0);
    Buffer.bufferToString(processor).subscribe(subscriber);
    subscriber.request(4);

    Flux.just(
            Buffer.wrap("Live"),
            Buffer.wrap("Hard"),
            Buffer.wrap("Die"),
            Buffer.wrap("Harder"),
            Buffer.wrap("Extra"))
        .subscribe(processor);

    subscriber.awaitAndAssertNextValues("Live", "Hard", "Die", "Harder");

    subscriber.request(1);

    subscriber.awaitAndAssertNextValues("Extra");
    subscriber.await();
  }