import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.util.Date;
+import java.util.concurrent.Callable;
+import java.util.concurrent.FutureTask;
import org.junit.Before;
import org.junit.Test;
// verify that manager didn't try to stop process in that case (stopped already)
verifyNoMoreInteractions(oStream);
}
+
+ /**
+ * Guarded variable used in {@link #forceStopTracing_async()} test.
+ */
+ volatile boolean pass;
+ final Object passLock = new Object();
+
+ /**
+ * Whitebox test of manager ability to correctly send destroy signal to
+ * the underlying process and correctly process case where there are another
+ * completion waiters at the moment.
+ */
+ @Test(timeout=2*TIMEOUT_MS)
+ public void forceStopTracing_async() throws Exception {
+ // setup guarded variable
+ pass = false;
+
+ // setup mocks
+ // btw, we should ignore normal stopTracing signal
+ doAnswer(new Answer<Integer>() {
+ @Override
+ public Integer answer(InvocationOnMock invocation) throws Throwable {
+ // block while pass != true or interruption occurred
+ if (!pass) {
+ synchronized (passLock) {
+ while (!pass && !Thread.currentThread().isInterrupted())
+ passLock.wait();
+
+ }
+
+ // emulate waitFor behavior
+ if (Thread.currentThread().isInterrupted())
+ throw new InterruptedException();
+ }
+ return 100500;
+ }
+ }).when(process).waitFor();
+
+ doAnswer(new Answer<Object>() {
+ @Override
+ public Object answer(InvocationOnMock invocation) throws Throwable {
+ // unlock waiters
+ synchronized (passLock) {
+ pass = true;
+ passLock.notifyAll();
+ }
+ return null;
+ }
+ }).when(process).destroy();
+
+ // create class under test
+ manager = managerConstructor.newInstance(args, process);
+
+ // call stopTracing in separate thread
+ FutureTask<Boolean> stopTask = new FutureTask<>(new Callable<Boolean>() {
+ @Override
+ public Boolean call() throws Exception {
+ // this call should block until timeout passed or forceStop will be called
+ try {
+ return manager.stopTracing(3*TIMEOUT_MS);
+ } catch (InterruptedException e) {
+ return null;
+ }
+ }
+ });
+
+ Thread stopThread = new Thread(stopTask);
+ stopThread.start();
+
+ // to ensure that stopTracing(timeout) was called
+ Thread.sleep(TIMEOUT_MS);
+
+ // while stop thread still running send force stop tracing signal
+ assertTrue(stopThread.isAlive());
+ manager.forceStopTracing();
+
+ // assert that stopTracing(timeout) correctly processed
+ // asynchronous forceStopTracing signal
+ Boolean stopTaskResult = stopTask.get();
+
+ // result of stopTracing(timeout) should be true if process stopped during this period
+ assertTrue(stopTaskResult);
+ }
}