@Test
public void testSemaphorePermitsInUse() throws Exception {

    final TestCircuitBreaker circuitBreaker = new TestCircuitBreaker();
    // this semaphore will be shared across multiple command instances
    final TryableSemaphoreActual sharedSemaphore =
            new TryableSemaphoreActual(HystrixProperty.Factory.asProperty(3));
    // used to wait until all commands have started
    final CountDownLatch startLatch = new CountDownLatch((sharedSemaphore.numberOfPermits.get() * 2) + 1);
    // used to signal that all command can finish
    final CountDownLatch sharedLatch = new CountDownLatch(1);
    // tracks failures to obtain semaphores
    final AtomicInteger failureCount = new AtomicInteger();
    final Runnable sharedSemaphoreRunnable = new HystrixContextRunnable(HystrixPlugins.getInstance().getConcurrencyStrategy(), new Runnable() {
        public void run() {
            try {
                new LatchedSemaphoreCommand("Command-Shared", circuitBreaker, sharedSemaphore, startLatch, sharedLatch).execute();
            } catch (Exception e) {
                startLatch.countDown();
                e.printStackTrace();
                failureCount.incrementAndGet();
            }
        }
    });
    // creates group of threads each using command sharing a single semaphore
    // I create extra threads and commands so that I can verify that some of them fail to obtain a semaphore
    final int sharedThreadCount = sharedSemaphore.numberOfPermits.get() * 2;
    final Thread[] sharedSemaphoreThreads = new Thread[sharedThreadCount];
    for (int i = 0; i < sharedThreadCount; i++) {
        sharedSemaphoreThreads[i] = new Thread(sharedSemaphoreRunnable);
    }
    // creates thread using isolated semaphore
    final TryableSemaphoreActual isolatedSemaphore =
            new TryableSemaphoreActual(HystrixProperty.Factory.asProperty(1));
    final CountDownLatch isolatedLatch = new CountDownLatch(1);
    final Thread isolatedThread = new Thread(new HystrixContextRunnable(HystrixPlugins.getInstance().getConcurrencyStrategy(), new Runnable() {
        public void run() {
            try {
                new LatchedSemaphoreCommand("Command-Isolated", circuitBreaker, isolatedSemaphore, startLatch, isolatedLatch).execute();
            } catch (Exception e) {
                startLatch.countDown();
                e.printStackTrace();
                failureCount.incrementAndGet();
            }
        }
    }));
    // verifies no permits in use before starting threads
    assertEquals("before threads start, shared semaphore should be unused", 0, sharedSemaphore.getNumberOfPermitsUsed());
    assertEquals("before threads start, isolated semaphore should be unused", 0, isolatedSemaphore.getNumberOfPermitsUsed());
    for (int i = 0; i < sharedThreadCount; i++) {
        sharedSemaphoreThreads[i].start();
    }
    isolatedThread.start();
    // waits until all commands have started
    startLatch.await(1000, TimeUnit.MILLISECONDS);
    // verifies that all semaphores are in use
    assertEquals("immediately after command start, all shared semaphores should be in-use",
            sharedSemaphore.numberOfPermits.get().longValue(), sharedSemaphore.getNumberOfPermitsUsed());
    assertEquals("immediately after command start, isolated semaphore should be in-use",
            isolatedSemaphore.numberOfPermits.get().longValue(), isolatedSemaphore.getNumberOfPermitsUsed());
    // signals commands to finish
    sharedLatch.countDown();
    isolatedLatch.countDown();
    for (int i = 0; i < sharedThreadCount; i++) {
        sharedSemaphoreThreads[i].join();
    }
    isolatedThread.join();
    // verifies no permits in use after finishing threads
    System.out.println("REQLOG : " + HystrixRequestLog.getCurrentRequest().getExecutedCommandsAsString());
    assertEquals("after all threads have finished, no shared semaphores should be in-use", 0, sharedSemaphore.getNumberOfPermitsUsed());
    assertEquals("after all threads have finished, isolated semaphore not in-use", 0, isolatedSemaphore.getNumberOfPermitsUsed());
    // verifies that some executions failed
    assertEquals("expected some of shared semaphore commands to get rejected", sharedSemaphore.numberOfPermits.get().longValue(), failureCount.get());
    assertEquals(0, circuitBreaker.metrics.getCurrentConcurrentExecutionCount());
}