package com.atlassian.bitbucket.mesh.io;

import com.atlassian.bitbucket.mesh.junit.TemporaryDirectory;
import org.junit.Rule;
import org.junit.Test;

import java.util.concurrent.CountDownLatch;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertEquals;

public class IoThrottleTest {

    @Rule
    public final TemporaryDirectory temporaryDirectory = new TemporaryDirectory();

    @Test(timeout = 1000L)
    public void testCancelAbortsThrottling() throws InterruptedException {
        IoThrottle throttle = IoThrottle.start(64); // throttle at 64b/s

        CountDownLatch latch = new CountDownLatch(1);

        Thread thread = new Thread(() -> {
            latch.countDown();
            //This should park the thread for a REALLY long time
            throttle.maybeThrottle(Long.MAX_VALUE / 10000L);
        });

        thread.start();

        //Wait for the thread to start throttling, then sleep a bit more to make sure the throttled thread is parked
        latch.await();
        for (int i = 0; i < 100; ++i) {
            if (thread.getState() == Thread.State.TIMED_WAITING) {
                break;
            }
            Thread.sleep(2L);
        }
        assertEquals(Thread.State.TIMED_WAITING, thread.getState());

        //Cancel the throttle and verity that the thread terminates (quickly)
        throttle.cancel();

        thread.join(75L);
        assertEquals(Thread.State.TERMINATED, thread.getState());
    }

    @Test
    public void testThrottling() {
        IoThrottle throttle = IoThrottle.start(64 * 1024); // throttle at 64kb/s
        long start = System.currentTimeMillis();

        //Now simulate processing 30 buffers of 1000 bytes each. This should take at least 457ms
        for (int i = 0; i < 30; ++i) {
            throttle.maybeThrottle(1000);
        }
        long durationMs = System.currentTimeMillis() - start;

        //Allow the pause to take 100ms
        assertThat("processing didn't take long enough", durationMs,
                both(greaterThanOrEqualTo(457L)).and(lessThanOrEqualTo(550L)));
    }
}