package org.fluentlenium.core.hook; import org.fluentlenium.core.FluentControl; import org.fluentlenium.core.components.ComponentInstantiator; import org.fluentlenium.core.components.DefaultComponentInstantiator; import org.fluentlenium.utils.ReflectionUtils; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.invocation.InvocationOnMock; import org.mockito.runners.MockitoJUnitRunner; import org.mockito.stubbing.Answer; import java.util.List; import java.util.function.Function; import java.util.function.Supplier; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyList; import static org.mockito.Matchers.argThat; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) public class HookControlTest { private final Object proxy = new Object(); @Mock private FluentControl control; @Mock private Supplier<HookControl> supplier; private final ComponentInstantiator instantiator = new DefaultComponentInstantiator(control); private HookControlImpl<HookControl> hookControl; public static class HookDefinitionMatcher implements ArgumentMatcher<List<HookDefinition<?>>> { private final Class<?>[] hooks; public HookDefinitionMatcher(Class<?>[] hooks) { this.hooks = hooks; } @Override public boolean matches(List<HookDefinition<?>> argument) { if (argument.size() != hooks.length) { return false; } for (int i = 0; i < argument.size(); i++) { if (!argument.get(i).getHookClass().equals(hooks[i])) { return false; } } return true; } } public static List<HookDefinition<?>> hookDefinition(Class<?>... hooks) { return argThat(new HookDefinitionMatcher(hooks)); } private static class Hook1 extends BaseHook { Hook1(FluentControl control, ComponentInstantiator instantiator, Supplier supplier, Supplier supplier2, Supplier toStringSupplier, Object options) { super(control, instantiator, supplier, supplier2, toStringSupplier, options); } } private static class Hook2 extends BaseHook { Hook2(FluentControl control, ComponentInstantiator instantiator, Supplier supplier, Supplier supplier2, Supplier toStringSupplier, Object options) { super(control, instantiator, supplier, supplier2, toStringSupplier, options); } } private static class Hook3 extends BaseHook { Hook3(FluentControl control, ComponentInstantiator instantiator, Supplier supplier, Supplier supplier2, Supplier toStringSupplier, Object options) { super(control, instantiator, supplier, supplier2, toStringSupplier, options); } } public void resetAndMock(HookControlImpl<?> hookControl) { reset(hookControl); doNothing().when(hookControl).applyHooks(any(Object.class), any(HookChainBuilder.class), anyList()); } @Before public void before() throws NoSuchFieldException, IllegalAccessException { hookControl = spy(new HookControlImpl<>(null, proxy, control, instantiator, supplier)); ReflectionUtils.set(HookControlImpl.class.getDeclaredField("self"), hookControl, hookControl); when(supplier.get()).thenAnswer(new Answer<HookControlImpl>() { @Override public HookControlImpl answer(InvocationOnMock invocation) throws Throwable { HookControlImpl<HookControl> answer = spy(new HookControlImpl<>(null, proxy, control, instantiator, supplier)); ReflectionUtils.set(HookControlImpl.class.getDeclaredField("self"), answer, answer); resetAndMock(answer); return answer; } }); resetAndMock(hookControl); } @Test public void testHook() { hookControl.withHook(Hook1.class); verify(hookControl).applyHooks(eq(proxy), any(), hookDefinition(Hook1.class)); resetAndMock(hookControl); } @Test public void testNoHook() { hookControl.withHook(Hook1.class); verify(hookControl).applyHooks(eq(proxy), any(), hookDefinition(Hook1.class)); resetAndMock(hookControl); hookControl.noHook(); verify(hookControl).applyHooks(eq(proxy), any(), hookDefinition()); resetAndMock(hookControl); } @Test public void testNoHookInstance() { hookControl.withHook(Hook1.class); verify(hookControl).applyHooks(eq(proxy), any(), hookDefinition(Hook1.class)); resetAndMock(hookControl); HookControlImpl newInstance = (HookControlImpl) hookControl.noHookInstance(); assertThat(newInstance.getHookDefinitions()).isEmpty(); assertThat(hookControl.getHookDefinitions()).hasSize(1); } @Test public void testNoHookOneClassInstance() { hookControl.withHook(Hook1.class); resetAndMock(hookControl); hookControl.withHook(Hook2.class); verify(hookControl).applyHooks(eq(proxy), any(), hookDefinition(Hook1.class, Hook2.class)); resetAndMock(hookControl); HookControlImpl newInstance = (HookControlImpl) hookControl.noHookInstance(Hook1.class); assertThat(newInstance.getHookDefinitions()).hasSize(1); assertThat(hookControl.getHookDefinitions()).hasSize(2); } @Test public void testNoHookOneClass() { hookControl.withHook(Hook1.class); resetAndMock(hookControl); hookControl.withHook(Hook2.class); verify(hookControl).applyHooks(eq(proxy), any(), hookDefinition(Hook1.class, Hook2.class)); resetAndMock(hookControl); hookControl.noHook(Hook2.class); verify(hookControl).applyHooks(eq(proxy), any(), hookDefinition(Hook1.class)); resetAndMock(hookControl); } @Test public void testThreeHooksNoHookAndRestore() { hookControl.withHook(Hook1.class); hookControl.withHook(Hook2.class); resetAndMock(hookControl); hookControl.withHook(Hook3.class); verify(hookControl).applyHooks(eq(proxy), any(), hookDefinition(Hook1.class, Hook2.class, Hook3.class)); resetAndMock(hookControl); hookControl.noHook(); verify(hookControl).applyHooks(eq(proxy), any(), hookDefinition()); resetAndMock(hookControl); hookControl.restoreHooks(); verify(hookControl).applyHooks(eq(proxy), any(), hookDefinition(Hook1.class, Hook2.class, Hook3.class)); resetAndMock(hookControl); } @Test public void testHooksNoHookFunction() { hookControl.withHook(Hook1.class); hookControl.withHook(Hook2.class); resetAndMock(hookControl); assertThat(hookControl.noHook(new Function<HookControl, String>() { @Override public String apply(HookControl input) { assertThat(input).isSameAs(hookControl); assertThat(hookControl.getHookDefinitions()).isEmpty(); verify(hookControl).applyHooks(eq(proxy), any(), hookDefinition()); resetAndMock(hookControl); return "test"; } })).isEqualTo("test"); verify(hookControl).applyHooks(eq(proxy), any(), hookDefinition(Hook1.class, Hook2.class)); resetAndMock(hookControl); hookControl.withHook(Hook3.class); verify(hookControl).applyHooks(eq(proxy), any(), hookDefinition(Hook1.class, Hook2.class, Hook3.class)); resetAndMock(hookControl); } @Test public void testHooksNoHookOneClassFunction() { hookControl.withHook(Hook1.class); hookControl.withHook(Hook2.class); resetAndMock(hookControl); assertThat(hookControl.noHook(Hook1.class, new Function<HookControl, String>() { @Override public String apply(HookControl input) { assertThat(input).isSameAs(hookControl); assertThat(hookControl.getHookDefinitions()).hasSize(1); assertThat(hookControl.getHookDefinitions().get(0).getHookClass()).isEqualTo(Hook2.class); verify(hookControl).applyHooks(eq(proxy), any(), hookDefinition(Hook2.class)); resetAndMock(hookControl); return "test"; } })).isEqualTo("test"); verify(hookControl).applyHooks(eq(proxy), any(), hookDefinition(Hook1.class, Hook2.class)); resetAndMock(hookControl); hookControl.withHook(Hook3.class); verify(hookControl).applyHooks(eq(proxy), any(), hookDefinition(Hook1.class, Hook2.class, Hook3.class)); resetAndMock(hookControl); } }