package cucumber.runtime;

import org.junit.Test;

import java.util.concurrent.TimeoutException;

import static java.lang.Thread.sleep;
import static org.junit.Assert.*;

public class TimeoutTest {
    @Test
    public void doesnt_time_out_if_it_takes_too_long() throws Throwable {
        final Slow slow = new Slow();
        String what = Timeout.timeout(new Timeout.Callback<String>() {
            @Override
            public String call() throws Throwable {
                return slow.slow();
            }
        }, 50);
        assertEquals("slow", what);
    }

    @Test(expected = TimeoutException.class)
    public void times_out_if_it_takes_too_long() throws Throwable {
        final Slow slow = new Slow();
        Timeout.timeout(new Timeout.Callback<String>() {
            @Override
            public String call() throws Throwable {
                return slow.slower();
            }
        }, 50);
        fail();
    }

    @Test(expected = TimeoutException.class)
    public void times_out_infinite_loop_if_it_takes_too_long() throws Throwable {
        final Slow slow = new Slow();
        Timeout.timeout(new Timeout.Callback<Void>() {
            @Override
            public Void call() throws Throwable {
                slow.infinite();
                return null;
            }
        }, 10);
        fail();
    }

    @Test
    public void doesnt_leak_threads() throws Throwable {

        long initialNumberOfThreads = Thread.getAllStackTraces().size();
        long currentNumberOfThreads = Long.MAX_VALUE;

        boolean cleanedUp = false;
        for (int i = 0; i < 1000; i++) {
            Timeout.timeout(new Timeout.Callback<String>() {
                @Override
                public String call() throws Throwable {
                    return null;
                }
            }, 10);
            Thread.sleep(5);
            currentNumberOfThreads = Thread.getAllStackTraces().size();
            if (i > 20 && currentNumberOfThreads <= initialNumberOfThreads) {
                cleanedUp = true;
                break;
            }
        }
        assertTrue(String.format("Threads weren't cleaned up, initial count: %d current count: %d",
                                 initialNumberOfThreads, currentNumberOfThreads),
                   cleanedUp);
    }

    public static class Slow {
        public String slow() throws InterruptedException {
            sleep(10);
            return "slow";
        }

        public String slower() throws InterruptedException {
            sleep(100);
            return "slower";
        }

        public void infinite() throws InterruptedException {
            while (true) {
                sleep(1);
            }
        }
    }
}

@@ -164,9 +164,100 @@
 ert.
-*
+assertEquals;%0Aimport static org.junit.Assert.assertTrue;%0Aimport static org.junit.Assert.fail
 ;%0A%0Ap
@@ -322,37 +322,43 @@
 _time_out_if_it_
+doesnt_
 take
-s
 _too_long() thro
@@ -584,24 +584,26 @@
 n slow.slow(
+10
 );%0A         
@@ -647,18 +647,24 @@
 uals(%22sl
-ow
+ept 10ms
 %22, what)
@@ -986,19 +986,20 @@
 low.slow
-er(
+(100
 );%0A     
@@ -2323,25 +2323,16 @@
         
-         
 initialN
@@ -2372,19 +2372,16 @@
 reads),%0A
-   
         
@@ -2465,16 +2465,26 @@
 ng slow(
+int millis
 ) throws
@@ -2525,18 +2525,22 @@
   sleep(
-10
+millis
 );%0A     
@@ -2557,139 +2557,43 @@
 urn 
-%22slow%22;%0A        %7D%0A%0A        public String slower() throws InterruptedException %7B%0A            sleep(100);%0A            return %22slower%22
+String.format(%22slept %25sms%22, millis)
 ;%0A  

Improve timeout tests