package org.threadly.util.debug; import static org.junit.Assert.*; import static org.threadly.TestConstants.*; import java.io.ByteArrayOutputStream; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.threadly.concurrent.StrictPriorityScheduler; import org.threadly.concurrent.future.ListenableFuture; import org.threadly.concurrent.statistics.PrioritySchedulerStatisticTracker; import org.threadly.test.concurrent.TestCondition; import org.threadly.util.Clock; @SuppressWarnings("javadoc") public class ProfilerTest { private static final int POLL_INTERVAL = 1; private static final int MIN_RESPONSE_LENGTH = 10; protected Profiler profiler; @Before public void setup() { profiler = new Profiler(POLL_INTERVAL); } @After public void cleanup() { profiler.stop(); profiler = null; } protected void blockForProfilerSample() { new TestCondition(() -> profiler.getCollectedSampleQty() > 0).blockTillTrue(1000 * 20); } @Test public void constructorTest() { int testPollInterval = Profiler.DEFAULT_POLL_INTERVAL_IN_MILLIS * 10; Profiler p; p = new Profiler(); assertNotNull(p.pStore.threadTraces); assertEquals(0, p.pStore.threadTraces.size()); assertEquals(Profiler.DEFAULT_POLL_INTERVAL_IN_MILLIS, p.pStore.pollIntervalInMs); assertNull(p.pStore.collectorThread.get()); assertNull(p.pStore.dumpingThread); assertNotNull(p.startStopLock); p = new Profiler(testPollInterval); assertNotNull(p.pStore.threadTraces); assertEquals(0, p.pStore.threadTraces.size()); assertEquals(testPollInterval, p.pStore.pollIntervalInMs); assertNull(p.pStore.collectorThread.get()); assertNull(p.pStore.dumpingThread); assertNotNull(p.startStopLock); } @Test public void getProfileThreadsIteratorTest() { Iterator<?> it = profiler.pStore.getProfileThreadsIterator(); assertNotNull(it); assertTrue(it.hasNext()); assertNotNull(it.next()); } @Test (expected = NoSuchElementException.class) public void profileThreadsIteratorNextFail() { Iterator<?> it = profiler.pStore.getProfileThreadsIterator(); while (it.hasNext()) { assertNotNull(it.next()); } it.next(); fail("Exception should have thrown"); } @Test (expected = UnsupportedOperationException.class) public void profileThreadsIteratorRemoveFail() { Iterator<?> it = profiler.pStore.getProfileThreadsIterator(); it.next(); // not currently supported it.remove(); } @SuppressWarnings("unused") @Test (expected = IllegalArgumentException.class) public void constructorFail() { new Profiler(-1); } @Test public void isRunningTest() { assertFalse(profiler.isRunning()); /* verification of isRunning after start happens in * startWithoutExecutorTest and startWitExecutorTest */ } @Test public void startWithoutExecutorTest() { profiler.start(null); assertTrue(profiler.isRunning()); } @Test public void startWitExecutorTest() { PrioritySchedulerStatisticTracker e = new PrioritySchedulerStatisticTracker(1); try { assertEquals(0, e.getActiveTaskCount()); profiler.start(e); assertTrue(profiler.isRunning()); assertEquals(1, e.getActiveTaskCount()); } finally { profiler.stop(); e.shutdownNow(); } } @Test public void startWithTimeoutTest() throws InterruptedException, ExecutionException, TimeoutException { long start = Clock.accurateForwardProgressingMillis(); ListenableFuture<String> lf = profiler.start(DELAY_TIME); String result = lf.get(DELAY_TIME + (10 * 1000), TimeUnit.MILLISECONDS); long end = Clock.accurateForwardProgressingMillis(); // profiler should be stopped now assertFalse(profiler.isRunning()); assertTrue(end - start >= DELAY_TIME); assertNotNull(result); } @Test public void startWitExecutorAndTimeoutTest() throws InterruptedException, ExecutionException, TimeoutException { StrictPriorityScheduler ps = new StrictPriorityScheduler(2); try { long start = Clock.accurateForwardProgressingMillis(); ListenableFuture<String> lf = profiler.start(ps, DELAY_TIME); String result = lf.get(10 * 1000, TimeUnit.MILLISECONDS); long end = Clock.accurateForwardProgressingMillis(); // profiler should be stopped now assertFalse(profiler.isRunning()); assertTrue(end - start >= DELAY_TIME); assertNotNull(result); } finally { ps.shutdownNow(); } } @Test public void getAndSetProfileIntervalTest() { int TEST_VAL = 100; profiler.setPollInterval(TEST_VAL); assertEquals(TEST_VAL, profiler.getPollInterval()); } @Test (expected = IllegalArgumentException.class) public void setProfileIntervalFail() { profiler.setPollInterval(-1); } @Test public void resetTest() { profiler.start(); // verify there are some samples blockForProfilerSample(); final Thread runningThread = profiler.pStore.collectorThread.get(); profiler.stop(); // verify stopped new TestCondition(() -> ! runningThread.isAlive()).blockTillTrue(1000 * 20); profiler.reset(); assertEquals(0, profiler.pStore.threadTraces.size()); assertEquals(0, profiler.getCollectedSampleQty()); } @Test public void dumpStoppedStringTest() { profiler.start(); blockForProfilerSample(); profiler.stop(); String resultStr = profiler.dump(); verifyDumpStr(resultStr); } @Test public void dumpStoppedOutputStreamTest() { profiler.start(); blockForProfilerSample(); profiler.stop(); ByteArrayOutputStream out = new ByteArrayOutputStream(); profiler.dump(out); String resultStr = out.toString(); verifyDumpStr(resultStr); } @Test public void dumpStringTest() { profiler.start(); blockForProfilerSample(); String resultStr = profiler.dump(); verifyDumpStr(resultStr); } @Test public void dumpOutputStreamTest() { profiler.start(); blockForProfilerSample(); ByteArrayOutputStream out = new ByteArrayOutputStream(); profiler.dump(out); String resultStr = out.toString(); verifyDumpStr(resultStr); } @Test public void dumpStringOnlySummaryTest() { profiler.start(); blockForProfilerSample(); String resultStr = profiler.dump(false); assertTrue(resultStr.startsWith("Combined profile for all threads")); } protected static void verifyDumpStr(String resultStr) { assertTrue(resultStr.length() > MIN_RESPONSE_LENGTH); assertTrue(resultStr.contains(Profiler.FUNCTION_BY_COUNT_HEADER)); assertTrue(resultStr.contains(Profiler.FUNCTION_BY_NET_HEADER)); } }