diff --git a/src/test/java/com/diamonddagger590/mccore/bootstrap/registrar/HooksRegistrarTest.java b/src/test/java/com/diamonddagger590/mccore/bootstrap/registrar/HooksRegistrarTest.java new file mode 100644 index 0000000..5e8e8c5 --- /dev/null +++ b/src/test/java/com/diamonddagger590/mccore/bootstrap/registrar/HooksRegistrarTest.java @@ -0,0 +1,245 @@ +package com.diamonddagger590.mccore.bootstrap.registrar; + +import com.diamonddagger590.mccore.CorePlugin; +import com.diamonddagger590.mccore.bootstrap.BootstrapContext; +import com.diamonddagger590.mccore.bootstrap.StartupProfile; +import com.diamonddagger590.mccore.external.citizens.CoreCitizensHook; +import com.diamonddagger590.mccore.external.cmi.CoreCMIHook; +import com.diamonddagger590.mccore.external.itemsadder.CoreItemsAdderHook; +import com.diamonddagger590.mccore.external.modelengine.CoreModelEngineHook; +import com.diamonddagger590.mccore.external.mythicmobs.CoreMythicMobsHook; +import com.diamonddagger590.mccore.external.nexo.CoreNexoHook; +import com.diamonddagger590.mccore.external.papi.CorePapiHook; +import com.diamonddagger590.mccore.registry.RegistryAccess; +import com.diamonddagger590.mccore.registry.RegistryKey; +import com.diamonddagger590.mccore.registry.plugin.CorePluginHookKey; +import com.diamonddagger590.mccore.registry.plugin.PluginHookRegistry; +import com.diamonddagger590.mccore.testing.RegistryResetExtension; +import org.bukkit.Bukkit; +import org.bukkit.plugin.PluginManager; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.jetbrains.annotations.NotNull; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.logging.Logger; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class HooksRegistrarTest { + + @Mock + private CorePlugin mockPlugin; + + @Mock + private PluginManager mockPluginManager; + + private PluginHookRegistry hookRegistry; + + @BeforeEach + void setUp() { + RegistryResetExtension.setupRegistry(); + hookRegistry = RegistryAccess.registryAccess().registry(RegistryKey.PLUGIN_HOOK); + when(mockPlugin.registryAccess()).thenReturn(RegistryAccess.registryAccess()); + when(mockPlugin.getLogger()).thenReturn(Logger.getLogger("HooksRegistrarTest")); + } + + @AfterEach + void tearDown() { + RegistryResetExtension.resetRegistry(); + } + + @NotNull + private BootstrapContext context() { + return new BootstrapContext<>(mockPlugin, StartupProfile.PROD); + } + + private void enablePlugin(@NotNull String name) { + when(mockPluginManager.isPluginEnabled(name)).thenReturn(true); + } + + private void enableAllPlugins() { + enablePlugin("Nexo"); + enablePlugin("ItemsAdder"); + enablePlugin("PlaceholderAPI"); + enablePlugin("ModelEngine"); + enablePlugin("MythicMobs"); + enablePlugin("CMI"); + enablePlugin("Citizens"); + } + + @Test + @DisplayName("Given no plugins enabled, when registering hooks, then no hooks are registered") + void register_registersNoHooks_whenNoPluginsEnabled() { + try (MockedStatic bukkit = mockStatic(Bukkit.class)) { + bukkit.when(Bukkit::getPluginManager).thenReturn(mockPluginManager); + when(mockPluginManager.isPluginEnabled(anyString())).thenReturn(false); + + new HooksRegistrar().register(context()); + + assertFalse(hookRegistry.pluginHook(CorePluginHookKey.CORE_NEXO).isPresent()); + assertFalse(hookRegistry.pluginHook(CorePluginHookKey.CORE_ITEMS_ADDER).isPresent()); + assertFalse(hookRegistry.pluginHook(CorePluginHookKey.CORE_PAPI).isPresent()); + assertFalse(hookRegistry.pluginHook(CorePluginHookKey.CORE_MODEL_ENGINE).isPresent()); + assertFalse(hookRegistry.pluginHook(CorePluginHookKey.CORE_MYTHIC_MOBS).isPresent()); + assertFalse(hookRegistry.pluginHook(CorePluginHookKey.CORE_CMI).isPresent()); + assertFalse(hookRegistry.pluginHook(CorePluginHookKey.CORE_CITIZENS).isPresent()); + } + } + + @Test + @DisplayName("Given Nexo enabled, when registering hooks, then Nexo hook is registered") + void register_registersNexoHook_whenNexoEnabled() { + try (MockedStatic bukkit = mockStatic(Bukkit.class)) { + bukkit.when(Bukkit::getPluginManager).thenReturn(mockPluginManager); + when(mockPluginManager.isPluginEnabled(anyString())).thenReturn(false); + enablePlugin("Nexo"); + + new HooksRegistrar().register(context()); + + assertTrue(hookRegistry.pluginHook(CorePluginHookKey.CORE_NEXO).isPresent()); + assertTrue(hookRegistry.pluginHook(CorePluginHookKey.CORE_NEXO).get() instanceof CoreNexoHook); + } + } + + @Test + @DisplayName("Given ItemsAdder enabled, when registering hooks, then ItemsAdder hook is registered") + void register_registersItemsAdderHook_whenItemsAdderEnabled() { + try (MockedStatic bukkit = mockStatic(Bukkit.class)) { + bukkit.when(Bukkit::getPluginManager).thenReturn(mockPluginManager); + when(mockPluginManager.isPluginEnabled(anyString())).thenReturn(false); + enablePlugin("ItemsAdder"); + + new HooksRegistrar().register(context()); + + assertTrue(hookRegistry.pluginHook(CorePluginHookKey.CORE_ITEMS_ADDER).isPresent()); + assertTrue(hookRegistry.pluginHook(CorePluginHookKey.CORE_ITEMS_ADDER).get() instanceof CoreItemsAdderHook); + } + } + + @Test + @DisplayName("Given PlaceholderAPI enabled, when registering hooks, then PAPI hook is registered") + void register_registersPapiHook_whenPlaceholderApiEnabled() { + try (MockedStatic bukkit = mockStatic(Bukkit.class)) { + bukkit.when(Bukkit::getPluginManager).thenReturn(mockPluginManager); + when(mockPluginManager.isPluginEnabled(anyString())).thenReturn(false); + enablePlugin("PlaceholderAPI"); + + new HooksRegistrar().register(context()); + + assertTrue(hookRegistry.pluginHook(CorePluginHookKey.CORE_PAPI).isPresent()); + assertTrue(hookRegistry.pluginHook(CorePluginHookKey.CORE_PAPI).get() instanceof CorePapiHook); + } + } + + @Test + @DisplayName("Given ModelEngine enabled, when registering hooks, then ModelEngine hook is registered") + void register_registersModelEngineHook_whenModelEngineEnabled() { + try (MockedStatic bukkit = mockStatic(Bukkit.class)) { + bukkit.when(Bukkit::getPluginManager).thenReturn(mockPluginManager); + when(mockPluginManager.isPluginEnabled(anyString())).thenReturn(false); + enablePlugin("ModelEngine"); + + new HooksRegistrar().register(context()); + + assertTrue(hookRegistry.pluginHook(CorePluginHookKey.CORE_MODEL_ENGINE).isPresent()); + assertTrue(hookRegistry.pluginHook(CorePluginHookKey.CORE_MODEL_ENGINE).get() instanceof CoreModelEngineHook); + } + } + + @Test + @DisplayName("Given MythicMobs enabled, when registering hooks, then MythicMobs hook is registered") + void register_registersMythicMobsHook_whenMythicMobsEnabled() { + try (MockedStatic bukkit = mockStatic(Bukkit.class)) { + bukkit.when(Bukkit::getPluginManager).thenReturn(mockPluginManager); + when(mockPluginManager.isPluginEnabled(anyString())).thenReturn(false); + enablePlugin("MythicMobs"); + + new HooksRegistrar().register(context()); + + assertTrue(hookRegistry.pluginHook(CorePluginHookKey.CORE_MYTHIC_MOBS).isPresent()); + assertTrue(hookRegistry.pluginHook(CorePluginHookKey.CORE_MYTHIC_MOBS).get() instanceof CoreMythicMobsHook); + } + } + + @Test + @DisplayName("Given CMI enabled, when registering hooks, then CMI hook is registered") + void register_registersCmiHook_whenCmiEnabled() { + try (MockedStatic bukkit = mockStatic(Bukkit.class)) { + bukkit.when(Bukkit::getPluginManager).thenReturn(mockPluginManager); + when(mockPluginManager.isPluginEnabled(anyString())).thenReturn(false); + enablePlugin("CMI"); + + new HooksRegistrar().register(context()); + + assertTrue(hookRegistry.pluginHook(CorePluginHookKey.CORE_CMI).isPresent()); + assertTrue(hookRegistry.pluginHook(CorePluginHookKey.CORE_CMI).get() instanceof CoreCMIHook); + } + } + + @Test + @DisplayName("Given Citizens enabled, when registering hooks, then Citizens hook is registered") + void register_registersCitizensHook_whenCitizensEnabled() { + try (MockedStatic bukkit = mockStatic(Bukkit.class)) { + bukkit.when(Bukkit::getPluginManager).thenReturn(mockPluginManager); + when(mockPluginManager.isPluginEnabled(anyString())).thenReturn(false); + enablePlugin("Citizens"); + + new HooksRegistrar().register(context()); + + assertTrue(hookRegistry.pluginHook(CorePluginHookKey.CORE_CITIZENS).isPresent()); + assertTrue(hookRegistry.pluginHook(CorePluginHookKey.CORE_CITIZENS).get() instanceof CoreCitizensHook); + } + } + + @Test + @DisplayName("Given all testable plugins enabled, when registering hooks, then all 7 hooks are registered") + void register_registersAllHooks_whenAllPluginsEnabled() { + try (MockedStatic bukkit = mockStatic(Bukkit.class)) { + bukkit.when(Bukkit::getPluginManager).thenReturn(mockPluginManager); + when(mockPluginManager.isPluginEnabled(anyString())).thenReturn(false); + enableAllPlugins(); + + new HooksRegistrar().register(context()); + + assertTrue(hookRegistry.pluginHook(CorePluginHookKey.CORE_NEXO).isPresent()); + assertTrue(hookRegistry.pluginHook(CorePluginHookKey.CORE_ITEMS_ADDER).isPresent()); + assertTrue(hookRegistry.pluginHook(CorePluginHookKey.CORE_PAPI).isPresent()); + assertTrue(hookRegistry.pluginHook(CorePluginHookKey.CORE_MODEL_ENGINE).isPresent()); + assertTrue(hookRegistry.pluginHook(CorePluginHookKey.CORE_MYTHIC_MOBS).isPresent()); + assertTrue(hookRegistry.pluginHook(CorePluginHookKey.CORE_CMI).isPresent()); + assertTrue(hookRegistry.pluginHook(CorePluginHookKey.CORE_CITIZENS).isPresent()); + } + } + + @Test + @DisplayName("Given only a subset of plugins enabled, when registering hooks, then only those hooks are registered") + void register_registersOnlyEnabledHooks_whenSubsetEnabled() { + try (MockedStatic bukkit = mockStatic(Bukkit.class)) { + bukkit.when(Bukkit::getPluginManager).thenReturn(mockPluginManager); + when(mockPluginManager.isPluginEnabled(anyString())).thenReturn(false); + enablePlugin("Nexo"); + enablePlugin("Citizens"); + + new HooksRegistrar().register(context()); + + assertTrue(hookRegistry.pluginHook(CorePluginHookKey.CORE_NEXO).isPresent()); + assertTrue(hookRegistry.pluginHook(CorePluginHookKey.CORE_CITIZENS).isPresent()); + assertFalse(hookRegistry.pluginHook(CorePluginHookKey.CORE_ITEMS_ADDER).isPresent()); + assertFalse(hookRegistry.pluginHook(CorePluginHookKey.CORE_PAPI).isPresent()); + assertFalse(hookRegistry.pluginHook(CorePluginHookKey.CORE_MODEL_ENGINE).isPresent()); + assertFalse(hookRegistry.pluginHook(CorePluginHookKey.CORE_MYTHIC_MOBS).isPresent()); + assertFalse(hookRegistry.pluginHook(CorePluginHookKey.CORE_CMI).isPresent()); + } + } +} diff --git a/src/test/java/com/diamonddagger590/mccore/parser/SfunTest.java b/src/test/java/com/diamonddagger590/mccore/parser/SfunTest.java index 7837a2f..0f6e53b 100644 --- a/src/test/java/com/diamonddagger590/mccore/parser/SfunTest.java +++ b/src/test/java/com/diamonddagger590/mccore/parser/SfunTest.java @@ -156,6 +156,29 @@ void cosh_isEvenFunction_whenComparingPositiveAndNegative() { void cosh_returnsNaN_whenInputIsNaN() { assertTrue(Double.isNaN(Sfun.cosh(Double.NaN))); } + + @Test + @DisplayName("Given positive infinity, when computing cosh, then returns infinity") + void cosh_returnsInfinity_whenInputIsPositiveInfinity() { + double result = Sfun.cosh(Double.POSITIVE_INFINITY); + assertEquals(Double.POSITIVE_INFINITY, result); + } + + @Test + @DisplayName("Given negative infinity, when computing cosh, then returns negative infinity") + void cosh_returnsNegativeInfinity_whenInputIsNegativeInfinity() { + double result = Sfun.cosh(Double.NEGATIVE_INFINITY); + assertEquals(Double.NEGATIVE_INFINITY, result); + } + + @Test + @DisplayName("Given very large x, when computing cosh, then uses large-value formula") + void cosh_returnsCorrectValue_whenXIsVeryLarge() { + double x = 40.0; + double y = Math.exp(x); + double expected = 0.5 * y; + assertEquals(expected, Sfun.cosh(x), expected * 1e-10); + } @Test @DisplayName("Given x = 0, when computing sinh, then returns 0") void sinh_returnsZero_whenXIsZero() { @@ -247,6 +270,13 @@ void tanh_returnsCorrectValue_whenXIsInMediumRange() { double expected = (y - 1.0 / y) / (y + 1.0 / y); assertEquals(expected, Sfun.tanh(x), DELTA); } + + @Test + @DisplayName("Given negative x in medium range, when computing tanh, then returns negative value") + void tanh_returnsNegative_whenXIsNegativeMediumRange() { + double x = -3.0; + assertEquals(-Sfun.tanh(3.0), Sfun.tanh(x), DELTA); + } @Test @DisplayName("Given x = pi/4, when computing cot, then returns approximately 1") void cot_returnsOne_whenXIsPiOverFour() { @@ -346,6 +376,38 @@ void erfc_returnsCorrectValue_whenXGreaterThanFour() { double result = Sfun.erfc(5.0); assertTrue(result >= 0.0 && result < 1e-10); } + + @Test + @DisplayName("Given negative x between -4 and -1, when computing erfc, then applies 2-ans correction") + void erfc_returnsCorrectValue_whenXIsNegativeBetweenOneAndFour() { + double result = Sfun.erfc(-2.0); + assertTrue(result > 1.0 && result < 2.0); + assertEquals(1.0, Sfun.erf(-2.0) + Sfun.erfc(-2.0), DELTA); + } + + @Test + @DisplayName("Given negative x beyond -4, when computing erfc, then applies 2-ans correction via erfcc series") + void erfc_returnsCorrectValue_whenXIsNegativeBeyondFour() { + double result = Sfun.erfc(-5.0); + assertTrue(result > 1.99); + assertEquals(1.0, Sfun.erf(-5.0) + Sfun.erfc(-5.0), DELTA); + } + + @Test + @DisplayName("Given x between 0 and 1, when computing erfc, then uses erfc series directly") + void erfc_returnsCorrectValue_whenXBetweenZeroAndOne() { + double result = Sfun.erfc(0.5); + assertTrue(result > 0.0 && result < 1.0); + assertEquals(1.0, Sfun.erf(0.5) + Sfun.erfc(0.5), DELTA); + } + + @Test + @DisplayName("Given very small negative x, when computing erfc, then returns value near 1") + void erfc_returnsNearOne_whenXIsVerySmallNegative() { + double result = Sfun.erfc(-1e-10); + assertTrue(result > 1.0); + assertEquals(1.0, Sfun.erf(-1e-10) + Sfun.erfc(-1e-10), DELTA); + } @Test @DisplayName("Given n = 0, when computing factorial, then returns 1") void fact_returnsOne_whenNIsZero() { diff --git a/src/test/java/com/diamonddagger590/mccore/registry/manager/ManagerRegistryTest.java b/src/test/java/com/diamonddagger590/mccore/registry/manager/ManagerRegistryTest.java index 1c78c43..cc41d02 100644 --- a/src/test/java/com/diamonddagger590/mccore/registry/manager/ManagerRegistryTest.java +++ b/src/test/java/com/diamonddagger590/mccore/registry/manager/ManagerRegistryTest.java @@ -132,4 +132,19 @@ void registered_returnsTrue_whenSubclassRegisteredAndCheckedByParentKey() { void manager_returnsNull_whenNoManagerRegisteredForKey() { assertNull(registry().manager(KEY_A)); } + + @Test + @DisplayName("Given a different manager registered, when retrieving by non-matching key, then returns null via fallback") + void manager_returnsNull_whenDifferentManagerRegisteredAndKeyDoesNotMatch() { + registry().register(new TestManagerB()); + assertNull(registry().manager(KEY_A)); + } + + @Test + @DisplayName("Given a subclass manager registered, when checking registered by subclass instance, then returns true") + void registered_returnsTrue_whenSubclassInstanceRegisteredAndCheckedBySubclassInstance() { + TestManagerASub subManager = new TestManagerASub(); + registry().register(subManager); + assertTrue(registry().registered(new TestManagerASub())); + } }