package fr.xephi.authme.initialization; import fr.xephi.authme.AuthMe; import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.datasource.DataSource; import org.bukkit.Server; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginLogger; import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.scheduler.BukkitScheduler; import org.bukkit.scheduler.BukkitWorker; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import java.util.Arrays; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import static org.hamcrest.Matchers.contains; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; /** * Test for {@link TaskCloser}. */ @RunWith(MockitoJUnitRunner.class) public class TaskCloserTest { private static final int[] ACTIVE_WORKERS_ID = {2, 5}; private TaskCloser taskCloser; @Mock private AuthMe authMe; @Mock private PluginLogger logger; @Mock private BukkitScheduler bukkitScheduler; @Mock private DataSource dataSource; @Before public void initAuthMe() { Server server = mock(Server.class); given(server.getScheduler()).willReturn(bukkitScheduler); ReflectionTestUtils.setField(JavaPlugin.class, authMe, "server", server); given(authMe.getLogger()).willReturn(logger); taskCloser = spy(new TaskCloser(authMe, dataSource)); } @Test public void shouldWaitForTasksToClose() throws InterruptedException { // given doNothing().when(taskCloser).sleep(); // avoid sleeping in tests mockActiveWorkers(); given(bukkitScheduler.isCurrentlyRunning(ACTIVE_WORKERS_ID[0])).willReturn(false); given(bukkitScheduler.isCurrentlyRunning(ACTIVE_WORKERS_ID[1])) .willReturn(true) // first time .willReturn(false); // second time // when taskCloser.run(); // then verify(bukkitScheduler, times(3)).isQueued(anyInt()); ArgumentCaptor taskIds = ArgumentCaptor.forClass(Integer.class); verify(bukkitScheduler, times(3)).isCurrentlyRunning(taskIds.capture()); assertThat(taskIds.getAllValues(), contains(ACTIVE_WORKERS_ID[0], ACTIVE_WORKERS_ID[1], ACTIVE_WORKERS_ID[1])); verify(taskCloser, times(2)).sleep(); verify(dataSource).closeConnection(); } @Test public void shouldAbortForNeverEndingTask() throws InterruptedException { // given doNothing().when(taskCloser).sleep(); // avoid sleeping in tests mockActiveWorkers(); // This task never ends given(bukkitScheduler.isCurrentlyRunning(ACTIVE_WORKERS_ID[0])).willReturn(true); given(bukkitScheduler.isCurrentlyRunning(ACTIVE_WORKERS_ID[1])).willReturn(false); // when taskCloser.run(); // then verify(bukkitScheduler, times(3)).isQueued(anyInt()); verify(bukkitScheduler, times(61)).isCurrentlyRunning(anyInt()); verify(taskCloser, times(60)).sleep(); verify(dataSource).closeConnection(); } @Test public void shouldStopForInterruptedThread() throws InterruptedException, ExecutionException { // Note ljacqu 20160827: This test must be run in its own thread because we throw an InterruptedException. // Somehow the java.nio.Files API used in tests that are run subsequently don't like this and fail otherwise. ExecutorService executor = Executors.newSingleThreadExecutor(); executor.submit(new Runnable() { @Override public void run() { try { shouldStopForInterruptedThread0(); } catch (Exception e) { throw new IllegalStateException(e); } } }).get(); } /** Test implementation for {@link #shouldStopForInterruptedThread()}. */ private void shouldStopForInterruptedThread0() throws InterruptedException { // given taskCloser = spy(new TaskCloser(authMe, null)); // First two times do nothing, third time throw exception when we sleep doNothing().doNothing().doThrow(InterruptedException.class).when(taskCloser).sleep(); mockActiveWorkers(); given(bukkitScheduler.isCurrentlyRunning(anyInt())).willReturn(true); // when taskCloser.run(); // then verify(bukkitScheduler, times(4)).isCurrentlyRunning(anyInt()); verify(taskCloser, times(3)).sleep(); } private void mockActiveWorkers() { Plugin otherOwner = mock(Plugin.class); List tasks = Arrays.asList( mockBukkitWorker(authMe, ACTIVE_WORKERS_ID[0], false), mockBukkitWorker(otherOwner, 3, false), mockBukkitWorker(authMe, ACTIVE_WORKERS_ID[1], false), mockBukkitWorker(authMe, 7, true), mockBukkitWorker(otherOwner, 11, true)); given(bukkitScheduler.getActiveWorkers()).willReturn(tasks); } private BukkitWorker mockBukkitWorker(Plugin owner, int taskId, boolean isQueued) { BukkitWorker worker = mock(BukkitWorker.class); given(worker.getOwner()).willReturn(owner); given(worker.getTaskId()).willReturn(taskId); given(bukkitScheduler.isQueued(taskId)).willReturn(isQueued); return worker; } }