From afde0d8628ff35ecb0c9e309e2dc16cbd4ce2f18 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Tue, 10 Feb 2026 19:06:05 -0800 Subject: [PATCH 01/23] initial test work --- trying it out, this will fail im sure --- .github/workflows/gradle.yml | 4 +- .gitignore | 3 + build.gradle.kts | 7 ++ .../api/skript/testing/TestResults.java | 71 +++++++++++ .../skript/api/skript/testing/TestRunner.java | 118 ++++++++++++++++++ .../api/skript/testing/TestingThingy.java | 113 +++++++++++++++++ .../skript/testing/elements/EffAssert.java | 67 ++++++++++ .../testing/elements/ElementHandler.java | 12 ++ .../api/skript/testing/elements/EvtTest.java | 70 +++++++++++ .../skriptdev/skript/api/utils/Utils.java | 5 + .../skriptdev/skript/plugin/Skript.java | 29 +++-- .../plugin/elements/ElementRegistration.java | 4 + src/test/skript/tests/test-script.sk | 3 + 13 files changed, 493 insertions(+), 13 deletions(-) create mode 100644 src/main/java/com/github/skriptdev/skript/api/skript/testing/TestResults.java create mode 100644 src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunner.java create mode 100644 src/main/java/com/github/skriptdev/skript/api/skript/testing/TestingThingy.java create mode 100644 src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/EffAssert.java create mode 100644 src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/ElementHandler.java create mode 100644 src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/EvtTest.java create mode 100644 src/test/skript/tests/test-script.sk diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 1c4a8308..113361a3 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -5,7 +5,7 @@ # This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle -name: Java CI with Gradle +name: Build, Test, and Upload Artifact on: push: @@ -38,3 +38,5 @@ jobs: name: HySkript-Artifact # A file, directory or wildcard pattern that describes what to upload path: build/libs/HySkript-*.jar + - name: Run Tests + run: ./gradlew testRunner diff --git a/.gitignore b/.gitignore index 8098e8e9..95247ca2 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,6 @@ bin/ ### Mac OS ### .DS_Store **/.DS_Store + +### Run Folder ### +run/ diff --git a/build.gradle.kts b/build.gradle.kts index 682b23c4..a06943ff 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -42,6 +42,13 @@ tasks { destinationDir = file("/Users/ShaneBee/Desktop/Server/Hytale/Creative/mods/") } } + register("testRunner") { + dependsOn("jar") + group = "application" + description = "Runs the test runner" + mainClass.set("com.github.skriptdev.skript.api.skript.testing.TestRunner") + classpath = sourceSets["main"].runtimeClasspath + } processResources { filesNotMatching("assets/**") { expand("pluginVersion" to projectVersion, "hytaleVersion" to hytaleVersion) diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestResults.java b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestResults.java new file mode 100644 index 00000000..0c3326d1 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestResults.java @@ -0,0 +1,71 @@ +package com.github.skriptdev.skript.api.skript.testing; + +import com.github.skriptdev.skript.plugin.HySk; + +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +public class TestResults { + + private boolean success = true; + private final Map successMap = new HashMap<>(); + private final Map failureMap = new HashMap<>(); + + public boolean isSuccess() { + return success; + } + + public Map getSuccessMap() { + return successMap; + } + + public Map getFailureMap() { + return failureMap; + } + + public void addSuccess(String test, String value) { + this.successMap.put(test, value); + } + + public void addFailure(String test, String value) { + this.success = false; + this.failureMap.put(test, value); + } + + @SuppressWarnings({"ResultOfMethodCallIgnored", "CallToPrintStackTrace"}) + public void printToJsonFile() { + Path resolve = HySk.getInstance().getDataDirectory().resolve("test-results.properties"); + try { + Files.createDirectories(resolve.getParent()); + } catch (IOException e) { + throw new RuntimeException("Failed to create directories for " + resolve.toAbsolutePath(), e); + } + + Properties props = new Properties(); + props.setProperty("success", Boolean.toString(this.success)); + props.setProperty("success.count", Integer.toString(this.successMap.size())); + props.setProperty("failure.count", Integer.toString(this.failureMap.size())); + + // Store individual entries (handy for debugging in CI logs) + for (var e : successMap.entrySet()) { + props.setProperty("success." + e.getKey(), e.getValue()); + } + for (var e : failureMap.entrySet()) { + props.setProperty("failure." + e.getKey(), e.getValue()); + } + + try (var out = new OutputStreamWriter(Files.newOutputStream(resolve), StandardCharsets.UTF_8)) { + props.store(out, "HySkript test results"); + System.out.println("Test-Results successfully written to " + resolve.toAbsolutePath()); + } catch (Exception e) { + e.printStackTrace(); + } + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunner.java b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunner.java new file mode 100644 index 00000000..31756b6c --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunner.java @@ -0,0 +1,118 @@ +package com.github.skriptdev.skript.api.skript.testing; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.Properties; + +public class TestRunner { + + static void main(String[] args) { + System.out.println("Downloading Hytale Server..."); + downloadHytaleServer(); + System.out.println("Download complete!"); + + // move plugin to mods file + System.out.println("Moving plugin to mods folder..."); + movePlugin(); + System.out.println("Plugin moved!"); + + // set system property for testing + System.out.println("Starting server..."); + runServer(); + System.out.println("Server started!"); + } + + private static void downloadHytaleServer() { + String url = "https://maven.hytale.com/release/com/hypixel/hytale/Server" + + "/2026.02.06-aa1b071c2/Server-2026.02.06-aa1b071c2.jar"; // TODO replace with gradle replace + String targetDir = "run/testServer/"; + String newName = "HytaleServer.jar"; + + try { + // 1. Create the directory if it doesn't exist + Path directoryPath = Paths.get(targetDir); + if (!Files.exists(directoryPath)) { + Files.createDirectories(directoryPath); + } + + // 2. Download the file to a temporary location + HttpClient client = HttpClient.newHttpClient(); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(url)) + .build(); + + // We download directly to a path to save memory + Path tempFile = Files.createTempFile("tempDownload", ".tmp"); + client.send(request, HttpResponse.BodyHandlers.ofFile(tempFile)); + + // 3. Move and Rename the file + Path finalPath = directoryPath.resolve(newName); + Files.move(tempFile, finalPath, StandardCopyOption.REPLACE_EXISTING); + + System.out.println("File saved to: " + finalPath.toAbsolutePath()); + + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + } + } + + private static void movePlugin() { + File file = new File("run/testServer/mods"); + file.mkdirs(); + try { + Files.copy(Path.of("build/libs/HySkript-1.0.0-pre-release-3.jar"), + Path.of("run/testServer/mods/HySkript-1.0.0-pre-release-3.jar"), + StandardCopyOption.REPLACE_EXISTING); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private static void runServer() { + try { + File serverFolder = new File("run/testServer/"); + ProcessBuilder processBuilder = new ProcessBuilder( + "java", + "-Xms2G", + "-Xmx2G", + "-jar", "HytaleServer.jar", + "--assets", "/Users/ShaneBee/Desktop/Server/Hytale/Assets/Assets.zip" + ); + processBuilder.inheritIO(); + processBuilder.directory(serverFolder); + Process process = processBuilder.start(); + int exitCode = process.waitFor(); + + // Read results written by the plugin (no Gson required). + Path resultsPath = Path.of("run/testServer/mods/skript_HySkript/test-results.properties"); + if (!Files.exists(resultsPath)) { + throw new IllegalStateException( + "Test results file not found at " + resultsPath.toAbsolutePath() + + " (server exit code was " + exitCode + ")" + ); + } + + Properties props = new Properties(); + try (var reader = Files.newBufferedReader(resultsPath, StandardCharsets.UTF_8)) { + props.load(reader); + } + + int failureCount = Integer.parseInt(props.getProperty("failure.count", "0")); + + System.out.println("Exited with code " + failureCount); + System.exit(failureCount); + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + } + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestingThingy.java b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestingThingy.java new file mode 100644 index 00000000..1b8b6aeb --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestingThingy.java @@ -0,0 +1,113 @@ +package com.github.skriptdev.skript.api.skript.testing; + +import com.github.skriptdev.skript.api.skript.testing.elements.EvtTest.TestContext; +import com.github.skriptdev.skript.api.utils.Utils; +import com.github.skriptdev.skript.plugin.Skript; +import com.hypixel.hytale.server.core.HytaleServer; +import com.hypixel.hytale.server.core.Message; +import com.hypixel.hytale.server.core.universe.Universe; +import com.hypixel.hytale.server.core.universe.world.World; +import com.hypixel.hytale.server.core.util.MessageUtil; +import fi.sulku.hytale.TinyMsg; +import io.github.syst3ms.skriptparser.lang.TriggerMap; +import io.github.syst3ms.skriptparser.log.LogEntry; +import io.github.syst3ms.skriptparser.parsing.ScriptLoader; + +import java.io.File; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.concurrent.TimeUnit; + +public class TestingThingy { + + private static Skript skript; + + @SuppressWarnings("DataFlowIssue") + public static void start(Skript skript) { + TestingThingy.skript = skript; + + Runnable runTestsInWorld = () -> { + Utils.log("Running tests in world 'default'..."); + runTests(); + Utils.log("Finished running tests!"); + }; + Runnable loadTestsRunnable = () -> { + Utils.log("Loading test scripts..."); + loadTests(); + Utils.log("Finished loading test scripts!"); + World world = Universe.get().getWorld("default"); + if (world.isPaused()) world.setPaused(false); + + world.execute(runTestsInWorld); + }; + HytaleServer.SCHEDULED_EXECUTOR.schedule(loadTestsRunnable, 2, TimeUnit.SECONDS); + } + + private static void loadTests() { + Path path = Path.of("/Users/ShaneBee/IdeaProjects/HySkript/HySkript/src/test/skript/tests"); + loadScripts(path); + } + + private static void runTests() { + + TestResults testResults = new TestResults(); + + TestContext testContext = new TestContext(testResults); + TriggerMap.callTriggersByContext(testContext); + + if (testResults.isSuccess()) { + Message message = TinyMsg.parse("All tests passed!"); + Utils.log(MessageUtil.toAnsiString(message).toAnsi()); + } else { + Utils.error("Some tests failed!"); + } + + testResults.printToJsonFile(); + + // Figure out how to fail the build on GitHub + Runnable shutdownServer = () -> HytaleServer.get().shutdownServer(); + HytaleServer.SCHEDULED_EXECUTOR.schedule(shutdownServer, 5, TimeUnit.SECONDS); + } + + private static void loadScripts(Path directory) { + File scriptsDirectory = directory.toFile(); + Utils.log("Loading test directory '" + scriptsDirectory.getAbsolutePath() + "'..."); + List scriptNames = loadScriptsInDirectory(scriptsDirectory); + Utils.log("Loaded " + scriptNames.size() + " scripts!"); + } + + private static List loadScriptsInDirectory(File directory) { + if (directory == null || !directory.isDirectory()) return List.of(); + + List loadedScripts = new ArrayList<>(); + + File[] files = directory.listFiles(); + if (files == null) return loadedScripts; + + Arrays.sort(files, + Comparator.comparing(File::isDirectory).reversed() // Directories first + .thenComparing(File::getName, String.CASE_INSENSITIVE_ORDER)); // Then sort by name alphabetically + + for (File file : files) { + // Skip disabled files and hidden files + String fileName = file.getName(); + if (fileName.startsWith("-") || fileName.startsWith(".")) continue; + if (file.isDirectory()) { + loadedScripts.addAll(loadScriptsInDirectory(file)); + } else { + if (!fileName.endsWith(".sk")) continue; + Utils.log("Loading script '" + fileName + "'..."); + List logEntries = ScriptLoader.loadScript(file.toPath(), false); + for (LogEntry logEntry : logEntries) { + Utils.log(null, logEntry); + } + loadedScripts.add(fileName.substring(0, fileName.length() - 3)); + } + } + return loadedScripts; + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/EffAssert.java b/src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/EffAssert.java new file mode 100644 index 00000000..da013b9e --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/EffAssert.java @@ -0,0 +1,67 @@ +package com.github.skriptdev.skript.api.skript.testing.elements; + +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.github.skriptdev.skript.api.skript.testing.TestResults; +import com.github.skriptdev.skript.api.skript.testing.elements.EvtTest.TestContext; +import io.github.syst3ms.skriptparser.lang.Effect; +import io.github.syst3ms.skriptparser.lang.Expression; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.log.SkriptLogger; +import io.github.syst3ms.skriptparser.parsing.ParseContext; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; + +public class EffAssert extends Effect { + + public static void register(SkriptRegistration reg) { + reg.newEffect(EffAssert.class, "assert %=boolean% with %*string%") + .register(); + } + + private String fileName; + private int lineNumber; + private Expression condition; + private Expression message; + + @SuppressWarnings("unchecked") + @Override + public boolean init(Expression[] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + this.condition = (Expression) expressions[0]; + this.message = (Expression) expressions[1]; + + SkriptLogger logger = parseContext.getLogger(); + this.fileName = logger.getFileName(); + this.lineNumber = logger.getLine() + 1; // I think it gets the last line?!?! + return true; + } + + @Override + protected void execute(@NotNull TriggerContext ctx) { + if (!(ctx instanceof TestContext context)) return; + + Optional b = this.condition.getSingle(ctx).filter(Boolean::booleanValue); + TestResults testResults = context.getTestResults(); + + if (b.isEmpty()) { + // Test failed + String message = this.message.getSingle(ctx).orElseThrow(); + String failure = String.format("assert '%s' failed with message \"%s\" {%s:%d}", + this.condition.toString(ctx, false), + message, + this.fileName, + this.lineNumber); + testResults.addFailure(context.getTestSubject(), failure); + } else { + String success = String.format("assert '%s' passed", + this.condition.toString(ctx, false)); + testResults.addSuccess(context.getTestSubject(), success); + } + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + return "assert " + this.condition.toString(ctx, debug) + " with " + this.message.toString(ctx, debug); + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/ElementHandler.java b/src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/ElementHandler.java new file mode 100644 index 00000000..9ab1a652 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/ElementHandler.java @@ -0,0 +1,12 @@ +package com.github.skriptdev.skript.api.skript.testing.elements; + +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; + +public class ElementHandler { + + public static void register(SkriptRegistration reg) { + EffAssert.register(reg); + EvtTest.register(reg); + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/EvtTest.java b/src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/EvtTest.java new file mode 100644 index 00000000..86be3f0b --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/EvtTest.java @@ -0,0 +1,70 @@ +package com.github.skriptdev.skript.api.skript.testing.elements; + +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.github.skriptdev.skript.api.skript.testing.TestResults; +import com.github.skriptdev.skript.api.utils.Utils; +import io.github.syst3ms.skriptparser.lang.Expression; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.lang.VariableString; +import io.github.syst3ms.skriptparser.lang.event.SkriptEvent; +import io.github.syst3ms.skriptparser.parsing.ParseContext; +import org.jetbrains.annotations.NotNull; + +public class EvtTest extends SkriptEvent { + + public static void register(SkriptRegistration reg) { + reg.newEvent(EvtTest.class, "test %*string%") + .setHandledContexts(TestContext.class) + .noDoc() + .register(); + } + + private String testSubject; + + @SuppressWarnings({"OptionalGetWithoutIsPresent"}) + @Override + public boolean init(Expression[] expressions, int matchedPattern, ParseContext parseContext) { + this.testSubject = ((VariableString) expressions[0]).getSingle(TriggerContext.DUMMY).get(); + return true; + } + + @Override + public boolean check(TriggerContext triggerContext) { + if (!(triggerContext instanceof TestContext context)) return false; + context.setTestSubject(this.testSubject); + Utils.logColored("Testing: \"" + this.testSubject + "\""); + return true; + } + + @Override + public String toString(@NotNull TriggerContext triggerContext, boolean b) { + return "test " + this.testSubject; + } + + public static final class TestContext implements TriggerContext { + private final TestResults testResults; + private String testSubject; + + public TestContext(TestResults testResults) { + this.testResults = testResults; + } + + public void setTestSubject(String testSubject) { + this.testSubject = testSubject; + } + + public String getTestSubject() { + return testSubject; + } + + @Override + public String getName() { + return "test context"; + } + + public TestResults getTestResults() { + return this.testResults; + } + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/api/utils/Utils.java b/src/main/java/com/github/skriptdev/skript/api/utils/Utils.java index bbbd17f5..0963c6bc 100644 --- a/src/main/java/com/github/skriptdev/skript/api/utils/Utils.java +++ b/src/main/java/com/github/skriptdev/skript/api/utils/Utils.java @@ -79,6 +79,11 @@ public static void log(String message, Object... args) { log(null, Level.INFO, message, args); } + public static void logColored(String message, Object... args) { + String ansi = MessageUtil.toAnsiString(TinyMsg.parse(message)).toAnsi(); + log(null, Level.INFO, ansi, args); + } + public static void log(IMessageReceiver receiver, String message, Object... args) { log(receiver, Level.INFO, message, args); } diff --git a/src/main/java/com/github/skriptdev/skript/plugin/Skript.java b/src/main/java/com/github/skriptdev/skript/plugin/Skript.java index e15bad3e..88c5ba1f 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/Skript.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/Skript.java @@ -6,13 +6,13 @@ import com.github.skriptdev.skript.api.skript.command.ArgUtils; import com.github.skriptdev.skript.api.skript.config.SkriptConfig; import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.github.skriptdev.skript.api.skript.testing.TestingThingy; import com.github.skriptdev.skript.api.skript.variables.JsonVariableStorage; import com.github.skriptdev.skript.api.utils.ReflectionUtils; import com.github.skriptdev.skript.api.utils.Utils; import com.github.skriptdev.skript.plugin.command.EffectCommands; import com.github.skriptdev.skript.plugin.elements.ElementRegistration; import com.github.skriptdev.skript.plugin.elements.events.EventHandler; -import com.hypixel.hytale.server.core.event.events.BootEvent; import io.github.syst3ms.skriptparser.config.Config.ConfigSection; import io.github.syst3ms.skriptparser.log.LogEntry; import io.github.syst3ms.skriptparser.log.SkriptLogger; @@ -98,15 +98,17 @@ private void setupSkript() { loadVariables(); // LOAD SCRIPTS - this.scriptsLoader = new ScriptsLoader(this); - this.scriptsLoader.loadScripts(null, this.scriptsPath, false); - - // FINALIZE SCRIPT LOADING - this.hySk.getEventRegistry().register(BootEvent.class, event -> { - Utils.debug("Hytale finished booting, starting post-load triggers..."); - // Start any post-load triggers after Hytale finishes booting. - getAddons().forEach(SkriptAddon::finishedLoading); - }); +// this.scriptsLoader = new ScriptsLoader(this); +// this.scriptsLoader.loadScripts(null, this.scriptsPath, false); +// +// // FINALIZE SCRIPT LOADING +// this.hySk.getEventRegistry().register(BootEvent.class, event -> { +// Utils.debug("Hytale finished booting, starting post-load triggers..."); +// // Start any post-load triggers after Hytale finishes booting. +// getAddons().forEach(SkriptAddon::finishedLoading); +// }); + + TestingThingy.start(this); } public void shutdown() { @@ -114,7 +116,10 @@ public void shutdown() { EventHandler.shutdown(); // SHUTDOWN SCRIPTS - this.scriptsLoader.shutdown(); + if (this.scriptsLoader != null) { + // TODO fix later + this.scriptsLoader.shutdown(); + } // SHUTDOWN VARIABLES Utils.log("Saving variables..."); @@ -189,7 +194,7 @@ private void loadVariables() { * * @return The Skript registration. */ - public @NotNull io.github.syst3ms.skriptparser.registration.SkriptRegistration getSkriptRegistration() { + public @NotNull SkriptRegistration getSkriptRegistration() { return this.registration; } diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/ElementRegistration.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/ElementRegistration.java index 7954536a..fea640b4 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/ElementRegistration.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/ElementRegistration.java @@ -1,6 +1,7 @@ package com.github.skriptdev.skript.plugin.elements; import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.github.skriptdev.skript.api.skript.testing.elements.ElementHandler; import com.github.skriptdev.skript.api.utils.Utils; import com.github.skriptdev.skript.plugin.elements.command.ScriptCommand; import com.github.skriptdev.skript.plugin.elements.command.ScriptSubCommand; @@ -61,6 +62,9 @@ public void registerElements() { ScriptCommand.register(this.registration); ScriptSubCommand.register(this.registration); + // TEST STUFF TODO do a test check + ElementHandler.register(this.registration); + // FINALIZE SETUP this.registration.register(); diff --git a/src/test/skript/tests/test-script.sk b/src/test/skript/tests/test-script.sk new file mode 100644 index 00000000..cfb0163f --- /dev/null +++ b/src/test/skript/tests/test-script.sk @@ -0,0 +1,3 @@ +test "Basic HySkript Test": + assert 1 = 1 with "1 should equal 1" + assert 2 = 2 with "2 should equal 2" From 59c17c6091f5941fade44ed67a665a4441888dab Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Tue, 10 Feb 2026 19:29:10 -0800 Subject: [PATCH 02/23] test - add temp auth --- .github/workflows/test.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..324542e6 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,11 @@ +name: Get Hytale Auth +on: workflow_dispatch # Allows you to run it manually from the "Actions" tab +jobs: + auth: + runs-on: ubuntu-latest + steps: + - name: Run Downloader + run: | + wget https://downloader.hytale.com/hytale-downloader-linux-amd64 -O downloader + chmod +x downloader + ./downloader # This will print the URL and Code to the logs From 7638dd2b92ce4f3ae0b64d2edf91dbf1a2a2fed6 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Tue, 10 Feb 2026 19:34:52 -0800 Subject: [PATCH 03/23] test - lets try this again --- .github/workflows/test.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 324542e6..bb2b375b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,5 +1,8 @@ name: Get Hytale Auth -on: workflow_dispatch # Allows you to run it manually from the "Actions" tab +on: + workflow_dispatch: # Allows you to run it manually from the "Actions" tab + push: + branches: [ dev/testing ] jobs: auth: runs-on: ubuntu-latest @@ -9,3 +12,5 @@ jobs: wget https://downloader.hytale.com/hytale-downloader-linux-amd64 -O downloader chmod +x downloader ./downloader # This will print the URL and Code to the logs + echo "URL: ${{ steps.downloader.outputs.url }}" + echo "Code: ${{ steps.downloader.outputs.code }}" From b4aeb089245e597386b294e4592c33f44c16658d Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Tue, 10 Feb 2026 19:42:38 -0800 Subject: [PATCH 04/23] delete test yml --- .github/workflows/test.yml | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index bb2b375b..00000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: Get Hytale Auth -on: - workflow_dispatch: # Allows you to run it manually from the "Actions" tab - push: - branches: [ dev/testing ] -jobs: - auth: - runs-on: ubuntu-latest - steps: - - name: Run Downloader - run: | - wget https://downloader.hytale.com/hytale-downloader-linux-amd64 -O downloader - chmod +x downloader - ./downloader # This will print the URL and Code to the logs - echo "URL: ${{ steps.downloader.outputs.url }}" - echo "Code: ${{ steps.downloader.outputs.code }}" From 5ec42b40ac65987e722bc590be2000c966c9de73 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Tue, 10 Feb 2026 19:50:30 -0800 Subject: [PATCH 05/23] gradle.yml - remove github test - can only do this locally since we cant get the assets.zip --- .github/workflows/gradle.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 113361a3..2c39e737 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -5,7 +5,7 @@ # This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle -name: Build, Test, and Upload Artifact +name: Build and Upload Artifact on: push: @@ -38,5 +38,3 @@ jobs: name: HySkript-Artifact # A file, directory or wildcard pattern that describes what to upload path: build/libs/HySkript-*.jar - - name: Run Tests - run: ./gradlew testRunner From 92f1846ace856d253fb8ef9aa84cbe5ec6cfe6d5 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Tue, 10 Feb 2026 21:48:30 -0800 Subject: [PATCH 06/23] More work on tests --- build.gradle.kts | 8 +- .../api/skript/testing/TestProperties.java | 8 + .../api/skript/testing/TestResults.java | 11 +- .../skript/api/skript/testing/TestRunner.java | 191 +++++++++--------- .../api/skript/testing/TestRunnerMain.java | 143 +++++++++++++ .../api/skript/testing/TestingThingy.java | 113 ----------- .../skript/testing/elements/EffAssert.java | 1 + .../skriptdev/skript/plugin/Skript.java | 34 ++-- src/test/skript/tests/test-script.sk | 8 +- 9 files changed, 285 insertions(+), 232 deletions(-) create mode 100644 src/main/java/com/github/skriptdev/skript/api/skript/testing/TestProperties.java create mode 100644 src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunnerMain.java delete mode 100644 src/main/java/com/github/skriptdev/skript/api/skript/testing/TestingThingy.java diff --git a/build.gradle.kts b/build.gradle.kts index a06943ff..4cf72dc1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -15,6 +15,11 @@ val hytaleVersion = "2026.02.06-aa1b071c2" // https://maven.hytale.com/pre-release/com/hypixel/hytale/Server/maven-metadata.xml // (Pre-releases shouldn't be used for production) +// Location of the Hytale Server Assets +// This is used in testing +// Change this to wherever you have it on your computer +val assetLocation= "/Users/ShaneBee/Desktop/Server/Hytale/Assets/Assets.zip" + repositories { mavenCentral() mavenLocal() @@ -46,8 +51,9 @@ tasks { dependsOn("jar") group = "application" description = "Runs the test runner" - mainClass.set("com.github.skriptdev.skript.api.skript.testing.TestRunner") + mainClass.set("com.github.skriptdev.skript.api.skript.testing.TestRunnerMain") classpath = sourceSets["main"].runtimeClasspath + args(hytaleVersion, assetLocation) } processResources { filesNotMatching("assets/**") { diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestProperties.java b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestProperties.java new file mode 100644 index 00000000..34d4b6ab --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestProperties.java @@ -0,0 +1,8 @@ +package com.github.skriptdev.skript.api.skript.testing; + +public class TestProperties { + + public static final boolean ENABLED = Boolean.parseBoolean(System.getProperty("skript.test.enabled", "false")); + public static final String TEST_SCRIPTS_FOLDER = System.getProperty("skript.test.scripts", "mods/skript_HySkript/tests"); + +} diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestResults.java b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestResults.java index 0c3326d1..243779ef 100644 --- a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestResults.java +++ b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestResults.java @@ -38,8 +38,14 @@ public void addFailure(String test, String value) { this.failureMap.put(test, value); } - @SuppressWarnings({"ResultOfMethodCallIgnored", "CallToPrintStackTrace"}) - public void printToJsonFile() { + public void clear() { + this.success = true; + this.successMap.clear(); + this.failureMap.clear(); + } + + @SuppressWarnings({"CallToPrintStackTrace"}) + public void printToProperties() { Path resolve = HySk.getInstance().getDataDirectory().resolve("test-results.properties"); try { Files.createDirectories(resolve.getParent()); @@ -52,7 +58,6 @@ public void printToJsonFile() { props.setProperty("success.count", Integer.toString(this.successMap.size())); props.setProperty("failure.count", Integer.toString(this.failureMap.size())); - // Store individual entries (handy for debugging in CI logs) for (var e : successMap.entrySet()) { props.setProperty("success." + e.getKey(), e.getValue()); } diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunner.java b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunner.java index 31756b6c..70830e4c 100644 --- a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunner.java +++ b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunner.java @@ -1,118 +1,115 @@ package com.github.skriptdev.skript.api.skript.testing; +import com.github.skriptdev.skript.api.skript.testing.elements.EvtTest.TestContext; +import com.github.skriptdev.skript.api.utils.Utils; +import com.hypixel.hytale.server.core.HytaleServer; +import com.hypixel.hytale.server.core.Message; +import com.hypixel.hytale.server.core.universe.Universe; +import com.hypixel.hytale.server.core.universe.world.World; +import com.hypixel.hytale.server.core.util.MessageUtil; +import fi.sulku.hytale.TinyMsg; +import io.github.syst3ms.skriptparser.lang.TriggerMap; +import io.github.syst3ms.skriptparser.log.LogEntry; +import io.github.syst3ms.skriptparser.log.LogType; +import io.github.syst3ms.skriptparser.parsing.ScriptLoader; + import java.io.File; -import java.io.IOException; -import java.net.URI; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; -import java.util.Properties; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.concurrent.TimeUnit; public class TestRunner { - static void main(String[] args) { - System.out.println("Downloading Hytale Server..."); - downloadHytaleServer(); - System.out.println("Download complete!"); - - // move plugin to mods file - System.out.println("Moving plugin to mods folder..."); - movePlugin(); - System.out.println("Plugin moved!"); - - // set system property for testing - System.out.println("Starting server..."); - runServer(); - System.out.println("Server started!"); + private static final TestResults TEST_RESULTS = new TestResults(); + + @SuppressWarnings("DataFlowIssue") + public static void start() { + Runnable runTestsRunnable = () -> { + Utils.log("Running tests in world 'default'..."); + runTests(); + Utils.log("Finished running tests!"); + }; + Runnable loadTestsRunnable = () -> { + Utils.log("Loading test scripts..."); + loadTests(); + Utils.log("Finished loading test scripts!"); + + World world = Universe.get().getWorld("default"); + if (world.isPaused()) world.setPaused(false); + + world.execute(runTestsRunnable); + }; + HytaleServer.SCHEDULED_EXECUTOR.schedule(loadTestsRunnable, 2, TimeUnit.SECONDS); } - private static void downloadHytaleServer() { - String url = "https://maven.hytale.com/release/com/hypixel/hytale/Server" + - "/2026.02.06-aa1b071c2/Server-2026.02.06-aa1b071c2.jar"; // TODO replace with gradle replace - String targetDir = "run/testServer/"; - String newName = "HytaleServer.jar"; - - try { - // 1. Create the directory if it doesn't exist - Path directoryPath = Paths.get(targetDir); - if (!Files.exists(directoryPath)) { - Files.createDirectories(directoryPath); - } - - // 2. Download the file to a temporary location - HttpClient client = HttpClient.newHttpClient(); - HttpRequest request = HttpRequest.newBuilder() - .uri(URI.create(url)) - .build(); - - // We download directly to a path to save memory - Path tempFile = Files.createTempFile("tempDownload", ".tmp"); - client.send(request, HttpResponse.BodyHandlers.ofFile(tempFile)); + private static void loadTests() { + Path path = Path.of(TestProperties.TEST_SCRIPTS_FOLDER); + loadScripts(path); + } - // 3. Move and Rename the file - Path finalPath = directoryPath.resolve(newName); - Files.move(tempFile, finalPath, StandardCopyOption.REPLACE_EXISTING); + private static void runTests() { + TestContext testContext = new TestContext(TEST_RESULTS); + TriggerMap.callTriggersByContext(testContext); + + if (TEST_RESULTS.isSuccess()) { + Message message = TinyMsg.parse("All tests passed!"); + Utils.log(MessageUtil.toAnsiString(message).toAnsi()); + } else { + Utils.error("Some tests failed!"); + TEST_RESULTS.getFailureMap().forEach((test, failure) -> { + Utils.error(" - [" + test + "]: " + failure); + }); + } - System.out.println("File saved to: " + finalPath.toAbsolutePath()); + TEST_RESULTS.printToProperties(); + TEST_RESULTS.clear(); - } catch (IOException | InterruptedException e) { - e.printStackTrace(); - } + Runnable shutdownServer = () -> HytaleServer.get().shutdownServer(); + HytaleServer.SCHEDULED_EXECUTOR.schedule(shutdownServer, 2, TimeUnit.SECONDS); } - private static void movePlugin() { - File file = new File("run/testServer/mods"); - file.mkdirs(); - try { - Files.copy(Path.of("build/libs/HySkript-1.0.0-pre-release-3.jar"), - Path.of("run/testServer/mods/HySkript-1.0.0-pre-release-3.jar"), - StandardCopyOption.REPLACE_EXISTING); - } catch (IOException e) { - throw new RuntimeException(e); - } + private static void loadScripts(Path directory) { + File scriptsDirectory = directory.toFile(); + Utils.log("Loading test directory '" + scriptsDirectory.getAbsolutePath() + "'..."); + List scriptNames = loadScriptsInDirectory(scriptsDirectory); + Utils.log("Loaded " + scriptNames.size() + " scripts!"); } - private static void runServer() { - try { - File serverFolder = new File("run/testServer/"); - ProcessBuilder processBuilder = new ProcessBuilder( - "java", - "-Xms2G", - "-Xmx2G", - "-jar", "HytaleServer.jar", - "--assets", "/Users/ShaneBee/Desktop/Server/Hytale/Assets/Assets.zip" - ); - processBuilder.inheritIO(); - processBuilder.directory(serverFolder); - Process process = processBuilder.start(); - int exitCode = process.waitFor(); - - // Read results written by the plugin (no Gson required). - Path resultsPath = Path.of("run/testServer/mods/skript_HySkript/test-results.properties"); - if (!Files.exists(resultsPath)) { - throw new IllegalStateException( - "Test results file not found at " + resultsPath.toAbsolutePath() + - " (server exit code was " + exitCode + ")" - ); + private static List loadScriptsInDirectory(File directory) { + if (directory == null || !directory.isDirectory()) return List.of(); + + List loadedScripts = new ArrayList<>(); + + File[] files = directory.listFiles(); + if (files == null) return loadedScripts; + + Arrays.sort(files, + Comparator.comparing(File::isDirectory).reversed() // Directories first + .thenComparing(File::getName, String.CASE_INSENSITIVE_ORDER)); // Then sort by name alphabetically + + for (File file : files) { + // Skip disabled files and hidden files + String fileName = file.getName(); + if (fileName.startsWith("-") || fileName.startsWith(".")) continue; + if (file.isDirectory()) { + loadedScripts.addAll(loadScriptsInDirectory(file)); + } else { + if (!fileName.endsWith(".sk")) continue; + Utils.log("Loading script '" + fileName + "'..."); + List logEntries = ScriptLoader.loadScript(file.toPath(), false); + for (LogEntry logEntry : logEntries) { + Utils.log(null, logEntry); + if (logEntry.getType() == LogType.ERROR) { + TEST_RESULTS.addFailure("Parsing Error:" + fileName, logEntry.getMessage()); + } + } + loadedScripts.add(fileName.substring(0, fileName.length() - 3)); } - - Properties props = new Properties(); - try (var reader = Files.newBufferedReader(resultsPath, StandardCharsets.UTF_8)) { - props.load(reader); - } - - int failureCount = Integer.parseInt(props.getProperty("failure.count", "0")); - - System.out.println("Exited with code " + failureCount); - System.exit(failureCount); - } catch (IOException | InterruptedException e) { - e.printStackTrace(); } + return loadedScripts; } } diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunnerMain.java b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunnerMain.java new file mode 100644 index 00000000..66444343 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunnerMain.java @@ -0,0 +1,143 @@ +package com.github.skriptdev.skript.api.skript.testing; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; + +public class TestRunnerMain { + + private static String serverVersion; + private static String assetPath; + + static void main(String[] args) { + serverVersion = args[0]; + assetPath = args[1]; + + System.out.println("Downloading Hytale Server..."); + downloadHytaleServer(); + System.out.println("Download complete!"); + + System.out.println("Moving plugin to mods folder..."); + movePlugin(); + System.out.println("Plugin moved!"); + + System.out.println("Starting Hytale server..."); + runServer(); + } + + @SuppressWarnings({"resource", "CallToPrintStackTrace"}) + private static void downloadHytaleServer() { + String url = "https://maven.hytale.com/release/com/hypixel/hytale/Server/" + + serverVersion + "/Server-" + serverVersion + ".jar"; + String targetDir = "run/testServer/"; + String newName = "HytaleServer.jar"; + + try { + // 1. Create the directory if it doesn't exist + Path directoryPath = Paths.get(targetDir); + if (!Files.exists(directoryPath)) { + Files.createDirectories(directoryPath); + } + + // 2. Download the file to a temporary location + HttpClient client = HttpClient.newHttpClient(); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(url)) + .build(); + + // We download directly to a path to save memory + Path tempFile = Files.createTempFile("tempDownload", ".tmp"); + client.send(request, HttpResponse.BodyHandlers.ofFile(tempFile)); + + // 3. Move and Rename the file + Path finalPath = directoryPath.resolve(newName); + Files.move(tempFile, finalPath, StandardCopyOption.REPLACE_EXISTING); + + System.out.println("File saved to: " + finalPath.toAbsolutePath()); + + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + } + } + + @SuppressWarnings("ResultOfMethodCallIgnored") + private static void movePlugin() { + File file = new File("run/testServer/mods"); + file.mkdirs(); + try { + Files.copy(Path.of("build/libs/HySkript-1.0.0-pre-release-3.jar"), + Path.of("run/testServer/mods/HySkript-1.0.0-pre-release-3.jar"), + StandardCopyOption.REPLACE_EXISTING); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @SuppressWarnings("CallToPrintStackTrace") + private static void runServer() { + try { + File serverFolder = new File("run/testServer/"); + ProcessBuilder processBuilder = new ProcessBuilder( + "java", + "-Xms2G", + "-Xmx2G", + "-Dskript.test.enabled=true", + "-Dskript.test.scripts=../../src/test/skript/tests", + "-jar", "HytaleServer.jar", + "--assets", assetPath + ); + processBuilder.inheritIO(); + processBuilder.directory(serverFolder); + Process process = processBuilder.start(); + int exitCode = process.waitFor(); + + // Read results written by the plugin (no Gson required). + Path resultsPath = Path.of("run/testServer/mods/skript_HySkript/test-results.properties"); + if (!Files.exists(resultsPath)) { + throw new IllegalStateException( + "Test results file not found at " + resultsPath.toAbsolutePath() + + " (server exit code was " + exitCode + ")" + ); + } + + Properties props = new Properties(); + try (var reader = Files.newBufferedReader(resultsPath, StandardCharsets.UTF_8)) { + props.load(reader); + } + + List errors = new ArrayList<>(); + + props.forEach((k, value) -> { + String key = k.toString(); + if (!key.equalsIgnoreCase("failure.count") && key.startsWith("failure.")) { + String[] split = key.split("\\.", 2); + errors.add(" - [" + split[1] + "]: " + value); + } + }); + + int failureCount = Integer.parseInt(props.getProperty("failure.count", "0")); + if (!errors.isEmpty()) { + System.err.println("Test failures:"); + errors.forEach(System.err::println); + } else { + System.out.println( "\u001B[32m" + "**All tests passed!**" + "\u001B[0m"); + } + + System.exit(failureCount); + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + } + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestingThingy.java b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestingThingy.java deleted file mode 100644 index 1b8b6aeb..00000000 --- a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestingThingy.java +++ /dev/null @@ -1,113 +0,0 @@ -package com.github.skriptdev.skript.api.skript.testing; - -import com.github.skriptdev.skript.api.skript.testing.elements.EvtTest.TestContext; -import com.github.skriptdev.skript.api.utils.Utils; -import com.github.skriptdev.skript.plugin.Skript; -import com.hypixel.hytale.server.core.HytaleServer; -import com.hypixel.hytale.server.core.Message; -import com.hypixel.hytale.server.core.universe.Universe; -import com.hypixel.hytale.server.core.universe.world.World; -import com.hypixel.hytale.server.core.util.MessageUtil; -import fi.sulku.hytale.TinyMsg; -import io.github.syst3ms.skriptparser.lang.TriggerMap; -import io.github.syst3ms.skriptparser.log.LogEntry; -import io.github.syst3ms.skriptparser.parsing.ScriptLoader; - -import java.io.File; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; -import java.util.concurrent.TimeUnit; - -public class TestingThingy { - - private static Skript skript; - - @SuppressWarnings("DataFlowIssue") - public static void start(Skript skript) { - TestingThingy.skript = skript; - - Runnable runTestsInWorld = () -> { - Utils.log("Running tests in world 'default'..."); - runTests(); - Utils.log("Finished running tests!"); - }; - Runnable loadTestsRunnable = () -> { - Utils.log("Loading test scripts..."); - loadTests(); - Utils.log("Finished loading test scripts!"); - World world = Universe.get().getWorld("default"); - if (world.isPaused()) world.setPaused(false); - - world.execute(runTestsInWorld); - }; - HytaleServer.SCHEDULED_EXECUTOR.schedule(loadTestsRunnable, 2, TimeUnit.SECONDS); - } - - private static void loadTests() { - Path path = Path.of("/Users/ShaneBee/IdeaProjects/HySkript/HySkript/src/test/skript/tests"); - loadScripts(path); - } - - private static void runTests() { - - TestResults testResults = new TestResults(); - - TestContext testContext = new TestContext(testResults); - TriggerMap.callTriggersByContext(testContext); - - if (testResults.isSuccess()) { - Message message = TinyMsg.parse("All tests passed!"); - Utils.log(MessageUtil.toAnsiString(message).toAnsi()); - } else { - Utils.error("Some tests failed!"); - } - - testResults.printToJsonFile(); - - // Figure out how to fail the build on GitHub - Runnable shutdownServer = () -> HytaleServer.get().shutdownServer(); - HytaleServer.SCHEDULED_EXECUTOR.schedule(shutdownServer, 5, TimeUnit.SECONDS); - } - - private static void loadScripts(Path directory) { - File scriptsDirectory = directory.toFile(); - Utils.log("Loading test directory '" + scriptsDirectory.getAbsolutePath() + "'..."); - List scriptNames = loadScriptsInDirectory(scriptsDirectory); - Utils.log("Loaded " + scriptNames.size() + " scripts!"); - } - - private static List loadScriptsInDirectory(File directory) { - if (directory == null || !directory.isDirectory()) return List.of(); - - List loadedScripts = new ArrayList<>(); - - File[] files = directory.listFiles(); - if (files == null) return loadedScripts; - - Arrays.sort(files, - Comparator.comparing(File::isDirectory).reversed() // Directories first - .thenComparing(File::getName, String.CASE_INSENSITIVE_ORDER)); // Then sort by name alphabetically - - for (File file : files) { - // Skip disabled files and hidden files - String fileName = file.getName(); - if (fileName.startsWith("-") || fileName.startsWith(".")) continue; - if (file.isDirectory()) { - loadedScripts.addAll(loadScriptsInDirectory(file)); - } else { - if (!fileName.endsWith(".sk")) continue; - Utils.log("Loading script '" + fileName + "'..."); - List logEntries = ScriptLoader.loadScript(file.toPath(), false); - for (LogEntry logEntry : logEntries) { - Utils.log(null, logEntry); - } - loadedScripts.add(fileName.substring(0, fileName.length() - 3)); - } - } - return loadedScripts; - } - -} diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/EffAssert.java b/src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/EffAssert.java index da013b9e..8b5b1d83 100644 --- a/src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/EffAssert.java +++ b/src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/EffAssert.java @@ -16,6 +16,7 @@ public class EffAssert extends Effect { public static void register(SkriptRegistration reg) { reg.newEffect(EffAssert.class, "assert %=boolean% with %*string%") + .noDoc() .register(); } diff --git a/src/main/java/com/github/skriptdev/skript/plugin/Skript.java b/src/main/java/com/github/skriptdev/skript/plugin/Skript.java index 88c5ba1f..032d1b7a 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/Skript.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/Skript.java @@ -6,13 +6,15 @@ import com.github.skriptdev.skript.api.skript.command.ArgUtils; import com.github.skriptdev.skript.api.skript.config.SkriptConfig; import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; -import com.github.skriptdev.skript.api.skript.testing.TestingThingy; +import com.github.skriptdev.skript.api.skript.testing.TestProperties; +import com.github.skriptdev.skript.api.skript.testing.TestRunner; import com.github.skriptdev.skript.api.skript.variables.JsonVariableStorage; import com.github.skriptdev.skript.api.utils.ReflectionUtils; import com.github.skriptdev.skript.api.utils.Utils; import com.github.skriptdev.skript.plugin.command.EffectCommands; import com.github.skriptdev.skript.plugin.elements.ElementRegistration; import com.github.skriptdev.skript.plugin.elements.events.EventHandler; +import com.hypixel.hytale.server.core.event.events.BootEvent; import io.github.syst3ms.skriptparser.config.Config.ConfigSection; import io.github.syst3ms.skriptparser.log.LogEntry; import io.github.syst3ms.skriptparser.log.SkriptLogger; @@ -98,17 +100,20 @@ private void setupSkript() { loadVariables(); // LOAD SCRIPTS -// this.scriptsLoader = new ScriptsLoader(this); -// this.scriptsLoader.loadScripts(null, this.scriptsPath, false); -// -// // FINALIZE SCRIPT LOADING -// this.hySk.getEventRegistry().register(BootEvent.class, event -> { -// Utils.debug("Hytale finished booting, starting post-load triggers..."); -// // Start any post-load triggers after Hytale finishes booting. -// getAddons().forEach(SkriptAddon::finishedLoading); -// }); - - TestingThingy.start(this); + this.scriptsLoader = new ScriptsLoader(this); + this.scriptsLoader.loadScripts(null, this.scriptsPath, false); + + // FINALIZE SCRIPT LOADING + this.hySk.getEventRegistry().register(BootEvent.class, event -> { + Utils.debug("Hytale finished booting, starting post-load triggers..."); + // Start any post-load triggers after Hytale finishes booting. + getAddons().forEach(SkriptAddon::finishedLoading); + }); + + // RUN TESTS + if (TestProperties.ENABLED) { + TestRunner.start(); + } } public void shutdown() { @@ -116,10 +121,7 @@ public void shutdown() { EventHandler.shutdown(); // SHUTDOWN SCRIPTS - if (this.scriptsLoader != null) { - // TODO fix later - this.scriptsLoader.shutdown(); - } + this.scriptsLoader.shutdown(); // SHUTDOWN VARIABLES Utils.log("Saving variables..."); diff --git a/src/test/skript/tests/test-script.sk b/src/test/skript/tests/test-script.sk index cfb0163f..363fe81d 100644 --- a/src/test/skript/tests/test-script.sk +++ b/src/test/skript/tests/test-script.sk @@ -1,3 +1,7 @@ test "Basic HySkript Test": - assert 1 = 1 with "1 should equal 1" - assert 2 = 2 with "2 should equal 2" + set {_spawn} to world spawn of world("default") + assert {_spawn} is set with "The main spawn point of the world should be set" + + spawn a sheep at {_spawn}: + set {_sheep} to event-spawned-entity + assert {_sheep} is set with "The sheep entity should be set" From df6162cff57d791b9230aec8b3efda4f309f7a0d Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Tue, 10 Feb 2026 22:14:16 -0800 Subject: [PATCH 07/23] more test stuff --- .../skript/plugin/elements/ElementRegistration.java | 7 +++++-- .../tests/elements/conditions/CondExprCompare.sk | 10 ++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 src/test/skript/tests/elements/conditions/CondExprCompare.sk diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/ElementRegistration.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/ElementRegistration.java index fea640b4..8fb341be 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/ElementRegistration.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/ElementRegistration.java @@ -1,6 +1,7 @@ package com.github.skriptdev.skript.plugin.elements; import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.github.skriptdev.skript.api.skript.testing.TestProperties; import com.github.skriptdev.skript.api.skript.testing.elements.ElementHandler; import com.github.skriptdev.skript.api.utils.Utils; import com.github.skriptdev.skript.plugin.elements.command.ScriptCommand; @@ -62,8 +63,10 @@ public void registerElements() { ScriptCommand.register(this.registration); ScriptSubCommand.register(this.registration); - // TEST STUFF TODO do a test check - ElementHandler.register(this.registration); + // TEST ELEMENTS + if (TestProperties.ENABLED) { + ElementHandler.register(this.registration); + } // FINALIZE SETUP this.registration.register(); diff --git a/src/test/skript/tests/elements/conditions/CondExprCompare.sk b/src/test/skript/tests/elements/conditions/CondExprCompare.sk new file mode 100644 index 00000000..3013c01a --- /dev/null +++ b/src/test/skript/tests/elements/conditions/CondExprCompare.sk @@ -0,0 +1,10 @@ +test "CondCompare": + assert 1 = 1 with "1 should equal 1" + assert 1 != 2 with "1 should not equal 2" + + assert 1 < 2 with "1 should be less than 2" + assert 2 > 1 with "2 should be greater than 1" + + set {_a} to ingredient_poop + set {_b} to ingredient_stick + assert {_a} != {_b} with "Poop and a stick should not be equal" From 83340a3417f2fac8a63f675d8363e1cd68cdf50d Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Wed, 11 Feb 2026 08:05:37 -0800 Subject: [PATCH 08/23] MORE test stuff --- build.gradle.kts | 14 +++-- .../api/skript/testing/TestResults.java | 36 ++++++------- .../api/skript/testing/TestRunnerMain.java | 54 ++++++++++++------- .../skript/testing/elements/EffAssert.java | 27 ++++++++-- .../elements/conditions/CondExprIsEmpty.sk | 4 ++ .../tests/elements/expressions/ExprAmount.sk | 4 ++ 6 files changed, 92 insertions(+), 47 deletions(-) create mode 100644 src/test/skript/tests/elements/conditions/CondExprIsEmpty.sk create mode 100644 src/test/skript/tests/elements/expressions/ExprAmount.sk diff --git a/build.gradle.kts b/build.gradle.kts index 4cf72dc1..a20a63e8 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -18,7 +18,7 @@ val hytaleVersion = "2026.02.06-aa1b071c2" // Location of the Hytale Server Assets // This is used in testing // Change this to wherever you have it on your computer -val assetLocation= "/Users/ShaneBee/Desktop/Server/Hytale/Assets/Assets.zip" +val assetLocation = "/Users/ShaneBee/Desktop/Server/Hytale/Assets/Assets.zip" repositories { mavenCentral() @@ -31,7 +31,7 @@ repositories { dependencies { compileOnly("com.hypixel.hytale:Server:${hytaleVersion}") compileOnly("org.jetbrains:annotations:26.0.2") - implementation("com.github.SkriptDev:skript-parser:1.0.7") { + implementation("com.github.SkriptDev:skript-parser:1.0.7-test") { isTransitive = false } implementation("com.github.Zoltus:TinyMessage:2.0.1") { @@ -39,6 +39,12 @@ dependencies { } } +// This is used to enable Gson in the test environment via HytaleServer +val testRunnerClasspath by configurations.creating { + extendsFrom(configurations.compileOnly.get()) + isCanBeResolved = true +} + tasks { register("server", Copy::class) { dependsOn("jar") @@ -50,10 +56,10 @@ tasks { register("testRunner") { dependsOn("jar") group = "application" - description = "Runs the test runner" mainClass.set("com.github.skriptdev.skript.api.skript.testing.TestRunnerMain") - classpath = sourceSets["main"].runtimeClasspath args(hytaleVersion, assetLocation) + + classpath = sourceSets["main"].runtimeClasspath + testRunnerClasspath } processResources { filesNotMatching("assets/**") { diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestResults.java b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestResults.java index 243779ef..ceb7186a 100644 --- a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestResults.java +++ b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestResults.java @@ -1,41 +1,46 @@ package com.github.skriptdev.skript.api.skript.testing; import com.github.skriptdev.skript.plugin.HySk; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import java.io.BufferedWriter; import java.io.IOException; import java.io.OutputStreamWriter; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Properties; public class TestResults { private boolean success = true; - private final Map successMap = new HashMap<>(); - private final Map failureMap = new HashMap<>(); + private final Map> successMap = new HashMap<>(); + private final Map> failureMap = new HashMap<>(); public boolean isSuccess() { return success; } - public Map getSuccessMap() { + public Map> getSuccessMap() { return successMap; } - public Map getFailureMap() { + public Map> getFailureMap() { return failureMap; } public void addSuccess(String test, String value) { - this.successMap.put(test, value); + this.successMap.computeIfAbsent(test, _ -> new ArrayList<>()).add(value); } public void addFailure(String test, String value) { this.success = false; - this.failureMap.put(test, value); + this.failureMap.computeIfAbsent(test, _ -> new ArrayList<>()).add(value); } public void clear() { @@ -46,27 +51,18 @@ public void clear() { @SuppressWarnings({"CallToPrintStackTrace"}) public void printToProperties() { - Path resolve = HySk.getInstance().getDataDirectory().resolve("test-results.properties"); + Path resolve = HySk.getInstance().getDataDirectory().resolve("test-results.json"); + try { Files.createDirectories(resolve.getParent()); } catch (IOException e) { throw new RuntimeException("Failed to create directories for " + resolve.toAbsolutePath(), e); } - Properties props = new Properties(); - props.setProperty("success", Boolean.toString(this.success)); - props.setProperty("success.count", Integer.toString(this.successMap.size())); - props.setProperty("failure.count", Integer.toString(this.failureMap.size())); - - for (var e : successMap.entrySet()) { - props.setProperty("success." + e.getKey(), e.getValue()); - } - for (var e : failureMap.entrySet()) { - props.setProperty("failure." + e.getKey(), e.getValue()); - } + Gson gson = new GsonBuilder().disableHtmlEscaping().setPrettyPrinting().create(); - try (var out = new OutputStreamWriter(Files.newOutputStream(resolve), StandardCharsets.UTF_8)) { - props.store(out, "HySkript test results"); + try (BufferedWriter writer = Files.newBufferedWriter(resolve, StandardCharsets.UTF_8)) { + gson.toJson(this, writer); System.out.println("Test-Results successfully written to " + resolve.toAbsolutePath()); } catch (Exception e) { e.printStackTrace(); diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunnerMain.java b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunnerMain.java index 66444343..8ad1015f 100644 --- a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunnerMain.java +++ b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunnerMain.java @@ -1,5 +1,8 @@ package com.github.skriptdev.skript.api.skript.testing; +import com.google.gson.Gson; + +import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.net.URI; @@ -13,10 +16,15 @@ import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.List; -import java.util.Properties; +import java.util.concurrent.atomic.AtomicInteger; public class TestRunnerMain { + public static final String GREEN = "\u001B[92m"; + public static final String LIGHT_GREY = "\u001B[37m"; + public static final String RED = "\u001B[91m"; + public static final String RESET = "\u001B[0m"; + private static String serverVersion; private static String assetPath; @@ -103,7 +111,7 @@ private static void runServer() { int exitCode = process.waitFor(); // Read results written by the plugin (no Gson required). - Path resultsPath = Path.of("run/testServer/mods/skript_HySkript/test-results.properties"); + Path resultsPath = Path.of("run/testServer/mods/skript_HySkript/test-results.json"); if (!Files.exists(resultsPath)) { throw new IllegalStateException( "Test results file not found at " + resultsPath.toAbsolutePath() + @@ -111,30 +119,40 @@ private static void runServer() { ); } - Properties props = new Properties(); - try (var reader = Files.newBufferedReader(resultsPath, StandardCharsets.UTF_8)) { - props.load(reader); + Gson gson = new Gson(); + TestResults results; + + try (BufferedReader reader = Files.newBufferedReader(resultsPath, StandardCharsets.UTF_8)) { + results = gson.fromJson(reader, TestResults.class); + + System.out.println("Successfully loaded results from: " + resultsPath.getFileName()); + } catch (Exception e) { + System.err.println("Could not read test results: " + e.getMessage()); + e.printStackTrace(); + return; } + AtomicInteger failureCount = new AtomicInteger(); List errors = new ArrayList<>(); - - props.forEach((k, value) -> { - String key = k.toString(); - if (!key.equalsIgnoreCase("failure.count") && key.startsWith("failure.")) { - String[] split = key.split("\\.", 2); - errors.add(" - [" + split[1] + "]: " + value); - } + results.getFailureMap().forEach((test, errorList) -> { + failureCount.incrementAndGet(); + errorList.forEach(error -> + errors.add(" - " + RED + test + LIGHT_GREY + ": " + error + RESET)); }); - int failureCount = Integer.parseInt(props.getProperty("failure.count", "0")); - if (!errors.isEmpty()) { - System.err.println("Test failures:"); - errors.forEach(System.err::println); + System.out.println("Succeeded:"); + results.getSuccessMap().forEach((test, success) -> + System.out.println(" - " + GREEN + test + RESET)); + + + System.out.println("Failed:"); + if (failureCount.get() > 0) { + errors.forEach(System.out::println); } else { - System.out.println( "\u001B[32m" + "**All tests passed!**" + "\u001B[0m"); + System.out.println(" - none"); } - System.exit(failureCount); + System.exit(failureCount.get()); } catch (IOException | InterruptedException e) { e.printStackTrace(); } diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/EffAssert.java b/src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/EffAssert.java index 8b5b1d83..809d3bc8 100644 --- a/src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/EffAssert.java +++ b/src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/EffAssert.java @@ -8,6 +8,9 @@ import io.github.syst3ms.skriptparser.lang.TriggerContext; import io.github.syst3ms.skriptparser.log.SkriptLogger; import io.github.syst3ms.skriptparser.parsing.ParseContext; +import io.github.syst3ms.skriptparser.parsing.SyntaxParser; +import io.github.syst3ms.skriptparser.types.PatternType; +import io.github.syst3ms.skriptparser.types.TypeManager; import org.jetbrains.annotations.NotNull; import java.util.Optional; @@ -15,7 +18,7 @@ public class EffAssert extends Effect { public static void register(SkriptRegistration reg) { - reg.newEffect(EffAssert.class, "assert %=boolean% with %*string%") + reg.newEffect(EffAssert.class, "assert <.+> with %*string%") .noDoc() .register(); } @@ -23,17 +26,31 @@ public static void register(SkriptRegistration reg) { private String fileName; private int lineNumber; private Expression condition; + private String conditionString; private Expression message; @SuppressWarnings("unchecked") @Override public boolean init(Expression[] expressions, int matchedPattern, @NotNull ParseContext parseContext) { - this.condition = (Expression) expressions[0]; - this.message = (Expression) expressions[1]; + this.message = (Expression) expressions[0]; SkriptLogger logger = parseContext.getLogger(); this.fileName = logger.getFileName(); this.lineNumber = logger.getLine() + 1; // I think it gets the last line?!?! + this.conditionString = parseContext.getMatches().getFirst().group(); + Optional> patternType = TypeManager.getPatternType("boolean"); + if (patternType.isEmpty()) { + return false; + } + + Optional> expression = SyntaxParser.parseExpression(this.conditionString, + patternType.get(), + parseContext.getParserState(), + parseContext.getLogger()); + if (expression.isEmpty() || expression.get().getReturnType() != Boolean.class) { + return false; + } + this.condition = (Expression) expression.get(); return true; } @@ -48,14 +65,14 @@ protected void execute(@NotNull TriggerContext ctx) { // Test failed String message = this.message.getSingle(ctx).orElseThrow(); String failure = String.format("assert '%s' failed with message \"%s\" {%s:%d}", - this.condition.toString(ctx, false), + this.conditionString, message, this.fileName, this.lineNumber); testResults.addFailure(context.getTestSubject(), failure); } else { String success = String.format("assert '%s' passed", - this.condition.toString(ctx, false)); + this.conditionString); testResults.addSuccess(context.getTestSubject(), success); } } diff --git a/src/test/skript/tests/elements/conditions/CondExprIsEmpty.sk b/src/test/skript/tests/elements/conditions/CondExprIsEmpty.sk new file mode 100644 index 00000000..4e7bfdf7 --- /dev/null +++ b/src/test/skript/tests/elements/conditions/CondExprIsEmpty.sk @@ -0,0 +1,4 @@ +test "CondExprIsEmpty": + #assert {_a::*} is empty with "The unset variable should be empty" TODO this fails, tries to compare to "empty" (Item/BlockType probably) + set {_a::*} to 1, 2 and 3 + assert {_a::*} is not empty with "The variable should not be empty after setting it" diff --git a/src/test/skript/tests/elements/expressions/ExprAmount.sk b/src/test/skript/tests/elements/expressions/ExprAmount.sk new file mode 100644 index 00000000..ad98398d --- /dev/null +++ b/src/test/skript/tests/elements/expressions/ExprAmount.sk @@ -0,0 +1,4 @@ +test "ExprAmount": + assert size of {_a::*} = 0 with "The unset variable should be empty" + set {_a::*} to 1, 2 and 3 + assert size of {_a::*} = 3 with "The variable should have three elements" From fdfe2585813536398ae21f5e1e31c9496dde8eb8 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Wed, 11 Feb 2026 08:30:26 -0800 Subject: [PATCH 09/23] MORE test stuff... again --- .../api/skript/testing/TestResults.java | 24 ++++++++++++---- .../skript/api/skript/testing/TestRunner.java | 6 ++-- .../api/skript/testing/TestRunnerMain.java | 28 ++++++++----------- .../api/skript/testing/elements/EvtTest.java | 2 +- src/test/skript/tests/test-script.sk | 2 ++ 5 files changed, 36 insertions(+), 26 deletions(-) diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestResults.java b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestResults.java index ceb7186a..2f27a3fd 100644 --- a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestResults.java +++ b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestResults.java @@ -1,12 +1,12 @@ package com.github.skriptdev.skript.api.skript.testing; +import com.github.skriptdev.skript.api.utils.Utils; import com.github.skriptdev.skript.plugin.HySk; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import java.io.BufferedWriter; import java.io.IOException; -import java.io.OutputStreamWriter; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; @@ -14,24 +14,28 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Properties; public class TestResults { private boolean success = true; + private int failCount = 0; private final Map> successMap = new HashMap<>(); private final Map> failureMap = new HashMap<>(); public boolean isSuccess() { - return success; + return this.success; + } + + public int getFailCount() { + return this.failCount; } public Map> getSuccessMap() { - return successMap; + return this.successMap; } public Map> getFailureMap() { - return failureMap; + return this.failureMap; } public void addSuccess(String test, String value) { @@ -40,9 +44,17 @@ public void addSuccess(String test, String value) { public void addFailure(String test, String value) { this.success = false; + this.failCount++; this.failureMap.computeIfAbsent(test, _ -> new ArrayList<>()).add(value); } + public void process() { + this.failureMap.forEach((test, _) -> { + // We don't care about success if other tests failed in that test + this.successMap.remove(test); + }); + } + public void clear() { this.success = true; this.successMap.clear(); @@ -63,7 +75,7 @@ public void printToProperties() { try (BufferedWriter writer = Files.newBufferedWriter(resolve, StandardCharsets.UTF_8)) { gson.toJson(this, writer); - System.out.println("Test-Results successfully written to " + resolve.toAbsolutePath()); + Utils.log("Test-Results successfully written to " + resolve.toAbsolutePath()); } catch (Exception e) { e.printStackTrace(); } diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunner.java b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunner.java index 70830e4c..7f2d5e8a 100644 --- a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunner.java +++ b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunner.java @@ -30,7 +30,6 @@ public static void start() { Runnable runTestsRunnable = () -> { Utils.log("Running tests in world 'default'..."); runTests(); - Utils.log("Finished running tests!"); }; Runnable loadTestsRunnable = () -> { Utils.log("Loading test scripts..."); @@ -54,16 +53,19 @@ private static void runTests() { TestContext testContext = new TestContext(TEST_RESULTS); TriggerMap.callTriggersByContext(testContext); + TEST_RESULTS.process(); + if (TEST_RESULTS.isSuccess()) { Message message = TinyMsg.parse("All tests passed!"); Utils.log(MessageUtil.toAnsiString(message).toAnsi()); } else { - Utils.error("Some tests failed!"); + Utils.error(TEST_RESULTS.getFailCount() + " tests failed!"); TEST_RESULTS.getFailureMap().forEach((test, failure) -> { Utils.error(" - [" + test + "]: " + failure); }); } + Utils.log("Finished running tests!"); TEST_RESULTS.printToProperties(); TEST_RESULTS.clear(); diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunnerMain.java b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunnerMain.java index 8ad1015f..64e1b476 100644 --- a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunnerMain.java +++ b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunnerMain.java @@ -14,9 +14,6 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; public class TestRunnerMain { @@ -132,27 +129,24 @@ private static void runServer() { return; } - AtomicInteger failureCount = new AtomicInteger(); - List errors = new ArrayList<>(); - results.getFailureMap().forEach((test, errorList) -> { - failureCount.incrementAndGet(); - errorList.forEach(error -> - errors.add(" - " + RED + test + LIGHT_GREY + ": " + error + RESET)); - }); - System.out.println("Succeeded:"); - results.getSuccessMap().forEach((test, success) -> - System.out.println(" - " + GREEN + test + RESET)); - + if (!results.getSuccessMap().isEmpty()) { + results.getSuccessMap().forEach((test, _) -> + System.out.println(" - " + GREEN + test + RESET)); + } else { + System.out.println(" - none"); + } System.out.println("Failed:"); - if (failureCount.get() > 0) { - errors.forEach(System.out::println); + if (results.getFailCount() > 0) { + results.getFailureMap().forEach((test, errorList) -> + errorList.forEach(error -> + System.out.println(" - " + RED + test + LIGHT_GREY + ": " + error + RESET))); } else { System.out.println(" - none"); } - System.exit(failureCount.get()); + System.exit(results.getFailCount()); } catch (IOException | InterruptedException e) { e.printStackTrace(); } diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/EvtTest.java b/src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/EvtTest.java index 86be3f0b..6df8de81 100644 --- a/src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/EvtTest.java +++ b/src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/EvtTest.java @@ -32,7 +32,7 @@ public boolean init(Expression[] expressions, int matchedPattern, ParseContex public boolean check(TriggerContext triggerContext) { if (!(triggerContext instanceof TestContext context)) return false; context.setTestSubject(this.testSubject); - Utils.logColored("Testing: \"" + this.testSubject + "\""); + Utils.logColored("Running test: '" + this.testSubject + "'"); return true; } diff --git a/src/test/skript/tests/test-script.sk b/src/test/skript/tests/test-script.sk index 363fe81d..a07dbfcf 100644 --- a/src/test/skript/tests/test-script.sk +++ b/src/test/skript/tests/test-script.sk @@ -5,3 +5,5 @@ test "Basic HySkript Test": spawn a sheep at {_spawn}: set {_sheep} to event-spawned-entity assert {_sheep} is set with "The sheep entity should be set" + + assert 1 = 2 with "This should fail" From 82e6af094cc68617e1a30b079658c5a50faf943c Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Wed, 11 Feb 2026 08:47:45 -0800 Subject: [PATCH 10/23] build.gradle.kts - didnt mean to commit my test parser --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index a20a63e8..a18fcff7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -31,7 +31,7 @@ repositories { dependencies { compileOnly("com.hypixel.hytale:Server:${hytaleVersion}") compileOnly("org.jetbrains:annotations:26.0.2") - implementation("com.github.SkriptDev:skript-parser:1.0.7-test") { + implementation("com.github.SkriptDev:skript-parser:dev~patch-SNAPSHOT") { isTransitive = false } implementation("com.github.Zoltus:TinyMessage:2.0.1") { From e56a07974c977533ef608c6d49fa425118df158f Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Wed, 11 Feb 2026 10:25:46 -0800 Subject: [PATCH 11/23] Skript - change when test start --- .../skript/api/skript/testing/TestRunner.java | 38 ++++++++++--------- .../skriptdev/skript/plugin/Skript.java | 11 +++--- 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunner.java b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunner.java index 7f2d5e8a..8ac0df3b 100644 --- a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunner.java +++ b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunner.java @@ -23,15 +23,16 @@ public class TestRunner { - private static final TestResults TEST_RESULTS = new TestResults(); + private final TestResults testResults = new TestResults(); @SuppressWarnings("DataFlowIssue") - public static void start() { + public void start() { Runnable runTestsRunnable = () -> { Utils.log("Running tests in world 'default'..."); runTests(); }; Runnable loadTestsRunnable = () -> { + Utils.log("Testing has started!"); Utils.log("Loading test scripts..."); loadTests(); Utils.log("Finished loading test scripts!"); @@ -41,46 +42,47 @@ public static void start() { world.execute(runTestsRunnable); }; - HytaleServer.SCHEDULED_EXECUTOR.schedule(loadTestsRunnable, 2, TimeUnit.SECONDS); + + // Delay start to make sure the server has finished loading + HytaleServer.SCHEDULED_EXECUTOR.schedule(loadTestsRunnable, 1, TimeUnit.SECONDS); } - private static void loadTests() { + private void loadTests() { Path path = Path.of(TestProperties.TEST_SCRIPTS_FOLDER); loadScripts(path); } - private static void runTests() { - TestContext testContext = new TestContext(TEST_RESULTS); + private void runTests() { + TestContext testContext = new TestContext(this.testResults); TriggerMap.callTriggersByContext(testContext); - TEST_RESULTS.process(); + this.testResults.process(); - if (TEST_RESULTS.isSuccess()) { + if (this.testResults.isSuccess()) { Message message = TinyMsg.parse("All tests passed!"); Utils.log(MessageUtil.toAnsiString(message).toAnsi()); } else { - Utils.error(TEST_RESULTS.getFailCount() + " tests failed!"); - TEST_RESULTS.getFailureMap().forEach((test, failure) -> { - Utils.error(" - [" + test + "]: " + failure); - }); + Utils.error(this.testResults.getFailCount() + " tests failed!"); + this.testResults.getFailureMap().forEach((test, failure) -> + Utils.error(" - [" + test + "]: " + failure)); } Utils.log("Finished running tests!"); - TEST_RESULTS.printToProperties(); - TEST_RESULTS.clear(); + this.testResults.printToProperties(); + this.testResults.clear(); Runnable shutdownServer = () -> HytaleServer.get().shutdownServer(); HytaleServer.SCHEDULED_EXECUTOR.schedule(shutdownServer, 2, TimeUnit.SECONDS); } - private static void loadScripts(Path directory) { + private void loadScripts(Path directory) { File scriptsDirectory = directory.toFile(); Utils.log("Loading test directory '" + scriptsDirectory.getAbsolutePath() + "'..."); List scriptNames = loadScriptsInDirectory(scriptsDirectory); Utils.log("Loaded " + scriptNames.size() + " scripts!"); } - private static List loadScriptsInDirectory(File directory) { + private List loadScriptsInDirectory(File directory) { if (directory == null || !directory.isDirectory()) return List.of(); List loadedScripts = new ArrayList<>(); @@ -104,8 +106,8 @@ private static List loadScriptsInDirectory(File directory) { List logEntries = ScriptLoader.loadScript(file.toPath(), false); for (LogEntry logEntry : logEntries) { Utils.log(null, logEntry); - if (logEntry.getType() == LogType.ERROR) { - TEST_RESULTS.addFailure("Parsing Error:" + fileName, logEntry.getMessage()); + if (logEntry.getType() == LogType.ERROR) { + this.testResults.addFailure("Parsing Error:" + fileName, logEntry.getMessage()); } } loadedScripts.add(fileName.substring(0, fileName.length() - 3)); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/Skript.java b/src/main/java/com/github/skriptdev/skript/plugin/Skript.java index 032d1b7a..0cce07af 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/Skript.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/Skript.java @@ -108,12 +108,13 @@ private void setupSkript() { Utils.debug("Hytale finished booting, starting post-load triggers..."); // Start any post-load triggers after Hytale finishes booting. getAddons().forEach(SkriptAddon::finishedLoading); - }); - // RUN TESTS - if (TestProperties.ENABLED) { - TestRunner.start(); - } + // RUN TESTS + if (TestProperties.ENABLED) { + TestRunner testRunner = new TestRunner(); + testRunner.start(); + } + }); } public void shutdown() { From 8775ca35dbbd619dd7c2eef6f81d911295f76d3f Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Wed, 11 Feb 2026 13:15:43 -0800 Subject: [PATCH 12/23] TestResults - use TreeMap to sort results --- .../skriptdev/skript/api/skript/testing/TestResults.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestResults.java b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestResults.java index 2f27a3fd..8fc0a642 100644 --- a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestResults.java +++ b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestResults.java @@ -11,16 +11,16 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.TreeMap; public class TestResults { private boolean success = true; private int failCount = 0; - private final Map> successMap = new HashMap<>(); - private final Map> failureMap = new HashMap<>(); + private final Map> successMap = new TreeMap<>(); + private final Map> failureMap = new TreeMap<>(); public boolean isSuccess() { return this.success; @@ -53,6 +53,7 @@ public void process() { // We don't care about success if other tests failed in that test this.successMap.remove(test); }); + } public void clear() { From 063745f705fe5bd919dd8264738c6bde60bc6c64 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Wed, 11 Feb 2026 13:18:27 -0800 Subject: [PATCH 13/23] add a few more basic tests --- .../skript/tests/elements/conditions/CondExprContains.sk | 7 +++++++ .../tests/elements/conditions/CondExprIsDivisible.sk | 3 +++ src/test/skript/tests/elements/conditions/CondExprIsSet.sk | 6 ++++++ .../skript/tests/elements/conditions/CondExprStartsEnds.sk | 4 ++++ .../tests/elements/expressions/ExprArithmeticOperators.sk | 3 +++ .../tests/elements/expressions/ExprBooleanOperators.sk | 3 +++ src/test/skript/tests/test-script.sk | 2 -- 7 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 src/test/skript/tests/elements/conditions/CondExprContains.sk create mode 100644 src/test/skript/tests/elements/conditions/CondExprIsDivisible.sk create mode 100644 src/test/skript/tests/elements/conditions/CondExprIsSet.sk create mode 100644 src/test/skript/tests/elements/conditions/CondExprStartsEnds.sk create mode 100644 src/test/skript/tests/elements/expressions/ExprArithmeticOperators.sk create mode 100644 src/test/skript/tests/elements/expressions/ExprBooleanOperators.sk diff --git a/src/test/skript/tests/elements/conditions/CondExprContains.sk b/src/test/skript/tests/elements/conditions/CondExprContains.sk new file mode 100644 index 00000000..ea4392ab --- /dev/null +++ b/src/test/skript/tests/elements/conditions/CondExprContains.sk @@ -0,0 +1,7 @@ +test "CondExprContains": + assert {_blah::*} does not contain "blah" with "The variable shouldn't contain the string" + add "blah" to {_blah::*} + assert {_blah::*} contains "blah" with "The variable should contain the string" + + assert "blah blah blah" does not contain "cheese" with "The string shouldn't contain the substring" + assert "I love cheese" contains "cheese" with "The string should contain the substring" diff --git a/src/test/skript/tests/elements/conditions/CondExprIsDivisible.sk b/src/test/skript/tests/elements/conditions/CondExprIsDivisible.sk new file mode 100644 index 00000000..3cd81a83 --- /dev/null +++ b/src/test/skript/tests/elements/conditions/CondExprIsDivisible.sk @@ -0,0 +1,3 @@ +test "CondExprIsDivisible": + #assert 10 is not divisible by 7 with "10 should not be divisible by 7" TODO bug, CondExprIsDivisible isnt doing a neg check + assert 10 is divisible by 2 with "10 should be divisible by 2" diff --git a/src/test/skript/tests/elements/conditions/CondExprIsSet.sk b/src/test/skript/tests/elements/conditions/CondExprIsSet.sk new file mode 100644 index 00000000..95c99930 --- /dev/null +++ b/src/test/skript/tests/elements/conditions/CondExprIsSet.sk @@ -0,0 +1,6 @@ +test "CondExprIsSet": + assert {_a} is not set with "The variable should not be set yet" + set {_a} to "blah" + assert {_a} is set with "The variable should now be set" + delete {_a} + assert {_a} is not set with "The variable should not be set after deleting" diff --git a/src/test/skript/tests/elements/conditions/CondExprStartsEnds.sk b/src/test/skript/tests/elements/conditions/CondExprStartsEnds.sk new file mode 100644 index 00000000..de2885aa --- /dev/null +++ b/src/test/skript/tests/elements/conditions/CondExprStartsEnds.sk @@ -0,0 +1,4 @@ +test "CondExprStartsEnds": + # TODO neither of these seem to work + #assert ("blah" does not start with "cheese") with "Blah shouldn't start with cheese" + #assert ("cheese is the best" starts with "cheese") with "Cheese is the best should start with cheese" diff --git a/src/test/skript/tests/elements/expressions/ExprArithmeticOperators.sk b/src/test/skript/tests/elements/expressions/ExprArithmeticOperators.sk new file mode 100644 index 00000000..20185cd7 --- /dev/null +++ b/src/test/skript/tests/elements/expressions/ExprArithmeticOperators.sk @@ -0,0 +1,3 @@ +test "ExprArithmeticOperators": + assert 1 + 2 == 3 with "1 + 2 should equal 3" + assert 1 + 2 != 5 with "1 + 2 should not equal 5" diff --git a/src/test/skript/tests/elements/expressions/ExprBooleanOperators.sk b/src/test/skript/tests/elements/expressions/ExprBooleanOperators.sk new file mode 100644 index 00000000..62c849d4 --- /dev/null +++ b/src/test/skript/tests/elements/expressions/ExprBooleanOperators.sk @@ -0,0 +1,3 @@ +test "ExprBooleanOperators": + assert ((1 + 1 == 2) && (2 + 2 == 4)) with "Both booleans should match" + assert ((1 + 1 == 5) || (2 + 2 == 4)) with "One of the booleans (the second) should match" diff --git a/src/test/skript/tests/test-script.sk b/src/test/skript/tests/test-script.sk index a07dbfcf..363fe81d 100644 --- a/src/test/skript/tests/test-script.sk +++ b/src/test/skript/tests/test-script.sk @@ -5,5 +5,3 @@ test "Basic HySkript Test": spawn a sheep at {_spawn}: set {_sheep} to event-spawned-entity assert {_sheep} is set with "The sheep entity should be set" - - assert 1 = 2 with "This should fail" From 150b81467fd18d3a77432bb4d4735055d3626589 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Wed, 11 Feb 2026 14:41:13 -0800 Subject: [PATCH 14/23] add more tests --- src/test/skript/tests/elements/effects/EffChange.sk | 13 +++++++++++++ src/test/skript/tests/elements/effects/EffDoIf.sk | 6 ++++++ src/test/skript/tests/elements/effects/EffExit.sk | 7 +++++++ src/test/skript/tests/elements/effects/EffReturn.sk | 8 ++++++++ .../tests/elements/expressions/ExprElement.sk | 4 ++++ .../skript/tests/elements/expressions/ExprLength.sk | 2 ++ .../tests/elements/expressions/ExprMutableList.sk | 7 +++++++ .../tests/elements/expressions/ExprParseAs.sk | 3 +++ .../tests/elements/expressions/ExprTernary.sk | 7 +++++++ 9 files changed, 57 insertions(+) create mode 100644 src/test/skript/tests/elements/effects/EffChange.sk create mode 100644 src/test/skript/tests/elements/effects/EffDoIf.sk create mode 100644 src/test/skript/tests/elements/effects/EffExit.sk create mode 100644 src/test/skript/tests/elements/effects/EffReturn.sk create mode 100644 src/test/skript/tests/elements/expressions/ExprElement.sk create mode 100644 src/test/skript/tests/elements/expressions/ExprLength.sk create mode 100644 src/test/skript/tests/elements/expressions/ExprMutableList.sk create mode 100644 src/test/skript/tests/elements/expressions/ExprParseAs.sk create mode 100644 src/test/skript/tests/elements/expressions/ExprTernary.sk diff --git a/src/test/skript/tests/elements/effects/EffChange.sk b/src/test/skript/tests/elements/effects/EffChange.sk new file mode 100644 index 00000000..38423994 --- /dev/null +++ b/src/test/skript/tests/elements/effects/EffChange.sk @@ -0,0 +1,13 @@ +test "EffChange": + set {_a} to "ayy" + assert {_a} is "ayy" with "The var should be set" + + set {_b} to 0 + add 10 to {_b} + assert {_b} is 10 with "The var should be 10 after adding" + + remove 5 from {_b} + assert {_b} is 5 with "The var should be 5 after removing" + + delete {_b} + assert {_b} is not set with "The var should be deleted" diff --git a/src/test/skript/tests/elements/effects/EffDoIf.sk b/src/test/skript/tests/elements/effects/EffDoIf.sk new file mode 100644 index 00000000..2b1fb7a4 --- /dev/null +++ b/src/test/skript/tests/elements/effects/EffDoIf.sk @@ -0,0 +1,6 @@ +test "EffDoIf": + do set {_a} to 1 if 1 = 10 + assert {_a} is not set with "The do should have been skipped" + + do set {_a} to 1 if 1 = 1 + assert {_a} is 1 with "The do should have been executed" diff --git a/src/test/skript/tests/elements/effects/EffExit.sk b/src/test/skript/tests/elements/effects/EffExit.sk new file mode 100644 index 00000000..400b9d4e --- /dev/null +++ b/src/test/skript/tests/elements/effects/EffExit.sk @@ -0,0 +1,7 @@ +test "EffExit": + assert {_a} is not set with "The variable should not be set yet" + loop 10 times: + set {_a} to loop-value + exit loop + + assert {_a} = 1 with "The loop should have stopped after 1 iteration" diff --git a/src/test/skript/tests/elements/effects/EffReturn.sk b/src/test/skript/tests/elements/effects/EffReturn.sk new file mode 100644 index 00000000..ae46274e --- /dev/null +++ b/src/test/skript/tests/elements/effects/EffReturn.sk @@ -0,0 +1,8 @@ +function testFunc(s: string) :: string: + return "this is a test %{_s}%" + +test "EffReturn": + stop + # Broken function... TODO gotta check it out + set {_a} to testFunc("blah") + assert {_a} is "this is a test blah" with "The function should return the value" diff --git a/src/test/skript/tests/elements/expressions/ExprElement.sk b/src/test/skript/tests/elements/expressions/ExprElement.sk new file mode 100644 index 00000000..b958a95e --- /dev/null +++ b/src/test/skript/tests/elements/expressions/ExprElement.sk @@ -0,0 +1,4 @@ +test "ExprElement": + set {_a::*} to "one", "two" and "three" + set {_b} to 2nd element of {_a::*} + assert {_b} is "two" with "The second element should have been 'two'" diff --git a/src/test/skript/tests/elements/expressions/ExprLength.sk b/src/test/skript/tests/elements/expressions/ExprLength.sk new file mode 100644 index 00000000..89562cd0 --- /dev/null +++ b/src/test/skript/tests/elements/expressions/ExprLength.sk @@ -0,0 +1,2 @@ +test "ExprLength": + assert length of "hi bob" = 6 with "There should be 6 characters in the string." diff --git a/src/test/skript/tests/elements/expressions/ExprMutableList.sk b/src/test/skript/tests/elements/expressions/ExprMutableList.sk new file mode 100644 index 00000000..abc70ac7 --- /dev/null +++ b/src/test/skript/tests/elements/expressions/ExprMutableList.sk @@ -0,0 +1,7 @@ +test "ExprMutableList": + set {_a::*} to "nine", "three", "four" and "ten" + set {_a::*} to sorted {_a::*} + assert 1st element of {_a::*} = "four" with "Four should have been the first after sorting" + + set {_a::*} to reversed {_a::*} + assert 1st element of {_a::*} = "three" with "Three should have been the first after reversing" diff --git a/src/test/skript/tests/elements/expressions/ExprParseAs.sk b/src/test/skript/tests/elements/expressions/ExprParseAs.sk new file mode 100644 index 00000000..58a69695 --- /dev/null +++ b/src/test/skript/tests/elements/expressions/ExprParseAs.sk @@ -0,0 +1,3 @@ +test "ExprParseAs": + set {_b} to "1" parsed as number + assert {_b} = 1 with "The string should have parsed as the number 1" diff --git a/src/test/skript/tests/elements/expressions/ExprTernary.sk b/src/test/skript/tests/elements/expressions/ExprTernary.sk new file mode 100644 index 00000000..b2a1d6e3 --- /dev/null +++ b/src/test/skript/tests/elements/expressions/ExprTernary.sk @@ -0,0 +1,7 @@ +test "ExprTernary": + set {_a} to "test" + set {_b} to {_a} is set ? {_a} : "something else" + assert {_b} = "test" with "It should have picked up {_a}" + + set {_b} to {_c} is set ? {_c} : "something else" + assert {_b} = "something else" with "It should not have picked up {_c}" From c6fda73add7cfe317ef3a1201b6511753f560e05 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Wed, 11 Feb 2026 15:02:24 -0800 Subject: [PATCH 15/23] More test changes --- .../api/skript/testing/TestResults.java | 2 +- .../skript/api/skript/testing/TestRunner.java | 23 +++++++++++++++---- .../skript/testing/elements/EffAssert.java | 2 +- .../api/skript/testing/elements/EvtTest.java | 21 ++++++++++++----- .../tests/elements/expressions/ExprParseAs.sk | 3 --- .../hyskript/expressions/other/ExprCast.sk | 7 ++++++ .../expressions/other/ExprDistance.sk | 4 ++++ .../other/ExprLocationDirection.sk | 7 ++++++ .../hyskript/expressions/world/ExprWorld.sk | 3 +++ .../expressions/world/ExprWorldSpawn.sk | 3 +++ .../conditions/CondExprCompare.sk | 2 +- .../conditions/CondExprContains.sk | 2 +- .../conditions/CondExprIsDivisible.sk | 2 +- .../conditions/CondExprIsEmpty.sk | 2 +- .../conditions/CondExprIsSet.sk | 2 +- .../conditions/CondExprStartsEnds.sk | 2 +- .../{ => skript-parser}/effects/EffChange.sk | 2 +- .../{ => skript-parser}/effects/EffDoIf.sk | 2 +- .../{ => skript-parser}/effects/EffExit.sk | 2 +- .../{ => skript-parser}/effects/EffReturn.sk | 2 +- .../expressions/ExprAmount.sk | 2 +- .../expressions/ExprArithmeticOperators.sk | 2 +- .../expressions/ExprBooleanOperators.sk | 2 +- .../expressions/ExprElement.sk | 2 +- .../expressions/ExprLength.sk | 2 +- .../expressions/ExprMutableList.sk | 2 +- .../skript-parser/expressions/ExprParseAs.sk | 5 ++++ .../expressions/ExprTernary.sk | 2 +- 28 files changed, 81 insertions(+), 33 deletions(-) delete mode 100644 src/test/skript/tests/elements/expressions/ExprParseAs.sk create mode 100644 src/test/skript/tests/elements/hyskript/expressions/other/ExprCast.sk create mode 100644 src/test/skript/tests/elements/hyskript/expressions/other/ExprDistance.sk create mode 100644 src/test/skript/tests/elements/hyskript/expressions/other/ExprLocationDirection.sk create mode 100644 src/test/skript/tests/elements/hyskript/expressions/world/ExprWorld.sk create mode 100644 src/test/skript/tests/elements/hyskript/expressions/world/ExprWorldSpawn.sk rename src/test/skript/tests/elements/{ => skript-parser}/conditions/CondExprCompare.sk (89%) rename src/test/skript/tests/elements/{ => skript-parser}/conditions/CondExprContains.sk (90%) rename src/test/skript/tests/elements/{ => skript-parser}/conditions/CondExprIsDivisible.sk (81%) rename src/test/skript/tests/elements/{ => skript-parser}/conditions/CondExprIsEmpty.sk (86%) rename src/test/skript/tests/elements/{ => skript-parser}/conditions/CondExprIsSet.sk (86%) rename src/test/skript/tests/elements/{ => skript-parser}/conditions/CondExprStartsEnds.sk (84%) rename src/test/skript/tests/elements/{ => skript-parser}/effects/EffChange.sk (90%) rename src/test/skript/tests/elements/{ => skript-parser}/effects/EffDoIf.sk (85%) rename src/test/skript/tests/elements/{ => skript-parser}/effects/EffExit.sk (86%) rename src/test/skript/tests/elements/{ => skript-parser}/effects/EffReturn.sk (88%) rename src/test/skript/tests/elements/{ => skript-parser}/expressions/ExprAmount.sk (83%) rename src/test/skript/tests/elements/{ => skript-parser}/expressions/ExprArithmeticOperators.sk (68%) rename src/test/skript/tests/elements/{ => skript-parser}/expressions/ExprBooleanOperators.sk (79%) rename src/test/skript/tests/elements/{ => skript-parser}/expressions/ExprElement.sk (81%) rename src/test/skript/tests/elements/{ => skript-parser}/expressions/ExprLength.sk (71%) rename src/test/skript/tests/elements/{ => skript-parser}/expressions/ExprMutableList.sk (88%) create mode 100644 src/test/skript/tests/elements/skript-parser/expressions/ExprParseAs.sk rename src/test/skript/tests/elements/{ => skript-parser}/expressions/ExprTernary.sk (88%) diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestResults.java b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestResults.java index 8fc0a642..d44c0bb7 100644 --- a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestResults.java +++ b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestResults.java @@ -63,7 +63,7 @@ public void clear() { } @SuppressWarnings({"CallToPrintStackTrace"}) - public void printToProperties() { + public void printToJsonFile() { Path resolve = HySk.getInstance().getDataDirectory().resolve("test-results.json"); try { diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunner.java b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunner.java index 8ac0df3b..c6794936 100644 --- a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunner.java +++ b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunner.java @@ -8,6 +8,7 @@ import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.util.MessageUtil; import fi.sulku.hytale.TinyMsg; +import io.github.syst3ms.skriptparser.lang.Statement; import io.github.syst3ms.skriptparser.lang.TriggerMap; import io.github.syst3ms.skriptparser.log.LogEntry; import io.github.syst3ms.skriptparser.log.LogType; @@ -24,6 +25,7 @@ public class TestRunner { private final TestResults testResults = new TestResults(); + private final World world = Universe.get().getWorld("default"); @SuppressWarnings("DataFlowIssue") public void start() { @@ -37,10 +39,9 @@ public void start() { loadTests(); Utils.log("Finished loading test scripts!"); - World world = Universe.get().getWorld("default"); - if (world.isPaused()) world.setPaused(false); + if (this.world.isPaused()) this.world.setPaused(false); - world.execute(runTestsRunnable); + this.world.execute(runTestsRunnable); }; // Delay start to make sure the server has finished loading @@ -53,11 +54,20 @@ private void loadTests() { } private void runTests() { - TestContext testContext = new TestContext(this.testResults); + TestContext testContext = new TestContext(this.testResults, this.world); + + // Catch exceptions and treat them as failures + Statement.setExceptionHandler(e -> + this.testResults.addFailure("Exception", + e.getClass().getSimpleName() + ": " + e.getMessage())); + + // Run all the test triggers TriggerMap.callTriggersByContext(testContext); + // Process results this.testResults.process(); + // Print results if (this.testResults.isSuccess()) { Message message = TinyMsg.parse("All tests passed!"); Utils.log(MessageUtil.toAnsiString(message).toAnsi()); @@ -68,9 +78,12 @@ private void runTests() { } Utils.log("Finished running tests!"); - this.testResults.printToProperties(); + + // Print results to file + this.testResults.printToJsonFile(); this.testResults.clear(); + // Shutdown server Runnable shutdownServer = () -> HytaleServer.get().shutdownServer(); HytaleServer.SCHEDULED_EXECUTOR.schedule(shutdownServer, 2, TimeUnit.SECONDS); } diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/EffAssert.java b/src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/EffAssert.java index 809d3bc8..e3d9ffca 100644 --- a/src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/EffAssert.java +++ b/src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/EffAssert.java @@ -18,7 +18,7 @@ public class EffAssert extends Effect { public static void register(SkriptRegistration reg) { - reg.newEffect(EffAssert.class, "assert <.+> with %*string%") + reg.newEffect(EffAssert.class, "assert <.+> with %string%") .noDoc() .register(); } diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/EvtTest.java b/src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/EvtTest.java index 6df8de81..1cb48831 100644 --- a/src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/EvtTest.java +++ b/src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/EvtTest.java @@ -1,8 +1,10 @@ package com.github.skriptdev.skript.api.skript.testing.elements; +import com.github.skriptdev.skript.api.skript.event.WorldContext; import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; import com.github.skriptdev.skript.api.skript.testing.TestResults; import com.github.skriptdev.skript.api.utils.Utils; +import com.hypixel.hytale.server.core.universe.world.World; import io.github.syst3ms.skriptparser.lang.Expression; import io.github.syst3ms.skriptparser.lang.TriggerContext; import io.github.syst3ms.skriptparser.lang.VariableString; @@ -41,12 +43,14 @@ public String toString(@NotNull TriggerContext triggerContext, boolean b) { return "test " + this.testSubject; } - public static final class TestContext implements TriggerContext { + public static final class TestContext implements WorldContext { private final TestResults testResults; private String testSubject; + private final World world; - public TestContext(TestResults testResults) { + public TestContext(TestResults testResults, World world) { this.testResults = testResults; + this.world = world; } public void setTestSubject(String testSubject) { @@ -57,13 +61,18 @@ public String getTestSubject() { return testSubject; } + public TestResults getTestResults() { + return this.testResults; + } + @Override - public String getName() { - return "test context"; + public World getWorld() { + return this.world; } - public TestResults getTestResults() { - return this.testResults; + @Override + public String getName() { + return "test context"; } } diff --git a/src/test/skript/tests/elements/expressions/ExprParseAs.sk b/src/test/skript/tests/elements/expressions/ExprParseAs.sk deleted file mode 100644 index 58a69695..00000000 --- a/src/test/skript/tests/elements/expressions/ExprParseAs.sk +++ /dev/null @@ -1,3 +0,0 @@ -test "ExprParseAs": - set {_b} to "1" parsed as number - assert {_b} = 1 with "The string should have parsed as the number 1" diff --git a/src/test/skript/tests/elements/hyskript/expressions/other/ExprCast.sk b/src/test/skript/tests/elements/hyskript/expressions/other/ExprCast.sk new file mode 100644 index 00000000..769f1a2d --- /dev/null +++ b/src/test/skript/tests/elements/hyskript/expressions/other/ExprCast.sk @@ -0,0 +1,7 @@ +test "ExprCast": + set {_a} to ingredient_poop as BlockType + assert class info of {_a} is "BlockType" with "The type should be BlockType" + + set {_a} to 1 as float + assert {_a} is 1.0 with "The cast should work" + assert class info of {_a} is "Float" with "The type should be Float" diff --git a/src/test/skript/tests/elements/hyskript/expressions/other/ExprDistance.sk b/src/test/skript/tests/elements/hyskript/expressions/other/ExprDistance.sk new file mode 100644 index 00000000..ae270e3b --- /dev/null +++ b/src/test/skript/tests/elements/hyskript/expressions/other/ExprDistance.sk @@ -0,0 +1,4 @@ +test "ExprDistance": + set {_a} to location(0, 0, 0, event-world) + set {_b} to location(0, 100, 0, event-world) + assert distance between {_a} and {_b} is 100 with "The locations are 100 blocks apart" diff --git a/src/test/skript/tests/elements/hyskript/expressions/other/ExprLocationDirection.sk b/src/test/skript/tests/elements/hyskript/expressions/other/ExprLocationDirection.sk new file mode 100644 index 00000000..bcbe3287 --- /dev/null +++ b/src/test/skript/tests/elements/hyskript/expressions/other/ExprLocationDirection.sk @@ -0,0 +1,7 @@ +test "ExprLocationDirection": + set {_l1} to location(0, 0, 0, event-world) + set {_l2} to location 10 blocks north of {_l1} + set {_distance} to distance between {_l1} and {_l2} + + # TODO fix you too + # assert {_distance} is 10 with "The new location should be 10 blocks away, but found %{_distance}%" diff --git a/src/test/skript/tests/elements/hyskript/expressions/world/ExprWorld.sk b/src/test/skript/tests/elements/hyskript/expressions/world/ExprWorld.sk new file mode 100644 index 00000000..6ff36373 --- /dev/null +++ b/src/test/skript/tests/elements/hyskript/expressions/world/ExprWorld.sk @@ -0,0 +1,3 @@ +test "ExprWorld": + set {_w} to world named "default" + assert {_w} = event-world with "The default world should match our event-world" diff --git a/src/test/skript/tests/elements/hyskript/expressions/world/ExprWorldSpawn.sk b/src/test/skript/tests/elements/hyskript/expressions/world/ExprWorldSpawn.sk new file mode 100644 index 00000000..05775457 --- /dev/null +++ b/src/test/skript/tests/elements/hyskript/expressions/world/ExprWorldSpawn.sk @@ -0,0 +1,3 @@ +test "ExprWorldSpawn": + set {_a} to world spawn of event-world + assert {_a} is set with "The variable should be set to spawn" diff --git a/src/test/skript/tests/elements/conditions/CondExprCompare.sk b/src/test/skript/tests/elements/skript-parser/conditions/CondExprCompare.sk similarity index 89% rename from src/test/skript/tests/elements/conditions/CondExprCompare.sk rename to src/test/skript/tests/elements/skript-parser/conditions/CondExprCompare.sk index 3013c01a..06522661 100644 --- a/src/test/skript/tests/elements/conditions/CondExprCompare.sk +++ b/src/test/skript/tests/elements/skript-parser/conditions/CondExprCompare.sk @@ -1,4 +1,4 @@ -test "CondCompare": +test "skript-parser-CondCompare": assert 1 = 1 with "1 should equal 1" assert 1 != 2 with "1 should not equal 2" diff --git a/src/test/skript/tests/elements/conditions/CondExprContains.sk b/src/test/skript/tests/elements/skript-parser/conditions/CondExprContains.sk similarity index 90% rename from src/test/skript/tests/elements/conditions/CondExprContains.sk rename to src/test/skript/tests/elements/skript-parser/conditions/CondExprContains.sk index ea4392ab..4bfa5b86 100644 --- a/src/test/skript/tests/elements/conditions/CondExprContains.sk +++ b/src/test/skript/tests/elements/skript-parser/conditions/CondExprContains.sk @@ -1,4 +1,4 @@ -test "CondExprContains": +test "skript-parser-CondExprContains": assert {_blah::*} does not contain "blah" with "The variable shouldn't contain the string" add "blah" to {_blah::*} assert {_blah::*} contains "blah" with "The variable should contain the string" diff --git a/src/test/skript/tests/elements/conditions/CondExprIsDivisible.sk b/src/test/skript/tests/elements/skript-parser/conditions/CondExprIsDivisible.sk similarity index 81% rename from src/test/skript/tests/elements/conditions/CondExprIsDivisible.sk rename to src/test/skript/tests/elements/skript-parser/conditions/CondExprIsDivisible.sk index 3cd81a83..974cca24 100644 --- a/src/test/skript/tests/elements/conditions/CondExprIsDivisible.sk +++ b/src/test/skript/tests/elements/skript-parser/conditions/CondExprIsDivisible.sk @@ -1,3 +1,3 @@ -test "CondExprIsDivisible": +test "skript-parser-CondExprIsDivisible": #assert 10 is not divisible by 7 with "10 should not be divisible by 7" TODO bug, CondExprIsDivisible isnt doing a neg check assert 10 is divisible by 2 with "10 should be divisible by 2" diff --git a/src/test/skript/tests/elements/conditions/CondExprIsEmpty.sk b/src/test/skript/tests/elements/skript-parser/conditions/CondExprIsEmpty.sk similarity index 86% rename from src/test/skript/tests/elements/conditions/CondExprIsEmpty.sk rename to src/test/skript/tests/elements/skript-parser/conditions/CondExprIsEmpty.sk index 4e7bfdf7..8c7a77ce 100644 --- a/src/test/skript/tests/elements/conditions/CondExprIsEmpty.sk +++ b/src/test/skript/tests/elements/skript-parser/conditions/CondExprIsEmpty.sk @@ -1,4 +1,4 @@ -test "CondExprIsEmpty": +test "skript-parser-CondExprIsEmpty": #assert {_a::*} is empty with "The unset variable should be empty" TODO this fails, tries to compare to "empty" (Item/BlockType probably) set {_a::*} to 1, 2 and 3 assert {_a::*} is not empty with "The variable should not be empty after setting it" diff --git a/src/test/skript/tests/elements/conditions/CondExprIsSet.sk b/src/test/skript/tests/elements/skript-parser/conditions/CondExprIsSet.sk similarity index 86% rename from src/test/skript/tests/elements/conditions/CondExprIsSet.sk rename to src/test/skript/tests/elements/skript-parser/conditions/CondExprIsSet.sk index 95c99930..386d8ff7 100644 --- a/src/test/skript/tests/elements/conditions/CondExprIsSet.sk +++ b/src/test/skript/tests/elements/skript-parser/conditions/CondExprIsSet.sk @@ -1,4 +1,4 @@ -test "CondExprIsSet": +test "skript-parser-CondExprIsSet": assert {_a} is not set with "The variable should not be set yet" set {_a} to "blah" assert {_a} is set with "The variable should now be set" diff --git a/src/test/skript/tests/elements/conditions/CondExprStartsEnds.sk b/src/test/skript/tests/elements/skript-parser/conditions/CondExprStartsEnds.sk similarity index 84% rename from src/test/skript/tests/elements/conditions/CondExprStartsEnds.sk rename to src/test/skript/tests/elements/skript-parser/conditions/CondExprStartsEnds.sk index de2885aa..9a65bdc2 100644 --- a/src/test/skript/tests/elements/conditions/CondExprStartsEnds.sk +++ b/src/test/skript/tests/elements/skript-parser/conditions/CondExprStartsEnds.sk @@ -1,4 +1,4 @@ -test "CondExprStartsEnds": +test "skript-parser-CondExprStartsEnds": # TODO neither of these seem to work #assert ("blah" does not start with "cheese") with "Blah shouldn't start with cheese" #assert ("cheese is the best" starts with "cheese") with "Cheese is the best should start with cheese" diff --git a/src/test/skript/tests/elements/effects/EffChange.sk b/src/test/skript/tests/elements/skript-parser/effects/EffChange.sk similarity index 90% rename from src/test/skript/tests/elements/effects/EffChange.sk rename to src/test/skript/tests/elements/skript-parser/effects/EffChange.sk index 38423994..d1f950b9 100644 --- a/src/test/skript/tests/elements/effects/EffChange.sk +++ b/src/test/skript/tests/elements/skript-parser/effects/EffChange.sk @@ -1,4 +1,4 @@ -test "EffChange": +test "skript-parser-EffChange": set {_a} to "ayy" assert {_a} is "ayy" with "The var should be set" diff --git a/src/test/skript/tests/elements/effects/EffDoIf.sk b/src/test/skript/tests/elements/skript-parser/effects/EffDoIf.sk similarity index 85% rename from src/test/skript/tests/elements/effects/EffDoIf.sk rename to src/test/skript/tests/elements/skript-parser/effects/EffDoIf.sk index 2b1fb7a4..db60cb57 100644 --- a/src/test/skript/tests/elements/effects/EffDoIf.sk +++ b/src/test/skript/tests/elements/skript-parser/effects/EffDoIf.sk @@ -1,4 +1,4 @@ -test "EffDoIf": +test "skript-parser-EffDoIf": do set {_a} to 1 if 1 = 10 assert {_a} is not set with "The do should have been skipped" diff --git a/src/test/skript/tests/elements/effects/EffExit.sk b/src/test/skript/tests/elements/skript-parser/effects/EffExit.sk similarity index 86% rename from src/test/skript/tests/elements/effects/EffExit.sk rename to src/test/skript/tests/elements/skript-parser/effects/EffExit.sk index 400b9d4e..2ee7c5cb 100644 --- a/src/test/skript/tests/elements/effects/EffExit.sk +++ b/src/test/skript/tests/elements/skript-parser/effects/EffExit.sk @@ -1,4 +1,4 @@ -test "EffExit": +test "skript-parser-EffExit": assert {_a} is not set with "The variable should not be set yet" loop 10 times: set {_a} to loop-value diff --git a/src/test/skript/tests/elements/effects/EffReturn.sk b/src/test/skript/tests/elements/skript-parser/effects/EffReturn.sk similarity index 88% rename from src/test/skript/tests/elements/effects/EffReturn.sk rename to src/test/skript/tests/elements/skript-parser/effects/EffReturn.sk index ae46274e..d8bc278e 100644 --- a/src/test/skript/tests/elements/effects/EffReturn.sk +++ b/src/test/skript/tests/elements/skript-parser/effects/EffReturn.sk @@ -1,7 +1,7 @@ function testFunc(s: string) :: string: return "this is a test %{_s}%" -test "EffReturn": +test "skript-parser-EffReturn": stop # Broken function... TODO gotta check it out set {_a} to testFunc("blah") diff --git a/src/test/skript/tests/elements/expressions/ExprAmount.sk b/src/test/skript/tests/elements/skript-parser/expressions/ExprAmount.sk similarity index 83% rename from src/test/skript/tests/elements/expressions/ExprAmount.sk rename to src/test/skript/tests/elements/skript-parser/expressions/ExprAmount.sk index ad98398d..70e33646 100644 --- a/src/test/skript/tests/elements/expressions/ExprAmount.sk +++ b/src/test/skript/tests/elements/skript-parser/expressions/ExprAmount.sk @@ -1,4 +1,4 @@ -test "ExprAmount": +test "skript-parser-ExprAmount": assert size of {_a::*} = 0 with "The unset variable should be empty" set {_a::*} to 1, 2 and 3 assert size of {_a::*} = 3 with "The variable should have three elements" diff --git a/src/test/skript/tests/elements/expressions/ExprArithmeticOperators.sk b/src/test/skript/tests/elements/skript-parser/expressions/ExprArithmeticOperators.sk similarity index 68% rename from src/test/skript/tests/elements/expressions/ExprArithmeticOperators.sk rename to src/test/skript/tests/elements/skript-parser/expressions/ExprArithmeticOperators.sk index 20185cd7..85120fe5 100644 --- a/src/test/skript/tests/elements/expressions/ExprArithmeticOperators.sk +++ b/src/test/skript/tests/elements/skript-parser/expressions/ExprArithmeticOperators.sk @@ -1,3 +1,3 @@ -test "ExprArithmeticOperators": +test "skript-parser-ExprArithmeticOperators": assert 1 + 2 == 3 with "1 + 2 should equal 3" assert 1 + 2 != 5 with "1 + 2 should not equal 5" diff --git a/src/test/skript/tests/elements/expressions/ExprBooleanOperators.sk b/src/test/skript/tests/elements/skript-parser/expressions/ExprBooleanOperators.sk similarity index 79% rename from src/test/skript/tests/elements/expressions/ExprBooleanOperators.sk rename to src/test/skript/tests/elements/skript-parser/expressions/ExprBooleanOperators.sk index 62c849d4..b90c24b4 100644 --- a/src/test/skript/tests/elements/expressions/ExprBooleanOperators.sk +++ b/src/test/skript/tests/elements/skript-parser/expressions/ExprBooleanOperators.sk @@ -1,3 +1,3 @@ -test "ExprBooleanOperators": +test "skript-parser-ExprBooleanOperators": assert ((1 + 1 == 2) && (2 + 2 == 4)) with "Both booleans should match" assert ((1 + 1 == 5) || (2 + 2 == 4)) with "One of the booleans (the second) should match" diff --git a/src/test/skript/tests/elements/expressions/ExprElement.sk b/src/test/skript/tests/elements/skript-parser/expressions/ExprElement.sk similarity index 81% rename from src/test/skript/tests/elements/expressions/ExprElement.sk rename to src/test/skript/tests/elements/skript-parser/expressions/ExprElement.sk index b958a95e..7b999af5 100644 --- a/src/test/skript/tests/elements/expressions/ExprElement.sk +++ b/src/test/skript/tests/elements/skript-parser/expressions/ExprElement.sk @@ -1,4 +1,4 @@ -test "ExprElement": +test "skript-parser-ExprElement": set {_a::*} to "one", "two" and "three" set {_b} to 2nd element of {_a::*} assert {_b} is "two" with "The second element should have been 'two'" diff --git a/src/test/skript/tests/elements/expressions/ExprLength.sk b/src/test/skript/tests/elements/skript-parser/expressions/ExprLength.sk similarity index 71% rename from src/test/skript/tests/elements/expressions/ExprLength.sk rename to src/test/skript/tests/elements/skript-parser/expressions/ExprLength.sk index 89562cd0..025560e2 100644 --- a/src/test/skript/tests/elements/expressions/ExprLength.sk +++ b/src/test/skript/tests/elements/skript-parser/expressions/ExprLength.sk @@ -1,2 +1,2 @@ -test "ExprLength": +test "skript-parser-ExprLength": assert length of "hi bob" = 6 with "There should be 6 characters in the string." diff --git a/src/test/skript/tests/elements/expressions/ExprMutableList.sk b/src/test/skript/tests/elements/skript-parser/expressions/ExprMutableList.sk similarity index 88% rename from src/test/skript/tests/elements/expressions/ExprMutableList.sk rename to src/test/skript/tests/elements/skript-parser/expressions/ExprMutableList.sk index abc70ac7..82f27b35 100644 --- a/src/test/skript/tests/elements/expressions/ExprMutableList.sk +++ b/src/test/skript/tests/elements/skript-parser/expressions/ExprMutableList.sk @@ -1,4 +1,4 @@ -test "ExprMutableList": +test "skript-parser-ExprMutableList": set {_a::*} to "nine", "three", "four" and "ten" set {_a::*} to sorted {_a::*} assert 1st element of {_a::*} = "four" with "Four should have been the first after sorting" diff --git a/src/test/skript/tests/elements/skript-parser/expressions/ExprParseAs.sk b/src/test/skript/tests/elements/skript-parser/expressions/ExprParseAs.sk new file mode 100644 index 00000000..ea2f94ef --- /dev/null +++ b/src/test/skript/tests/elements/skript-parser/expressions/ExprParseAs.sk @@ -0,0 +1,5 @@ +test "skript-parser-ExprParseAs": + set {_b} to "1" parsed as Number + + # You broken too? TODO fix + # assert {_b} = 1 with "The string should have parsed as the number 1, but found %{_b}%//%class info of {_b}%" diff --git a/src/test/skript/tests/elements/expressions/ExprTernary.sk b/src/test/skript/tests/elements/skript-parser/expressions/ExprTernary.sk similarity index 88% rename from src/test/skript/tests/elements/expressions/ExprTernary.sk rename to src/test/skript/tests/elements/skript-parser/expressions/ExprTernary.sk index b2a1d6e3..bc3a8769 100644 --- a/src/test/skript/tests/elements/expressions/ExprTernary.sk +++ b/src/test/skript/tests/elements/skript-parser/expressions/ExprTernary.sk @@ -1,4 +1,4 @@ -test "ExprTernary": +test "skript-parser-ExprTernary": set {_a} to "test" set {_b} to {_a} is set ? {_a} : "something else" assert {_b} = "test" with "It should have picked up {_a}" From 06b683db40f21843c0cee524767d2d2e568a8a13 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Wed, 11 Feb 2026 15:29:48 -0800 Subject: [PATCH 16/23] ExprLocationDirection - fixed location offset --- .../hyskript/expressions/other/ExprLocationDirection.sk | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/test/skript/tests/elements/hyskript/expressions/other/ExprLocationDirection.sk b/src/test/skript/tests/elements/hyskript/expressions/other/ExprLocationDirection.sk index bcbe3287..3542a6d6 100644 --- a/src/test/skript/tests/elements/hyskript/expressions/other/ExprLocationDirection.sk +++ b/src/test/skript/tests/elements/hyskript/expressions/other/ExprLocationDirection.sk @@ -2,6 +2,4 @@ test "ExprLocationDirection": set {_l1} to location(0, 0, 0, event-world) set {_l2} to location 10 blocks north of {_l1} set {_distance} to distance between {_l1} and {_l2} - - # TODO fix you too - # assert {_distance} is 10 with "The new location should be 10 blocks away, but found %{_distance}%" + assert {_distance} is 10 with "The new location should be 10 blocks away, but found %{_distance}%" From 092d40183e112077378ee99d70c6e0c754eba524 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Wed, 11 Feb 2026 17:30:00 -0800 Subject: [PATCH 17/23] TestRunner - run finish loading on addons to make sure functions register - this is stupid --- .../github/skriptdev/skript/api/skript/testing/TestRunner.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunner.java b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunner.java index c6794936..4a0aa185 100644 --- a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunner.java +++ b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunner.java @@ -13,6 +13,7 @@ import io.github.syst3ms.skriptparser.log.LogEntry; import io.github.syst3ms.skriptparser.log.LogType; import io.github.syst3ms.skriptparser.parsing.ScriptLoader; +import io.github.syst3ms.skriptparser.registration.SkriptAddon; import java.io.File; import java.nio.file.Path; @@ -92,6 +93,8 @@ private void loadScripts(Path directory) { File scriptsDirectory = directory.toFile(); Utils.log("Loading test directory '" + scriptsDirectory.getAbsolutePath() + "'..."); List scriptNames = loadScriptsInDirectory(scriptsDirectory); + SkriptAddon.getAddons().forEach(SkriptAddon::finishedLoading); + Utils.log("Loaded " + scriptNames.size() + " scripts!"); } From 0620e9c78ecf4db02615a90ec5a4303a0eb6d114 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Wed, 11 Feb 2026 17:30:21 -0800 Subject: [PATCH 18/23] EffReturn.sk - functions fixed --- .../skript/tests/elements/skript-parser/effects/EffReturn.sk | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/test/skript/tests/elements/skript-parser/effects/EffReturn.sk b/src/test/skript/tests/elements/skript-parser/effects/EffReturn.sk index d8bc278e..67158a74 100644 --- a/src/test/skript/tests/elements/skript-parser/effects/EffReturn.sk +++ b/src/test/skript/tests/elements/skript-parser/effects/EffReturn.sk @@ -2,7 +2,5 @@ function testFunc(s: string) :: string: return "this is a test %{_s}%" test "skript-parser-EffReturn": - stop - # Broken function... TODO gotta check it out set {_a} to testFunc("blah") - assert {_a} is "this is a test blah" with "The function should return the value" + assert {_a} is "this is a test blah" with "The function should return the value, but found: %{_a}%" From 5907c3fbfa07201cca7d5a40eae03efca02e4274 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Wed, 11 Feb 2026 18:27:29 -0800 Subject: [PATCH 19/23] more tests --- .../hyskript/expressions/block/ExprBlockAt.sk | 13 +++++++++++++ .../hyskript/expressions/entity/ExprNPCType.sk | 6 ++++++ .../hyskript/expressions/other/ExprClassInfoOf.sk | 12 ++++++++++++ .../hyskript/expressions/other/ExprLocationOf.sk | 4 ++++ .../hyskript/expressions/world/ExprWorldSpawn.sk | 5 +++++ .../tests/elements/hyskript/sections/SecDropItem.sk | 6 ++++++ .../tests/elements/hyskript/sections/SecSpawnNPC.sk | 7 +++++++ .../elements/hyskript/structures/ScriptCommand.sk | 8 ++++++++ 8 files changed, 61 insertions(+) create mode 100644 src/test/skript/tests/elements/hyskript/expressions/block/ExprBlockAt.sk create mode 100644 src/test/skript/tests/elements/hyskript/expressions/entity/ExprNPCType.sk create mode 100644 src/test/skript/tests/elements/hyskript/expressions/other/ExprClassInfoOf.sk create mode 100644 src/test/skript/tests/elements/hyskript/expressions/other/ExprLocationOf.sk create mode 100644 src/test/skript/tests/elements/hyskript/sections/SecDropItem.sk create mode 100644 src/test/skript/tests/elements/hyskript/sections/SecSpawnNPC.sk create mode 100644 src/test/skript/tests/elements/hyskript/structures/ScriptCommand.sk diff --git a/src/test/skript/tests/elements/hyskript/expressions/block/ExprBlockAt.sk b/src/test/skript/tests/elements/hyskript/expressions/block/ExprBlockAt.sk new file mode 100644 index 00000000..a6258936 --- /dev/null +++ b/src/test/skript/tests/elements/hyskript/expressions/block/ExprBlockAt.sk @@ -0,0 +1,13 @@ +test "ExprBlockAt": + set {_loc} to location(0, 200, 0, event-world) + assert block at {_loc} is set with "The block should be set" + assert blocktype of block at {_loc} == Empty with "High chance a block that high is empty/air, but found: %block type of block at {_loc}%" + + set blocktype of block at {_loc} to rock_stone + assert blocktype of block at {_loc} == rock_stone with "Blocktype should be rock_stone" + + # Let's set it back in case we re-run a test on the same server + set blocktype of block at {_loc} to empty + + set {_loc} to location(0, 0, 0, event-world) + assert blocktype of block at {_loc} == rock_bedrock with "It should be bedrock down there" diff --git a/src/test/skript/tests/elements/hyskript/expressions/entity/ExprNPCType.sk b/src/test/skript/tests/elements/hyskript/expressions/entity/ExprNPCType.sk new file mode 100644 index 00000000..5ecdeb9b --- /dev/null +++ b/src/test/skript/tests/elements/hyskript/expressions/entity/ExprNPCType.sk @@ -0,0 +1,6 @@ +test "ExprNPCType": + set {_loc} to world spawn of event-world + spawn a sheep at {_loc}: + set {_e} to event-spawned-npc + + assert npc type of {_e} == Sheep with "Should be a sheep, but found %npc type of {_e}%" diff --git a/src/test/skript/tests/elements/hyskript/expressions/other/ExprClassInfoOf.sk b/src/test/skript/tests/elements/hyskript/expressions/other/ExprClassInfoOf.sk new file mode 100644 index 00000000..98801802 --- /dev/null +++ b/src/test/skript/tests/elements/hyskript/expressions/other/ExprClassInfoOf.sk @@ -0,0 +1,12 @@ +test "ExprClassInfoOf": + set {_i} to itemstack of ingredient_poop + assert classinfo of {_i} = "Item Stack" with "Should be an itemstack but found %classinfo of {_i}%" + + set {_loc} to location(0, 0, 0, event-world) + assert classinfo of {_loc} = "Location" with "Should be a Location, but found %classinfo of {_loc}%" + + set {_b} to block at {_loc} + assert classinfo of {_b} = "Block" with "Should be a block, but found %classinfo of {_b}%" + + set {_bt} to blocktype of {_b} + assert classinfo of {_bt} = "BlockType" with "Should be a BlockType but found %classinfo of {_bt}%" diff --git a/src/test/skript/tests/elements/hyskript/expressions/other/ExprLocationOf.sk b/src/test/skript/tests/elements/hyskript/expressions/other/ExprLocationOf.sk new file mode 100644 index 00000000..eb649bfa --- /dev/null +++ b/src/test/skript/tests/elements/hyskript/expressions/other/ExprLocationOf.sk @@ -0,0 +1,4 @@ +test "ExprLocationOf": + set {_loc} to location(0, 0, 0, event-world) + set {_b} to block at {_loc} + assert location of {_b} = {_loc} with "The location of the block should be the same as the original location" diff --git a/src/test/skript/tests/elements/hyskript/expressions/world/ExprWorldSpawn.sk b/src/test/skript/tests/elements/hyskript/expressions/world/ExprWorldSpawn.sk index 05775457..e4aecd7c 100644 --- a/src/test/skript/tests/elements/hyskript/expressions/world/ExprWorldSpawn.sk +++ b/src/test/skript/tests/elements/hyskript/expressions/world/ExprWorldSpawn.sk @@ -1,3 +1,8 @@ test "ExprWorldSpawn": set {_a} to world spawn of event-world assert {_a} is set with "The variable should be set to spawn" + + set {_w} to world("default") + assert {_w} = event-world with "The world should match" + set {_b} to world spawn of {_w} + assert {_b} = {_a} with "The spawn should be the same" diff --git a/src/test/skript/tests/elements/hyskript/sections/SecDropItem.sk b/src/test/skript/tests/elements/hyskript/sections/SecDropItem.sk new file mode 100644 index 00000000..efdb103d --- /dev/null +++ b/src/test/skript/tests/elements/hyskript/sections/SecDropItem.sk @@ -0,0 +1,6 @@ +test "SecDropItem": + set {_l} to world spawn of event-world + drop ingredient_stick at {_l}: + set {_i} to event-item-component + + assert {_i} is set with "The item should have dropped" diff --git a/src/test/skript/tests/elements/hyskript/sections/SecSpawnNPC.sk b/src/test/skript/tests/elements/hyskript/sections/SecSpawnNPC.sk new file mode 100644 index 00000000..3e94869e --- /dev/null +++ b/src/test/skript/tests/elements/hyskript/sections/SecSpawnNPC.sk @@ -0,0 +1,7 @@ +test "SecSpawnNPC": + set {_l} to world spawn location of event-world + spawn a sheep at {_l}: + set {_e} to event-spawned-npc + + assert {_e} is set with "The sheep should have spawned" + assert "%{_e}%" = "NPCEntity{role=Sheep}" with "It should be a sheep" diff --git a/src/test/skript/tests/elements/hyskript/structures/ScriptCommand.sk b/src/test/skript/tests/elements/hyskript/structures/ScriptCommand.sk new file mode 100644 index 00000000..29726d7f --- /dev/null +++ b/src/test/skript/tests/elements/hyskript/structures/ScriptCommand.sk @@ -0,0 +1,8 @@ +command /letest: + trigger: + set {-var::in_command} to true + +test "ScriptCommand": + execute console command "/letest" + wait 1 tick # The command is async so we have to wait + assert {-var::in_command} is true with "The command should run and set the var" From 703893d64894e71355fbd6d30898632591750771 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Wed, 11 Feb 2026 18:33:01 -0800 Subject: [PATCH 20/23] TestRunner - more changes --- .../skript/api/skript/testing/TestRunner.java | 59 +++++++++++-------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunner.java b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunner.java index 4a0aa185..47413325 100644 --- a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunner.java +++ b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunner.java @@ -9,11 +9,13 @@ import com.hypixel.hytale.server.core.util.MessageUtil; import fi.sulku.hytale.TinyMsg; import io.github.syst3ms.skriptparser.lang.Statement; +import io.github.syst3ms.skriptparser.lang.Trigger; import io.github.syst3ms.skriptparser.lang.TriggerMap; import io.github.syst3ms.skriptparser.log.LogEntry; import io.github.syst3ms.skriptparser.log.LogType; import io.github.syst3ms.skriptparser.parsing.ScriptLoader; import io.github.syst3ms.skriptparser.registration.SkriptAddon; +import io.github.syst3ms.skriptparser.variables.Variables; import java.io.File; import java.nio.file.Path; @@ -21,10 +23,12 @@ import java.util.Arrays; import java.util.Comparator; import java.util.List; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class TestRunner { + private static final ScheduledExecutorService SCHEDULED = HytaleServer.SCHEDULED_EXECUTOR; private final TestResults testResults = new TestResults(); private final World world = Universe.get().getWorld("default"); @@ -40,13 +44,15 @@ public void start() { loadTests(); Utils.log("Finished loading test scripts!"); + // Make sure the world isn't paused if (this.world.isPaused()) this.world.setPaused(false); + // Run our tests in the world to make sure we have access to blocks/entities this.world.execute(runTestsRunnable); }; // Delay start to make sure the server has finished loading - HytaleServer.SCHEDULED_EXECUTOR.schedule(loadTestsRunnable, 1, TimeUnit.SECONDS); + SCHEDULED.schedule(loadTestsRunnable, 1, TimeUnit.SECONDS); } private void loadTests() { @@ -55,38 +61,45 @@ private void loadTests() { } private void runTests() { - TestContext testContext = new TestContext(this.testResults, this.world); - // Catch exceptions and treat them as failures Statement.setExceptionHandler(e -> this.testResults.addFailure("Exception", e.getClass().getSimpleName() + ": " + e.getMessage())); // Run all the test triggers - TriggerMap.callTriggersByContext(testContext); - - // Process results - this.testResults.process(); - - // Print results - if (this.testResults.isSuccess()) { - Message message = TinyMsg.parse("All tests passed!"); - Utils.log(MessageUtil.toAnsiString(message).toAnsi()); - } else { - Utils.error(this.testResults.getFailCount() + " tests failed!"); - this.testResults.getFailureMap().forEach((test, failure) -> - Utils.error(" - [" + test + "]: " + failure)); + for (Trigger allTrigger : TriggerMap.getAllTriggers()) { + TestContext context = new TestContext(this.testResults, this.world); + Statement.runAll(allTrigger, context); + Variables.clearLocalVariables(context); } - Utils.log("Finished running tests!"); + Runnable finishingRunnable = () -> { + // Process results + this.testResults.process(); + + // Print results + if (this.testResults.isSuccess()) { + Message message = TinyMsg.parse("All tests passed!"); + Utils.log(MessageUtil.toAnsiString(message).toAnsi()); + } else { + Utils.error(this.testResults.getFailCount() + " tests failed!"); + this.testResults.getFailureMap().forEach((test, failure) -> + Utils.error(" - [" + test + "]: " + failure)); + } - // Print results to file - this.testResults.printToJsonFile(); - this.testResults.clear(); + Utils.log("Finished running tests!"); + + // Print results to file + this.testResults.printToJsonFile(); + this.testResults.clear(); + + // Shutdown server + // Give it a bit of delay to really make sure we're finished + SCHEDULED.schedule(() -> HytaleServer.get().shutdownServer(), 1, TimeUnit.SECONDS); + }; - // Shutdown server - Runnable shutdownServer = () -> HytaleServer.get().shutdownServer(); - HytaleServer.SCHEDULED_EXECUTOR.schedule(shutdownServer, 2, TimeUnit.SECONDS); + // Delay finish to allow waits in tests + SCHEDULED.schedule(finishingRunnable, 1, TimeUnit.SECONDS); } private void loadScripts(Path directory) { From 0518c30e7bfb518bdde42895476ea89c5f25b572 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Wed, 11 Feb 2026 20:38:10 -0800 Subject: [PATCH 21/23] Add test readme --- src/test/README.md | 64 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 src/test/README.md diff --git a/src/test/README.md b/src/test/README.md new file mode 100644 index 00000000..797783d8 --- /dev/null +++ b/src/test/README.md @@ -0,0 +1,64 @@ +# TESTS +> [!INFORMATION] +> This page is a work in progress. + +## Running: + +To run tests, there are a few steps: + +1) In the build.gradle.kts file, you will need to change this line: + `val assetLocation = "/Users/ShaneBee/Desktop/Server/Hytale/Assets/Assets.zip"` + This will need to be changed to the location of your Assets.zip file on your computer. +2) After that you can run tests with `./gradlew testRunner` + The server will start up, the tests will run and you will see the outcome. + +## Making Tests: + +### Location of Tests: + +Tests are in this package under `skript/tests`. +Please make sure to add your test in a matching package to their respective Java packages. + +### Naming Tests: + +The file should match the name of the class you are testing for. +Ex: `ExprSomeThing.java` -> `ExprSomeThing.sk` + +In the test file, the test name should match the class/file name. +Ex: + +```applescript +test "ExprSomeThing": + some test of some sort +``` + +### Available Syntaxes: + +#### Test Event: + +The test event is the top level structure used for your tests to run. +See above for naming conventions. +You can use commands/functions in your tests, but other events will not be called. + +#### Test Event Context Values: + +- `event-world` = The world the tests happen in (which is "default") + +#### Assert Effect: + +The assert effect is used to test conditions and make sure the test can pass. + +Pattern: `assert %=boolean% with %string%` +Example: `assert 1 = 1 with "1 should equal 1"` + +If the test fails, your message and the condition will be sent to the TestResults class, which the TestRunner will use +to decide the outcome of all tests. + +#### Others: + +For the most part, all other syntaxes will be available. +Some notes: + +- Do not use async effects/sections (They'll most likely cause issues) +- Use wait/delay effect with extreme caution + - Delays must be less than a second, or the server is going to stop before your test finishes From 8cf4e305e0c70779cd557f10f45bd751e5cdd1a3 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Wed, 11 Feb 2026 20:39:19 -0800 Subject: [PATCH 22/23] Add test readme - wrong wordr --- src/test/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/README.md b/src/test/README.md index 797783d8..cc64be41 100644 --- a/src/test/README.md +++ b/src/test/README.md @@ -1,5 +1,5 @@ # TESTS -> [!INFORMATION] +> [!IMPORTANT] > This page is a work in progress. ## Running: From d7e6da6808753f7c6b866314ac835a12b239accb Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Wed, 11 Feb 2026 20:53:01 -0800 Subject: [PATCH 23/23] contributing.md - add link to test readme --- .github/contributing.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/contributing.md b/.github/contributing.md index d18757a7..d30758ac 100644 --- a/.github/contributing.md +++ b/.github/contributing.md @@ -23,6 +23,8 @@ This way the team can discuss with you whether or not we want this in HySkript. - Use descriptive commit messages - Use descriptive PR titles - Ensure you follow the code style of this project +- Do make sure you add tests and run the test to make sure it works. + - See the [Testing Guide](https://github.com/SkriptDev/HySkript/tree/master/src/test/README.md) for more information. ### Don't: - Don't commit directly to `master` @@ -60,3 +62,6 @@ This way the team can discuss with you whether or not we want this in HySkript. - For expressions, please provide an example of using the getter as well as each changer you have applied. - For all others, please provide at least one example per pattern. - Please see other examples in HySkript for further inspiration. + +### Tests: +See the [Testing Guide](https://github.com/SkriptDev/HySkript/tree/master/src/test/README.md) for more information.