package org.jooby.exec; import static org.easymock.EasyMock.eq; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.expectLastCall; import static org.easymock.EasyMock.isA; import java.lang.Thread.UncaughtExceptionHandler; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadFactory; import java.util.function.Function; import org.jooby.Env; import org.jooby.test.MockUnit; import org.jooby.test.MockUnit.Block; import org.junit.Test; import org.junit.runner.RunWith; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import com.google.inject.Binder; import com.google.inject.Key; import com.google.inject.binder.LinkedBindingBuilder; import com.google.inject.name.Names; import com.typesafe.config.Config; import com.typesafe.config.ConfigFactory; import com.typesafe.config.ConfigValueFactory; import javaslang.control.Try.CheckedRunnable; @RunWith(PowerMockRunner.class) @PrepareForTest({Exec.class, Executors.class, ForkJoinPool.class, Thread.class }) public class ExecTest { private Block executors = unit -> { unit.mockStatic(Executors.class); }; private Block onStop = unit -> { Env env = unit.get(Env.class); expect(env.onStop(unit.capture(CheckedRunnable.class))).andReturn(env); }; @Test public void cached1() throws Exception { Config conf = ConfigFactory.empty() .withValue("executors", ConfigValueFactory.fromAnyRef("cached")); new MockUnit(Env.class, Binder.class, ExecutorService.class) .expect(executors) .expect(unit -> { expect(Executors.newCachedThreadPool(isA(ThreadFactory.class))) .andReturn(unit.get(ExecutorService.class)); }) .expect(bind("default", true, ExecutorService.class, Executor.class)) .expect(onStop) .run(unit -> { new Exec().configure(unit.get(Env.class), conf, unit.get(Binder.class)); }); } @Test public void cached1Alternative() throws Exception { Config conf = ConfigFactory.empty() .withValue("executors.default.type", ConfigValueFactory.fromAnyRef("cached")); new MockUnit(Env.class, Binder.class, ExecutorService.class) .expect(executors) .expect(unit -> { expect(Executors.newCachedThreadPool(isA(ThreadFactory.class))) .andReturn(unit.get(ExecutorService.class)); }) .expect(bind("default", true, ExecutorService.class, Executor.class)) .expect(onStop) .run(unit -> { new Exec().configure(unit.get(Env.class), conf, unit.get(Binder.class)); }); } @Test public void fixed1() throws Exception { int n = Runtime.getRuntime().availableProcessors(); Config conf = ConfigFactory.empty() .withValue("executors", ConfigValueFactory.fromAnyRef("priority, fixed")); new MockUnit(Env.class, Binder.class, ExecutorService.class) .expect(executors) .expect(fixedPool(n)) .expect(bind("default", true, ExecutorService.class, Executor.class)) .expect(onStop) .run(unit -> { new Exec().configure(unit.get(Env.class), conf, unit.get(Binder.class)); }); } @Test public void fixed1Alternative() throws Exception { int n = Runtime.getRuntime().availableProcessors(); Config conf = ConfigFactory.empty() .withValue("executors.default.type", ConfigValueFactory.fromAnyRef("fixed")); new MockUnit(Env.class, Binder.class, ExecutorService.class) .expect(executors) .expect(fixedPool(n)) .expect(bind("default", true, ExecutorService.class, Executor.class)) .expect(onStop) .run(unit -> { new Exec().configure(unit.get(Env.class), conf, unit.get(Binder.class)); }); } @Test public void onStop() throws Exception { int n = Runtime.getRuntime().availableProcessors(); Config conf = ConfigFactory.empty() .withValue("executors", ConfigValueFactory.fromAnyRef("priority, fixed")); new MockUnit(Env.class, Binder.class, ExecutorService.class) .expect(executors) .expect(fixedPool(n)) .expect(bind("default", true, ExecutorService.class, Executor.class)) .expect(onStop) .expect(unit -> { ExecutorService es = unit.get(ExecutorService.class); es.shutdown(); }) .run(unit -> { new Exec().configure(unit.get(Env.class), conf, unit.get(Binder.class)); }, unit -> { CheckedRunnable stop = unit.captured(CheckedRunnable.class).iterator().next(); stop.run(); }); } @Test public void onStopWithFailure() throws Exception { int n = Runtime.getRuntime().availableProcessors(); Config conf = ConfigFactory.empty() .withValue("executors", ConfigValueFactory.fromAnyRef("priority, fixed")); new MockUnit(Env.class, Binder.class, ExecutorService.class) .expect(executors) .expect(fixedPool(n)) .expect(bind("default", true, ExecutorService.class, Executor.class)) .expect(onStop) .expect(unit -> { ExecutorService es = unit.get(ExecutorService.class); es.shutdown(); expectLastCall().andThrow(new IllegalStateException("intentional err")); }) .run(unit -> { new Exec().configure(unit.get(Env.class), conf, unit.get(Binder.class)); }, unit -> { CheckedRunnable stop = unit.captured(CheckedRunnable.class).iterator().next(); stop.run(); }); } @Test public void defexec() throws Exception { int n = Runtime.getRuntime().availableProcessors(); new MockUnit(Env.class, Binder.class, ExecutorService.class) .expect(executors) .expect(fixedPool(n)) .expect(bind("default", true, ExecutorService.class, Executor.class)) .expect(onStop) .run(unit -> { Exec exec = new Exec(); exec.configure(unit.get(Env.class), exec.config(), unit.get(Binder.class)); }); } @Test public void threadFactory() throws Exception { int n = Runtime.getRuntime().availableProcessors(); new MockUnit(Env.class, Binder.class, ExecutorService.class, Runnable.class) .expect(executors) .expect(fixedPool(n)) .expect(bind("default", true, ExecutorService.class, Executor.class)) .expect(onStop) .expect(unit -> { Thread t = unit.constructor(Thread.class) .args(Runnable.class, String.class) .build(unit.get(Runnable.class), "default-1"); t.setDaemon(true); t.setPriority(Thread.NORM_PRIORITY); }) .run(unit -> { Exec exec = new Exec(); exec.configure(unit.get(Env.class), exec.config(), unit.get(Binder.class)); }, unit -> { ThreadFactory tf = unit.captured(ThreadFactory.class).iterator().next(); tf.newThread(unit.get(Runnable.class)); }); } @Test public void threadFactoryNotDaemonMaxPriority() throws Exception { int n = Runtime.getRuntime().availableProcessors(); new MockUnit(Env.class, Binder.class, ExecutorService.class, Runnable.class) .expect(executors) .expect(fixedPool(n)) .expect(bind("default", true, ExecutorService.class, Executor.class)) .expect(onStop) .expect(unit -> { Thread t = unit.constructor(Thread.class) .args(Runnable.class, String.class) .build(unit.get(Runnable.class), "default-1"); t.setDaemon(false); t.setPriority(Thread.MAX_PRIORITY); }) .run(unit -> { Exec exec = new Exec().daemon(false).priority(Thread.MAX_PRIORITY); exec.configure(unit.get(Env.class), exec.config(), unit.get(Binder.class)); }, unit -> { ThreadFactory tf = unit.captured(ThreadFactory.class).iterator().next(); tf.newThread(unit.get(Runnable.class)); }); } @Test public void scheduled1() throws Exception { int n = Runtime.getRuntime().availableProcessors(); Config conf = ConfigFactory.empty() .withValue("executors", ConfigValueFactory.fromAnyRef("scheduled")); new MockUnit(Env.class, Binder.class, ScheduledExecutorService.class) .expect(executors) .expect(scheduledPool(n)) .expect(bind("default", true, ScheduledExecutorService.class, ExecutorService.class, Executor.class)) .expect(onStop) .run(unit -> { new Exec().configure(unit.get(Env.class), conf, unit.get(Binder.class)); }); } @Test public void scheduled1Alternative() throws Exception { int n = Runtime.getRuntime().availableProcessors(); Config conf = ConfigFactory.empty() .withValue("executors.default.type", ConfigValueFactory.fromAnyRef("scheduled")); new MockUnit(Env.class, Binder.class, ScheduledExecutorService.class) .expect(executors) .expect(scheduledPool(n)) .expect(bind("default", true, ScheduledExecutorService.class, ExecutorService.class, Executor.class)) .expect(onStop) .run(unit -> { new Exec().configure(unit.get(Env.class), conf, unit.get(Binder.class)); }); } @Test public void forkJoin() throws Exception { int n = Runtime.getRuntime().availableProcessors(); Config conf = ConfigFactory.empty() .withValue("executors", ConfigValueFactory.fromAnyRef("forkjoin, asyncMode")); new MockUnit(Env.class, Binder.class) .expect(executors) .expect(unit -> { ForkJoinPool pool = unit.constructor(ForkJoinPool.class) .args(int.class, ForkJoinWorkerThreadFactory.class, UncaughtExceptionHandler.class, boolean.class) .build(eq(n), isA(ForkJoinWorkerThreadFactory.class), eq(null), eq(false)); unit.registerMock(ExecutorService.class, pool); }) .expect(bind("default", true, ExecutorService.class, Executor.class, ForkJoinPool.class)) .expect(onStop) .run(unit -> { new Exec().configure(unit.get(Env.class), conf, unit.get(Binder.class)); }); } @Test public void forkJoinAlternative() throws Exception { int n = Runtime.getRuntime().availableProcessors(); Config conf = ConfigFactory.empty() .withValue("executors.default.type", ConfigValueFactory.fromAnyRef("forkjoin")) .withValue("executors.default.asyncMode", ConfigValueFactory.fromAnyRef(false)); new MockUnit(Env.class, Binder.class) .expect(executors) .expect(unit -> { ForkJoinPool pool = unit.constructor(ForkJoinPool.class) .args(int.class, ForkJoinWorkerThreadFactory.class, UncaughtExceptionHandler.class, boolean.class) .build(eq(n), isA(ForkJoinWorkerThreadFactory.class), eq(null), eq(false)); unit.registerMock(ExecutorService.class, pool); }) .expect(bind("default", true, ExecutorService.class, Executor.class, ForkJoinPool.class)) .expect(onStop) .run(unit -> { new Exec().configure(unit.get(Env.class), conf, unit.get(Binder.class)); }); } @Test public void forkJoinAsync() throws Exception { int n = 1; Config conf = ConfigFactory.empty() .withValue("executors", ConfigValueFactory.fromAnyRef("forkjoin=1, asyncMode=true")); new MockUnit(Env.class, Binder.class) .expect(executors) .expect(unit -> { ForkJoinPool pool = unit.constructor(ForkJoinPool.class) .args(int.class, ForkJoinWorkerThreadFactory.class, UncaughtExceptionHandler.class, boolean.class) .build(eq(n), isA(ForkJoinWorkerThreadFactory.class), eq(null), eq(true)); unit.registerMock(ExecutorService.class, pool); }) .expect(bind("default", true, ExecutorService.class, Executor.class, ForkJoinPool.class)) .expect(onStop) .run(unit -> { new Exec().configure(unit.get(Env.class), conf, unit.get(Binder.class)); }); } @Test public void forkJoinAsyncAlternative() throws Exception { int n = 1; Config conf = ConfigFactory.empty() .withValue("executors.default.type", ConfigValueFactory.fromAnyRef("forkjoin")) .withValue("executors.default.size", ConfigValueFactory.fromAnyRef(1)) .withValue("executors.default.asyncMode", ConfigValueFactory.fromAnyRef(true)); new MockUnit(Env.class, Binder.class) .expect(executors) .expect(unit -> { ForkJoinPool pool = unit.constructor(ForkJoinPool.class) .args(int.class, ForkJoinWorkerThreadFactory.class, UncaughtExceptionHandler.class, boolean.class) .build(eq(n), isA(ForkJoinWorkerThreadFactory.class), eq(null), eq(true)); unit.registerMock(ExecutorService.class, pool); }) .expect(bind("default", true, ExecutorService.class, Executor.class, ForkJoinPool.class)) .expect(onStop) .run(unit -> { new Exec().configure(unit.get(Env.class), conf, unit.get(Binder.class)); }); } @Test public void scheduled7() throws Exception { int n = 7; Config conf = ConfigFactory.empty() .withValue("executors", ConfigValueFactory.fromAnyRef("scheduled=" + n)); new MockUnit(Env.class, Binder.class, ScheduledExecutorService.class) .expect(executors) .expect(scheduledPool(n)) .expect(bind("default", true, ScheduledExecutorService.class, ExecutorService.class, Executor.class)) .expect(onStop) .run(unit -> { new Exec().configure(unit.get(Env.class), conf, unit.get(Binder.class)); }); } @Test public void scheduled7Alternative() throws Exception { int n = 7; Config conf = ConfigFactory.empty() .withValue("executors.default.type", ConfigValueFactory.fromAnyRef("scheduled")) .withValue("executors.default.size", ConfigValueFactory.fromAnyRef(n)); new MockUnit(Env.class, Binder.class, ScheduledExecutorService.class) .expect(executors) .expect(scheduledPool(n)) .expect(bind("default", true, ScheduledExecutorService.class, ExecutorService.class, Executor.class)) .expect(onStop) .run(unit -> { new Exec().configure(unit.get(Env.class), conf, unit.get(Binder.class)); }); } @Test public void fixed5() throws Exception { int n = 8; Config conf = ConfigFactory.empty() .withValue("executors", ConfigValueFactory.fromAnyRef("fixed = " + n)); new MockUnit(Env.class, Binder.class, ExecutorService.class) .expect(executors) .expect(fixedPool(n)) .expect(bind("default", true, ExecutorService.class, Executor.class)) .expect(onStop) .run(unit -> { new Exec().configure(unit.get(Env.class), conf, unit.get(Binder.class)); }); } @Test public void fixed5Alternative() throws Exception { int n = 8; Config conf = ConfigFactory.empty() .withValue("executors.default.type", ConfigValueFactory.fromAnyRef("fixed")) .withValue("executors.default.size", ConfigValueFactory.fromAnyRef(n)); new MockUnit(Env.class, Binder.class, ExecutorService.class) .expect(executors) .expect(fixedPool(n)) .expect(bind("default", true, ExecutorService.class, Executor.class)) .expect(onStop) .run(unit -> { new Exec().configure(unit.get(Env.class), conf, unit.get(Binder.class)); }); } private Block fixedPool(final int n) { return unit -> { expect(Executors.newFixedThreadPool(eq(n), unit.capture(ThreadFactory.class))) .andReturn(unit.get(ExecutorService.class)); }; } private Block scheduledPool(final int n) { return unit -> { expect(Executors.newScheduledThreadPool(eq(n), isA(ThreadFactory.class))) .andReturn(unit.get(ScheduledExecutorService.class)); }; } @SuppressWarnings({"rawtypes", "unchecked" }) private Block bind(final String name, final boolean one, final Class... classes) { return unit -> { LinkedBindingBuilder eslbb = unit.mock(LinkedBindingBuilder.class); eslbb.toInstance(unit.get(classes[0])); int times = classes.length + 1; expectLastCall().times(one ? times * 2 : times); Binder binder = unit.get(Binder.class); Function<Class, Key> k = t -> name == null ? Key.get(t) : Key.get(t, Names.named(name)); // name for (Class t : classes) { expect(binder.bind(k.apply(t))).andReturn(eslbb); } expect(binder.bind(k.apply(unit.get(classes[0]).getClass()))).andReturn(eslbb); if (one) { for (Class t : classes) { expect(binder.bind(Key.get(t))).andReturn(eslbb); } expect(binder.bind(Key.get(unit.get(classes[0]).getClass()))).andReturn(eslbb); } }; } @Test public void daemon() throws Exception { int n = Runtime.getRuntime().availableProcessors(); Config conf = ConfigFactory.empty() .withValue("executors", ConfigValueFactory.fromAnyRef("fixed, daemon=false")); new MockUnit(Env.class, Binder.class, ExecutorService.class) .expect(executors) .expect(fixedPool(n)) .expect(bind("default", true, ExecutorService.class, Executor.class)) .expect(onStop) .run(unit -> { new Exec().configure(unit.get(Env.class), conf, unit.get(Binder.class)); }); } @Test public void daemonAlternative() throws Exception { int n = Runtime.getRuntime().availableProcessors(); Config conf = ConfigFactory.empty() .withValue("executors.default.type", ConfigValueFactory.fromAnyRef("fixed")) .withValue("executors.default.daemon", ConfigValueFactory.fromAnyRef(false)); new MockUnit(Env.class, Binder.class, ExecutorService.class) .expect(executors) .expect(fixedPool(n)) .expect(bind("default", true, ExecutorService.class, Executor.class)) .expect(onStop) .run(unit -> { new Exec().configure(unit.get(Env.class), conf, unit.get(Binder.class)); }); } @Test(expected = IllegalArgumentException.class) public void wrongType() throws Exception { Config conf = ConfigFactory.empty() .withValue("executors", ConfigValueFactory.fromAnyRef("wrongtype")); new MockUnit(Env.class, Binder.class, ExecutorService.class) .expect(executors) .run(unit -> { new Exec().configure(unit.get(Env.class), conf, unit.get(Binder.class)); }); } @Test(expected = IllegalArgumentException.class) public void wrongTypeAlternative() throws Exception { Config conf = ConfigFactory.empty() .withValue("executors.default.type", ConfigValueFactory.fromAnyRef("wrongtype")); new MockUnit(Env.class, Binder.class, ExecutorService.class) .expect(executors) .run(unit -> { new Exec().configure(unit.get(Env.class), conf, unit.get(Binder.class)); }); } @Test(expected = IllegalArgumentException.class) public void missingTypeAlternative() throws Exception { Config conf = ConfigFactory.empty() .withValue("executors.default.size", ConfigValueFactory.fromAnyRef(1)); new MockUnit(Env.class, Binder.class, ExecutorService.class) .expect(executors) .run(unit -> { new Exec().configure(unit.get(Env.class), conf, unit.get(Binder.class)); }); } @Test public void priority() throws Exception { int n = Runtime.getRuntime().availableProcessors(); Config conf = ConfigFactory.empty() .withValue("executors", ConfigValueFactory.fromAnyRef("fixed, priority=5")); new MockUnit(Env.class, Binder.class, ExecutorService.class) .expect(executors) .expect(fixedPool(n)) .expect(bind("default", true, ExecutorService.class, Executor.class)) .expect(onStop) .run(unit -> { new Exec().configure(unit.get(Env.class), conf, unit.get(Binder.class)); }); } @Test public void priorityAlternative() throws Exception { int n = Runtime.getRuntime().availableProcessors(); Config conf = ConfigFactory.empty() .withValue("executors.default.type", ConfigValueFactory.fromAnyRef("fixed")) .withValue("executors.default.priority", ConfigValueFactory.fromAnyRef(5)); new MockUnit(Env.class, Binder.class, ExecutorService.class) .expect(executors) .expect(fixedPool(n)) .expect(bind("default", true, ExecutorService.class, Executor.class)) .expect(onStop) .run(unit -> { new Exec().configure(unit.get(Env.class), conf, unit.get(Binder.class)); }); } @Test public void moreExecutors() throws Exception { Config conf = ConfigFactory.empty() .withValue("executors.f1", ConfigValueFactory.fromAnyRef("fixed=1")) .withValue("executors.f2", ConfigValueFactory.fromAnyRef("fixed=1")); new MockUnit(Env.class, Binder.class, ExecutorService.class) .expect(executors) .expect(fixedPool(1)) .expect(fixedPool(1)) .expect(bind("f1", false, ExecutorService.class, Executor.class)) .expect(bind("f2", false, ExecutorService.class, Executor.class)) .expect(onStop) .run(unit -> { new Exec().configure(unit.get(Env.class), conf, unit.get(Binder.class)); }); } @Test public void moreExecutorsAlternative() throws Exception { Config conf = ConfigFactory.empty() .withValue("executors.f1.type", ConfigValueFactory.fromAnyRef("fixed")) .withValue("executors.f1.size", ConfigValueFactory.fromAnyRef(1)) .withValue("executors.f2.type", ConfigValueFactory.fromAnyRef("fixed")) .withValue("executors.f2.size", ConfigValueFactory.fromAnyRef(1)); new MockUnit(Env.class, Binder.class, ExecutorService.class) .expect(executors) .expect(fixedPool(1)) .expect(fixedPool(1)) .expect(bind("f1", false, ExecutorService.class, Executor.class)) .expect(bind("f2", false, ExecutorService.class, Executor.class)) .expect(onStop) .run(unit -> { new Exec().configure(unit.get(Env.class), conf, unit.get(Binder.class)); }); } }