private void doTest(int concurrentConsumers, ContainerConfigurer configurer) {
    int messageCount = 10;
    RabbitTemplate template = new RabbitTemplate();
    CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
    connectionFactory.setHost("localhost");
    connectionFactory.setChannelCacheSize(concurrentConsumers);
    connectionFactory.setPort(BrokerTestUtils.getPort());
    template.setConnectionFactory(connectionFactory);
    SimpleMessageConverter messageConverter = new SimpleMessageConverter();
    messageConverter.setCreateMessageIds(true);
    template.setMessageConverter(messageConverter);
    for (int i = 0; i < messageCount; i++) {
      template.convertAndSend(queue1.getName(), new Integer(i));
      template.convertAndSend(queue2.getName(), new Integer(i));
    }
    final SimpleMessageListenerContainer container =
        new SimpleMessageListenerContainer(connectionFactory);
    final CountDownLatch latch = new CountDownLatch(messageCount * 2);
    PojoListener listener = new PojoListener(latch);
    container.setMessageListener(new MessageListenerAdapter(listener));
    container.setAcknowledgeMode(AcknowledgeMode.AUTO);
    container.setChannelTransacted(true);
    container.setConcurrentConsumers(concurrentConsumers);
    configurer.configure(container);
    container.afterPropertiesSet();
    container.start();
    try {
      int timeout = Math.min(1 + messageCount / concurrentConsumers, 30);
      boolean waited = latch.await(timeout, TimeUnit.SECONDS);
      logger.info("All messages recovered: " + waited);
      assertEquals(concurrentConsumers, container.getActiveConsumerCount());
      assertTrue("Timed out waiting for messages", waited);
    } catch (InterruptedException e) {
      Thread.currentThread().interrupt();
      throw new IllegalStateException("unexpected interruption");
    } finally {
      container.shutdown();
      assertEquals(0, container.getActiveConsumerCount());
    }
    assertNull(template.receiveAndConvert(queue1.getName()));
    assertNull(template.receiveAndConvert(queue2.getName()));

    connectionFactory.destroy();
  }
  private void doTestRetry(
      int messageCount, int txSize, int failFrequency, int concurrentConsumers, boolean stateful)
      throws Exception {

    int failedMessageCount =
        messageCount / failFrequency + (messageCount % failFrequency == 0 ? 0 : 1);

    RabbitTemplate template = createTemplate(concurrentConsumers);
    for (int i = 0; i < messageCount; i++) {
      template.convertAndSend(queue.getName(), i);
    }

    final SimpleMessageListenerContainer container =
        new SimpleMessageListenerContainer(template.getConnectionFactory());
    PojoListener listener = new PojoListener(failFrequency);
    container.setMessageListener(new MessageListenerAdapter(listener));
    container.setAcknowledgeMode(AcknowledgeMode.AUTO);
    container.setChannelTransacted(true);
    container.setTxSize(txSize);
    container.setConcurrentConsumers(concurrentConsumers);

    final CountDownLatch latch = new CountDownLatch(failedMessageCount);
    container.setAdviceChain(new Advice[] {createRetryInterceptor(latch, stateful)});

    container.setQueueNames(queue.getName());
    container.afterPropertiesSet();
    container.start();

    try {

      int timeout = Math.min(1 + 2 * messageCount / concurrentConsumers, 30);

      final int count = messageCount;
      logger.debug("Waiting for messages with timeout = " + timeout + " (s)");
      Executors.newSingleThreadExecutor()
          .execute(
              () -> {
                while (container.getActiveConsumerCount() > 0) {
                  try {
                    Thread.sleep(100L);
                  } catch (InterruptedException e) {
                    latch.countDown();
                    Thread.currentThread().interrupt();
                    return;
                  }
                }
                for (int i = 0; i < count; i++) {
                  latch.countDown();
                }
              });
      boolean waited = latch.await(timeout, TimeUnit.SECONDS);
      logger.info("All messages recovered: " + waited);
      assertEquals(concurrentConsumers, container.getActiveConsumerCount());
      assertTrue("Timed out waiting for messages", waited);

      // Retried each failure 3 times (default retry policy)...
      assertEquals(3 * failedMessageCount, listener.getCount());

      // All failed messages recovered
      assertEquals(null, template.receiveAndConvert(queue.getName()));

    } finally {
      container.shutdown();
      ((DisposableBean) template.getConnectionFactory()).destroy();

      assertEquals(0, container.getActiveConsumerCount());
    }
  }