From 3ab8972ad9f9f6a849cdd0a0b98921852dec3087 Mon Sep 17 00:00:00 2001
From: Joshua Sing Allows for runtime dependency loading, etc. before Chameleon is actually loaded. Allows you to perform actions and load extensions before Chameleon has been loaded.
Dispatched when Chameleon#onDisable is called by the plugin.
+ */ +public final class ChameleonDisableEvent implements ChameleonEvent { + + private final @NotNull Chameleon chameleon; + + /** + * ChameleonDisableEvent constructor. + *This event is intended to be dispatched internally by Chameleon only.
+ * + * @param chameleon Chameleon instance. + */ + @Internal + public ChameleonDisableEvent(@NotNull Chameleon chameleon) { + this.chameleon = chameleon; + } + + /** + * Get the Chameleon instance that triggered this event. + * + * @return Chameleon instance. + */ + public @NotNull Chameleon chameleon() { + return this.chameleon; + } + +} diff --git a/api/src/main/java/dev/hypera/chameleon/event/common/ChameleonEnableEvent.java b/api/src/main/java/dev/hypera/chameleon/event/common/ChameleonEnableEvent.java new file mode 100644 index 00000000..f6b324d8 --- /dev/null +++ b/api/src/main/java/dev/hypera/chameleon/event/common/ChameleonEnableEvent.java @@ -0,0 +1,59 @@ +/* + * This file is a part of the Chameleon Framework, licensed under the MIT License. + * + * Copyright (c) 2021-2023 The Chameleon Framework Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.hypera.chameleon.event.common; + +import dev.hypera.chameleon.Chameleon; +import dev.hypera.chameleon.event.ChameleonEvent; +import org.jetbrains.annotations.ApiStatus.Internal; +import org.jetbrains.annotations.NotNull; + +/** + * Chameleon enable event. + *Dispatched when Chameleon#onLoad is called by the plugin.
+ */ +public final class ChameleonEnableEvent implements ChameleonEvent { + + private final @NotNull Chameleon chameleon; + + /** + * ChameleonEnableEvent constructor. + *This event is intended to be dispatched internally by Chameleon only.
+ * + * @param chameleon Chameleon instance. + */ + @Internal + public ChameleonEnableEvent(@NotNull Chameleon chameleon) { + this.chameleon = chameleon; + } + + /** + * Get the Chameleon instance that triggered this event. + * + * @return Chameleon instance. + */ + public @NotNull Chameleon chameleon() { + return this.chameleon; + } + +} diff --git a/api/src/main/java/dev/hypera/chameleon/event/common/ChameleonLoadEvent.java b/api/src/main/java/dev/hypera/chameleon/event/common/ChameleonLoadEvent.java new file mode 100644 index 00000000..3c0cd5ad --- /dev/null +++ b/api/src/main/java/dev/hypera/chameleon/event/common/ChameleonLoadEvent.java @@ -0,0 +1,59 @@ +/* + * This file is a part of the Chameleon Framework, licensed under the MIT License. + * + * Copyright (c) 2021-2023 The Chameleon Framework Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.hypera.chameleon.event.common; + +import dev.hypera.chameleon.Chameleon; +import dev.hypera.chameleon.event.ChameleonEvent; +import org.jetbrains.annotations.ApiStatus.Internal; +import org.jetbrains.annotations.NotNull; + +/** + * Chameleon load event. + *Dispatched when Chameleon#onLoad is called by the plugin.
+ */ +public final class ChameleonLoadEvent implements ChameleonEvent { + + private final @NotNull Chameleon chameleon; + + /** + * ChameleonLoadEvent constructor. + *This event is intended to be dispatched internally by Chameleon only.
+ * + * @param chameleon Chameleon instance. + */ + @Internal + public ChameleonLoadEvent(@NotNull Chameleon chameleon) { + this.chameleon = chameleon; + } + + /** + * Get the Chameleon instance that triggered this event. + * + * @return Chameleon instance. + */ + public @NotNull Chameleon chameleon() { + return this.chameleon; + } + +} diff --git a/api/src/main/java/dev/hypera/chameleon/exception/extension/ChameleonExtensionException.java b/api/src/main/java/dev/hypera/chameleon/exception/extension/ChameleonExtensionException.java index 3359c784..606570ad 100644 --- a/api/src/main/java/dev/hypera/chameleon/exception/extension/ChameleonExtensionException.java +++ b/api/src/main/java/dev/hypera/chameleon/exception/extension/ChameleonExtensionException.java @@ -24,7 +24,7 @@ package dev.hypera.chameleon.exception.extension; import dev.hypera.chameleon.exception.ChameleonRuntimeException; - +import org.jetbrains.annotations.NotNull; /** * Chameleon extension exception. @@ -72,4 +72,16 @@ protected ChameleonExtensionException(String message, Throwable cause, boolean e super(message, cause, enableSuppression, writableStackTrace); } + /** + * Returns a new Chameleon extension exception with the formatted message. + * + * @param message Message to format. + * @param args Format args. + * + * @return created Chameleon extension exception. + */ + public static @NotNull ChameleonExtensionException create(@NotNull String message, @NotNull Object... args) { + return new ChameleonExtensionException(String.format(message, args)); + } + } diff --git a/api/src/main/java/dev/hypera/chameleon/extension/ChameleonExtension.java b/api/src/main/java/dev/hypera/chameleon/extension/ChameleonExtension.java index 3bacc747..7b9e45b0 100644 --- a/api/src/main/java/dev/hypera/chameleon/extension/ChameleonExtension.java +++ b/api/src/main/java/dev/hypera/chameleon/extension/ChameleonExtension.java @@ -23,55 +23,11 @@ */ package dev.hypera.chameleon.extension; -import dev.hypera.chameleon.Chameleon; -import org.jetbrains.annotations.NotNull; - /** * Extension. * - * @paramNote that the returned ChameleonPlatformExtension must implement + * {@code T}.
+ * + * @param platformId Platform identifier to create the extension for. This identifier can be + * compared against known platform identifiers stored as constants in + * {@link dev.hypera.chameleon.platform.Platform}. + * + * @return new extension instance. + * @throws ChameleonExtensionException if something goes wrong while creating the extension. + */ + @NotNull ChameleonPlatformExtension create(@NotNull String platformId) throws ChameleonExtensionException; + + /** + * Returns the dependencies this extension requires on the given platform. + * + * @param platformId Platform identifier. + * + * @return collection of dependencies. + */ + default @NotNull CollectionClasses implementing this interface must also implement {@code T}.
* - * @paramThis method will be called when the Extension is initialised by Chameleon, either before + * Chameleon is constructed, or when EventManager#loadExtension is called.
+ * + * @param logger Logger. + * @param eventBus Event bus. */ - public void onLoad(@NotNull C chameleon) { - - } + void init(@NotNull ChameleonLogger logger, @NotNull EventBus eventBus) throws ChameleonExtensionException; /** - * Get Chameleon extension instance. + * Extension load. + * + *This method will be called when Chameleon has finished loading, or when + * EventManager#loadExtension is called after Chameleon has loaded.
+ * + *If your extension is platform dependant, then you can cast {@code chameleon} to the + * platform Chameleon implementation, e.g. BukkitChameleon, BungeeCordChameleon, etc.
* - * @return Chameleon extension instance. + * @param chameleon Chameleon instance. */ - public final @NotNull T getExtension() { - return this.extension; - } + void load(@NotNull Chameleon chameleon); } diff --git a/api/src/main/java/dev/hypera/chameleon/extension/ExtensionManager.java b/api/src/main/java/dev/hypera/chameleon/extension/ExtensionManager.java new file mode 100644 index 00000000..2c9c3995 --- /dev/null +++ b/api/src/main/java/dev/hypera/chameleon/extension/ExtensionManager.java @@ -0,0 +1,75 @@ +/* + * This file is a part of the Chameleon Framework, licensed under the MIT License. + * + * Copyright (c) 2021-2023 The Chameleon Framework Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.hypera.chameleon.extension; + +import dev.hypera.chameleon.exception.extension.ChameleonExtensionException; +import java.util.Collection; +import java.util.Optional; +import org.jetbrains.annotations.ApiStatus.NonExtendable; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +/** + * Extension manager. + * + * @see ChameleonExtension + * @see ChameleonPlatformExtension + * @see ChameleonExtensionFactory + */ +@NonExtendable +public interface ExtensionManager { + + /** + * Load a Chameleon extension. + * + * @param factory The factory to create the Chameleon extension. + * @paramThis is not the best directed graph implementation, however it is enough for what Chameleon + * currently needs.
+ *If you know more about graphs and want to improve this implementation, or maybe add other + * graph implementations, please create a pull request :)
+ * + * @paramThis is equivalent to the union of {@link #predecessors(Object)} and + * {@link #successors(Object)}
+ * + * @param node Node. + * + * @return adjacent nodes to {@code node}. + */ + @Contract(value = "_ -> _", pure = true) + @NotNull CollectionIn an undirected graph this is equivalent to {@link #adjacentNodes(Object)}.
+ * + * @param node Node to get predecessors. + * + * @return predecessors to {@code node}. + */ + @Contract(value = "_ -> _", pure = true) + @NotNull CollectionIn an undirected graph this is equivalent to {@link #adjacentNodes(Object)}.
+ * + * @param node Node to get successors of. + * + * @return successors to {@code node}. + */ + @Contract(value = "_ -> _", pure = true) + @NotNull CollectionAll edges connecting to {@code node} will also be removed.
+ * + * @param node Node to be removed. + * + * @return {@code true} if the graph changed as a result of this call. + */ + @Contract("_ -> _") + boolean removeNode(@NotNull T node); + + /** + * Adds an edge connecting {@code source} and {@code target}, if not already present. + *If this graph is directed, the edge will also be directed; otherwise it will be + * undirected.
+ * + * @param source Source node. + * @param target Target node. + * + * @return {@code true} if the graph changed as a result of this call. + * @throws IllegalArgumentException if this graph does not allow self-loops and this edge is a + * self-loop. + */ + @Contract("_, _ -> _") + default boolean putEdge(@NotNull T source, @NotNull T target) { + return putEdge(Edge.of(source, target)); + } + + /** + * Adds an edge connecting {@link Edge#source()} and {@link Edge#target()}, if not already + * present. + *If this graph is directed, the edge will also be directed; otherwise it will be + * undirected.
+ * + * @param edge Edge to be added. + * + * @return {@code true} if the graph changed as a result of this call. + * @throws IllegalArgumentException if this graph does not allow self-loops and this edge is a + * self-loop. + */ + @Contract("_ -> _") + boolean putEdge(@NotNull EdgeAttempting to add a self-loop to a graph with self-loops not allowed will cause an + * IllegalArgumentException to be thrown.
+ * + *Defaults to {@code false}.
+ * + * @param allowSelfLoops Whether self-loops should be allowed. + * + * @return {@code this}. + */ + @Contract("_ -> this") + @NotNull BuilderThis will return the common name of the API that is in use, e.g. "BungeeCord" or + * "Velocity".
+ * + * @return Platform identifier. + */ + @Override + public @NotNull String getId() { + return TestChameleon.PLATFORM_ID; + } + + /** + * Get the friendly name of this Platform. + *This will return the name provided by the Platform, which may not match the name of the + * API that is in use.
+ * + * @return Platform friendly name. + */ + @Override + public @NotNull String getName() { + return "Test"; + } + + /** + * Get the version of this Platform. + *This will return the version provided by the Platform.
+ * + * @return Platform version. + */ + @Override + public @NotNull String getVersion() { + return Chameleon.getVersion() + "-" + Chameleon.getCommit(); + } + +} diff --git a/api/src/test/java/dev/hypera/chameleon/DummyChameleonPlugin.java b/api/src/test/java/dev/hypera/chameleon/TestChameleonPlugin.java similarity index 93% rename from api/src/test/java/dev/hypera/chameleon/DummyChameleonPlugin.java rename to api/src/test/java/dev/hypera/chameleon/TestChameleonPlugin.java index ee195074..62dbaf8c 100644 --- a/api/src/test/java/dev/hypera/chameleon/DummyChameleonPlugin.java +++ b/api/src/test/java/dev/hypera/chameleon/TestChameleonPlugin.java @@ -28,14 +28,14 @@ /** * Dummy Chameleon plugin implementation. */ -public final class DummyChameleonPlugin extends ChameleonPlugin { +public final class TestChameleonPlugin extends ChameleonPlugin { /** * Chameleon plugin constructor. * * @param chameleon Chameleon implementation. */ - public DummyChameleonPlugin(@NotNull Chameleon chameleon) { + public TestChameleonPlugin(@NotNull Chameleon chameleon) { super(chameleon); } diff --git a/api/src/test/java/dev/hypera/chameleon/adventure/ReflectedAudienceTests.java b/api/src/test/java/dev/hypera/chameleon/adventure/ReflectedAudienceTests.java index 0f4c7580..48126682 100644 --- a/api/src/test/java/dev/hypera/chameleon/adventure/ReflectedAudienceTests.java +++ b/api/src/test/java/dev/hypera/chameleon/adventure/ReflectedAudienceTests.java @@ -33,7 +33,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import dev.hypera.chameleon.DummyChameleon; +import dev.hypera.chameleon.TestChameleon; import dev.hypera.chameleon.adventure.mapper.AdventureMapper; import dev.hypera.chameleon.adventure.matches.BossBarMatcher; import dev.hypera.chameleon.adventure.matches.BoundMatcher; @@ -70,18 +70,17 @@ final class ReflectedAudienceTests { private static AdventureMapper adventureMapper; - private Audience audience; + private Audience audience = mock(Audience.class); private Audience reflectedAudience; @BeforeAll static void loadAdventureMapper() throws ChameleonInstantiationException { - adventureMapper = new AdventureMapper(new DummyChameleon()); + adventureMapper = new AdventureMapper(new TestChameleon()); assertDoesNotThrow(adventureMapper::load); } @BeforeEach void setup() { - // Note: the first time #mock is called, it may take up to 1,000 ms to fully initialise. this.audience = mock(Audience.class); this.reflectedAudience = adventureMapper.createReflectedAudience(this.audience); } diff --git a/api/src/test/java/dev/hypera/chameleon/adventure/mapper/AdventureMapperTests.java b/api/src/test/java/dev/hypera/chameleon/adventure/mapper/AdventureMapperTests.java index d2357565..50a6a870 100644 --- a/api/src/test/java/dev/hypera/chameleon/adventure/mapper/AdventureMapperTests.java +++ b/api/src/test/java/dev/hypera/chameleon/adventure/mapper/AdventureMapperTests.java @@ -28,7 +28,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import dev.hypera.chameleon.DummyChameleon; +import dev.hypera.chameleon.TestChameleon; import dev.hypera.chameleon.exception.instantiation.ChameleonInstantiationException; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -39,7 +39,7 @@ final class AdventureMapperTests { @BeforeAll static void setup() throws ChameleonInstantiationException { - mapper = new AdventureMapper(new DummyChameleon()); + mapper = new AdventureMapper(new TestChameleon()); } @Test diff --git a/api/src/test/java/dev/hypera/chameleon/extension/ExtensionMapTests.java b/api/src/test/java/dev/hypera/chameleon/extension/ExtensionMapTests.java new file mode 100644 index 00000000..44e5f971 --- /dev/null +++ b/api/src/test/java/dev/hypera/chameleon/extension/ExtensionMapTests.java @@ -0,0 +1,90 @@ +/* + * This file is a part of the Chameleon Framework, licensed under the MIT License. + * + * Copyright (c) 2021-2023 The Chameleon Framework Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package dev.hypera.chameleon.extension; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrowsExactly; + +import dev.hypera.chameleon.exception.extension.ChameleonExtensionException; +import dev.hypera.chameleon.extension.objects.Test2Extension; +import dev.hypera.chameleon.extension.objects.Test2ExtensionImpl; +import dev.hypera.chameleon.extension.objects.TestCircularDetection1Extension; +import dev.hypera.chameleon.extension.objects.TestCircularDetection1ExtensionImpl; +import dev.hypera.chameleon.extension.objects.TestCircularDetection2Extension; +import dev.hypera.chameleon.extension.objects.TestCircularDetection2ExtensionImpl; +import dev.hypera.chameleon.extension.objects.TestExtension; +import dev.hypera.chameleon.extension.objects.TestExtensionImpl; +import dev.hypera.chameleon.util.Pair; +import java.util.Arrays; +import java.util.Collections; +import org.junit.jupiter.api.Test; + +final class ExtensionMapTests { + + @Test + void testLoadSort() { + // Create extension map and extensions. + ExtensionMap extensionMap = new ExtensionMap(); + TestExtensionImpl testExtensionImpl = new TestExtensionImpl(); + Test2ExtensionImpl test2ExtensionImpl = new Test2ExtensionImpl(); + extensionMap.put(Test2Extension.class, Pair.of(test2ExtensionImpl, Arrays.asList( + ChameleonExtensionDependency.required("Test", TestExtension.class), + ChameleonExtensionDependency.optional(TestCircularDetection1Extension.class), + ChameleonExtensionDependency.optional(TestCircularDetection2Extension.class.getCanonicalName()), + ChameleonExtensionDependency.optional("dev.hypera.chameleon.nonexistant.NonexistantExtension") + ))); + extensionMap.put(TestExtension.class, Pair.of(testExtensionImpl, Collections.emptyList())); + + // Verify that the extension map contains the expected extensions. + assertThat(extensionMap).hasSize(2); + + // Verify that the extensions are sorted in the correct order. + assertThat(extensionMap.loadSort()) + .containsExactly(testExtensionImpl, test2ExtensionImpl) + .inOrder(); + } + + @Test + void testLoadSortCircular() { + // Create extension map and extensions. + ExtensionMap extensionMap = new ExtensionMap(); + TestCircularDetection1ExtensionImpl test1Impl = new TestCircularDetection1ExtensionImpl(); + TestCircularDetection2ExtensionImpl test2Impl = new TestCircularDetection2ExtensionImpl(); + extensionMap.put(TestCircularDetection1Extension.class, Pair.of(test1Impl, Collections.singleton( + ChameleonExtensionDependency.required(TestCircularDetection2Extension.class) + ))); + extensionMap.put(TestCircularDetection2Extension.class, Pair.of(test2Impl, Collections.singleton( + ChameleonExtensionDependency.required(TestCircularDetection1Extension.class) + ))); + + // Verify that the extension map contains the expected extensions. + assertThat(extensionMap).hasSize(2); + + // Verify that the circular dependencies are detected. + ChameleonExtensionException ex = assertThrowsExactly( + ChameleonExtensionException.class, extensionMap::loadSort); + assertThat(ex).hasMessageThat().contains("Detected circular dependencies"); + } + +} diff --git a/api/src/test/java/dev/hypera/chameleon/extension/ExtensionTests.java b/api/src/test/java/dev/hypera/chameleon/extension/ExtensionTests.java index 6eb906dd..8de686c5 100644 --- a/api/src/test/java/dev/hypera/chameleon/extension/ExtensionTests.java +++ b/api/src/test/java/dev/hypera/chameleon/extension/ExtensionTests.java @@ -23,27 +23,161 @@ */ package dev.hypera.chameleon.extension; -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth8.assertThat; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrowsExactly; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; -import dev.hypera.chameleon.extension.objects.TestInvalidPlatformExtension; -import dev.hypera.chameleon.extension.objects.TestPlatformExtension; +import dev.hypera.chameleon.TestChameleon; +import dev.hypera.chameleon.exception.extension.ChameleonExtensionException; +import dev.hypera.chameleon.exception.instantiation.ChameleonInstantiationException; +import dev.hypera.chameleon.extension.objects.Test2Extension; +import dev.hypera.chameleon.extension.objects.Test2ExtensionImpl; +import dev.hypera.chameleon.extension.objects.TestExtension; +import dev.hypera.chameleon.extension.objects.TestExtensionFactory; +import dev.hypera.chameleon.extension.objects.TestExtensionImpl; +import dev.hypera.chameleon.extension.objects.TestRequiredDependencyEmptyExtension; +import java.util.Collections; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; final class ExtensionTests { + private TestChameleon chameleon; + private @NotNull TestExtensionFactoryWarning: When an extension is "post loaded", the {@link ChameleonExtension#onPreLoad()} method - * will not be called.
- */ -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -public @interface PostLoadable { +public interface Test2Extension extends ChameleonExtension { + + static @NotNull TestExtensionFactoryNot final to allow Folia implementation to extend this class.
*/ @NonExtendable public class BukkitChameleon extends Chameleon { @@ -68,9 +70,15 @@ public class BukkitChameleon extends Chameleon { private @Nullable ChameleonAudienceProvider audienceProvider; @Internal - // Protected to allow Folia to extend this class. - protected BukkitChameleon(@NotNull Class extends ChameleonPlugin> chameleonPlugin, @NotNull Collection