/*
 * Copyright 2016 higherfrequencytrading.com
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package net.openhft.chronicle.core.threads;

import net.openhft.chronicle.core.Jvm;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/*
 * Created by Peter Lawrey on 09/04/16.
 */
public class ThreadDump {
    @NotNull
    final Set<Thread> threads;
    final Set<String> ignored = new HashSet<>();

    public ThreadDump() {
        this.threads = new HashSet<>(Thread.getAllStackTraces().keySet());
        ignored.add("Time-limited test");
        ignored.add("Attach Listener");
        ignored.add("process reaper");
        ignored.add("chronicle-weak-reference-cleaner");
        for (int i = 0, max = Runtime.getRuntime().availableProcessors(); i < max; i++)
            ignored.add("ForkJoinPool.commonPool-worker-" + i);
    }

    public void ignore(String threadName) {
        ignored.add(threadName);
    }

    public void assertNoNewThreads() {
        @Nullable Map<Thread, StackTraceElement[]> allStackTraces = null;
        for (int i = 1; i < 5; i++) {
            Jvm.pause(i * i * 50);
            allStackTraces = Thread.getAllStackTraces();
            allStackTraces.keySet().removeAll(threads);
            if (allStackTraces.isEmpty())
                return;
            allStackTraces.keySet().removeIf(next -> ignored.stream().anyMatch(item -> next.getName().contains(item)));
            if (allStackTraces.isEmpty())
                return;
            for (@NotNull Map.Entry<Thread, StackTraceElement[]> threadEntry : allStackTraces.entrySet()) {
                @NotNull StringBuilder sb = new StringBuilder();
                sb.append("Thread still running ").append(threadEntry.getKey());
                Jvm.trimStackTrace(sb, threadEntry.getValue());
                System.err.println(sb);
            }
        }
        throw new AssertionError("Threads still running " + allStackTraces.keySet());
    }
}

@@ -850,16 +850,54 @@
 til.Set;
+%0Aimport java.util.concurrent.TimeUnit;
 %0A%0A/*%0A * 
@@ -1587,42 +1587,686 @@
     
-public void assertNoNewThreads() %7B
+/**%0A     * Waits for all new threads to finish execution, up for 50ms.%0A     * %3Cp%3E%0A     * Then, prints 3 warnings after 0ms, 200ms and 650ms. Then, throws an exception after 800ms.%0A     */%0A    public void assertNoNewThreads() %7B%0A        assertNoNewThreads(0, TimeUnit.NANOSECONDS);%0A    %7D%0A%0A    /**%0A     * Waits for all new threads to finish execution, up for the specified amount of time %C2%B1 50ms.%0A     * %3Cp%3E%0A     * Then, prints 3 warnings after 0ms, 200ms and 650ms. Then, throws an exception after 800ms.%0A     */%0A    public void assertNoNewThreads(int delay, @NotNull TimeUnit delayUnit) %7B%0A        long start = System.nanoTime();%0A        long delayNanos = delayUnit.toNanos(delay);
 %0A   
@@ -2367,19 +2367,16 @@
  i %3C 5; 
-i++
 ) %7B%0A    
@@ -2763,32 +2763,157 @@
         return;%0A
+            if (i == 1 && System.nanoTime() - start %3C delayNanos) %7B%0A                continue;%0A            %7D%0A            i++;%0A
             for 

Implement ThreadDump#assertNoNewThreads() overload waiting up for a certain amount of time before emitting the first warning