diff --git a/spine-c/.vscode/launch.json b/spine-c/.vscode/launch.json new file mode 100644 index 000000000..9c01e2444 --- /dev/null +++ b/spine-c/.vscode/launch.json @@ -0,0 +1,22 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "debug-printer (c)", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/build/debug-printer", + "args": [ + "${workspaceFolder}/../examples/spineboy/export/spineboy-pro.json", + "${workspaceFolder}/../examples/spineboy/export/spineboy-pma.atlas", + "run" + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}/build", + "environment": [], + "externalConsole": false, + "MIMode": "lldb", + "preLaunchTask": "CMake: build" + } + ] +} \ No newline at end of file diff --git a/spine-c/build.sh b/spine-c/build.sh index 548d03981..b03b82928 100755 --- a/spine-c/build.sh +++ b/spine-c/build.sh @@ -3,14 +3,12 @@ set -e cd "$(dirname "$0")" -for arg in "${@:-clean build}"; do - case $arg in - clean) rm -rf build ;; - build) - mkdir -p build && cd build - [ -f CMakeCache.txt ] || cmake .. -DCMAKE_BUILD_TYPE=Debug - make -j$(sysctl -n hw.ncpu 2>/dev/null || nproc 2>/dev/null || echo 4) - cd .. - ;; - esac -done \ No newline at end of file +# Clean only if explicitly requested +if [ "$1" = "clean" ]; then + rm -rf build +fi + +# Always build +mkdir -p build && cd build +[ -f CMakeCache.txt ] || cmake .. -DCMAKE_BUILD_TYPE=Debug +make -j$(sysctl -n hw.ncpu 2>/dev/null || nproc 2>/dev/null || echo 4) \ No newline at end of file diff --git a/spine-c/src/extensions.cpp b/spine-c/src/extensions.cpp index ce28e4f4a..34e1c818f 100644 --- a/spine-c/src/extensions.cpp +++ b/spine-c/src/extensions.cpp @@ -288,21 +288,22 @@ spine_skeleton_data_result spine_skeleton_data_load_json(spine_atlas atlas, cons } // Set name from path if provided - if (path != nullptr) { + if (path != nullptr && data != nullptr) { + String pathStr(path); + // Extract filename without extension from path - const char *lastSlash = strrchr(path, '/'); - const char *lastBackslash = strrchr(path, '\\'); - const char *start = path; + int lastSlash = pathStr.lastIndexOf('/'); + int lastBackslash = pathStr.lastIndexOf('\\'); + int start = 0; - if (lastSlash != nullptr) start = lastSlash + 1; - if (lastBackslash != nullptr && lastBackslash > start) start = lastBackslash + 1; + if (lastSlash != -1) start = lastSlash + 1; + if (lastBackslash != -1 && lastBackslash > start) start = lastBackslash + 1; - const char *lastDot = strrchr(start, '.'); - if (lastDot != nullptr) { - int length = lastDot - start; - data->setName(String(start, length)); + int lastDot = pathStr.lastIndexOf('.'); + if (lastDot != -1 && lastDot > start) { + data->setName(pathStr.substring(start, lastDot - start)); } else { - data->setName(String(start)); + data->setName(pathStr.substring(start)); } } @@ -323,21 +324,22 @@ spine_skeleton_data_result spine_skeleton_data_load_binary(spine_atlas atlas, co } // Set name from path if provided - if (path != nullptr) { + if (path != nullptr && data != nullptr) { + String pathStr(path); + // Extract filename without extension from path - const char *lastSlash = strrchr(path, '/'); - const char *lastBackslash = strrchr(path, '\\'); - const char *start = path; + int lastSlash = pathStr.lastIndexOf('/'); + int lastBackslash = pathStr.lastIndexOf('\\'); + int start = 0; - if (lastSlash != nullptr) start = lastSlash + 1; - if (lastBackslash != nullptr && lastBackslash > start) start = lastBackslash + 1; + if (lastSlash != -1) start = lastSlash + 1; + if (lastBackslash != -1 && lastBackslash > start) start = lastBackslash + 1; - const char *lastDot = strrchr(start, '.'); - if (lastDot != nullptr) { - int length = lastDot - start; - data->setName(String(start, length)); + int lastDot = pathStr.lastIndexOf('.'); + if (lastDot != -1 && lastDot > start) { + data->setName(pathStr.substring(start, lastDot - start)); } else { - data->setName(String(start)); + data->setName(pathStr.substring(start)); } } diff --git a/spine-c/tests/debug-printer.c b/spine-c/tests/debug-printer.c index e32ea3d5e..3448ba6f2 100644 --- a/spine-c/tests/debug-printer.c +++ b/spine-c/tests/debug-printer.c @@ -128,14 +128,16 @@ uint8_t *read_file(const char *path, int *length) { } int main(int argc, char *argv[]) { - if (argc < 4) { - fprintf(stderr, "Usage: DebugPrinter \n"); + if (argc < 3) { + fprintf(stderr, "Usage: DebugPrinter [animation-name]\n"); return 1; } + spine_bone_set_y_down(false); + const char *skeletonPath = argv[1]; const char *atlasPath = argv[2]; - const char *animationName = argv[3]; + const char *animationName = argc >= 4 ? argv[3] : NULL; // Read atlas file int atlasLength = 0; @@ -210,24 +212,27 @@ int main(int argc, char *argv[]) { spine_animation_state_data stateData = spine_animation_state_data_create(skeletonData); spine_animation_state state = spine_animation_state_create(stateData); - // Find and set animation - spine_animation animation = spine_skeleton_data_find_animation(skeletonData, animationName); - if (!animation) { - fprintf(stderr, "Animation not found: %s\n", animationName); - spine_animation_state_dispose(state); - spine_animation_state_data_dispose(stateData); - spine_skeleton_dispose(skeleton); - spine_skeleton_data_result_dispose(result); - spine_atlas_dispose(atlas); - return 1; + spine_skeleton_setup_pose(skeleton); + + // Set animation or setup pose + if (animationName != NULL) { + // Find and set animation + spine_animation animation = spine_skeleton_data_find_animation(skeletonData, animationName); + if (!animation) { + fprintf(stderr, "Animation not found: %s\n", animationName); + spine_animation_state_dispose(state); + spine_animation_state_data_dispose(stateData); + spine_skeleton_dispose(skeleton); + spine_skeleton_data_result_dispose(result); + spine_atlas_dispose(atlas); + return 1; + } + spine_animation_state_set_animation_1(state, 0, animationName, 1); + // Update and apply + spine_animation_state_update(state, 0.016f); + spine_animation_state_apply(state, skeleton); } - spine_animation_state_set_animation_1(state, 0, animationName, 1); - - // Update and apply - spine_animation_state_update(state, 0.016f); - spine_animation_state_apply(state, skeleton); - spine_skeleton_update(skeleton, 0.016f); spine_skeleton_update_world_transform_1(skeleton, SPINE_PHYSICS_UPDATE); // Print skeleton state diff --git a/spine-cpp/.vscode/launch.json b/spine-cpp/.vscode/launch.json new file mode 100644 index 000000000..322287f0c --- /dev/null +++ b/spine-cpp/.vscode/launch.json @@ -0,0 +1,22 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "debug-printer (cpp)", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/build/debug-printer", + "args": [ + "${workspaceFolder}/../examples/spineboy/export/spineboy-pro.json", + "${workspaceFolder}/../examples/spineboy/export/spineboy-pma.atlas", + "run" + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}/build", + "environment": [], + "externalConsole": false, + "MIMode": "lldb", + "preLaunchTask": "CMake: build" + } + ] +} \ No newline at end of file diff --git a/spine-cpp/CMakeLists.txt b/spine-cpp/CMakeLists.txt index 671e9f885..5a055e652 100644 --- a/spine-cpp/CMakeLists.txt +++ b/spine-cpp/CMakeLists.txt @@ -22,5 +22,6 @@ export( # Optional tests if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) - add_subdirectory(tests) + add_executable(debug-printer ${CMAKE_CURRENT_SOURCE_DIR}/tests/DebugPrinter.cpp) + target_link_libraries(debug-printer spine-cpp) endif() \ No newline at end of file diff --git a/spine-cpp/build.sh b/spine-cpp/build.sh index 548d03981..b03b82928 100755 --- a/spine-cpp/build.sh +++ b/spine-cpp/build.sh @@ -3,14 +3,12 @@ set -e cd "$(dirname "$0")" -for arg in "${@:-clean build}"; do - case $arg in - clean) rm -rf build ;; - build) - mkdir -p build && cd build - [ -f CMakeCache.txt ] || cmake .. -DCMAKE_BUILD_TYPE=Debug - make -j$(sysctl -n hw.ncpu 2>/dev/null || nproc 2>/dev/null || echo 4) - cd .. - ;; - esac -done \ No newline at end of file +# Clean only if explicitly requested +if [ "$1" = "clean" ]; then + rm -rf build +fi + +# Always build +mkdir -p build && cd build +[ -f CMakeCache.txt ] || cmake .. -DCMAKE_BUILD_TYPE=Debug +make -j$(sysctl -n hw.ncpu 2>/dev/null || nproc 2>/dev/null || echo 4) \ No newline at end of file diff --git a/spine-cpp/tests/CMakeLists.txt b/spine-cpp/tests/CMakeLists.txt deleted file mode 100644 index bdd96009a..000000000 --- a/spine-cpp/tests/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -# Create DebugPrinter executable -add_executable(DebugPrinter ${CMAKE_CURRENT_SOURCE_DIR}/src/DebugPrinter.cpp) -target_link_libraries(DebugPrinter spine-cpp) -target_compile_features(DebugPrinter PRIVATE cxx_std_11) \ No newline at end of file diff --git a/spine-cpp/tests/src/DebugPrinter.cpp b/spine-cpp/tests/DebugPrinter.cpp similarity index 90% rename from spine-cpp/tests/src/DebugPrinter.cpp rename to spine-cpp/tests/DebugPrinter.cpp index 7f058ca4a..fa6831de1 100644 --- a/spine-cpp/tests/src/DebugPrinter.cpp +++ b/spine-cpp/tests/DebugPrinter.cpp @@ -129,14 +129,16 @@ public: }; int main(int argc, char *argv[]) { - if (argc < 4) { - fprintf(stderr, "Usage: DebugPrinter \n"); + if (argc < 3) { + fprintf(stderr, "Usage: DebugPrinter [animation-name]\n"); return 1; } + Bone::setYDown(false); + const char *skeletonPath = argv[1]; const char *atlasPath = argv[2]; - const char *animationName = argv[3]; + const char *animationName = argc >= 4 ? argv[3] : nullptr; // Load atlas with headless texture loader HeadlessTextureLoader textureLoader; @@ -171,21 +173,24 @@ int main(int argc, char *argv[]) { AnimationStateData stateData(skeletonData); AnimationState state(&stateData); - // Find and set animation - Animation *animation = skeletonData->findAnimation(animationName); - if (!animation) { - fprintf(stderr, "Animation not found: %s\n", animationName); - delete skeletonData; - delete atlas; - return 1; + skeleton.setupPose(); + + // Set animation or setup pose + if (animationName != nullptr) { + // Find and set animation + Animation *animation = skeletonData->findAnimation(animationName); + if (!animation) { + fprintf(stderr, "Animation not found: %s\n", animationName); + delete skeletonData; + delete atlas; + return 1; + } + state.setAnimation(0, animation, true); + // Update and apply + state.update(0.016f); + state.apply(skeleton); } - state.setAnimation(0, animation, true); - - // Update and apply - state.update(0.016f); - state.apply(skeleton); - skeleton.update(0.016f); skeleton.updateWorldTransform(Physics_Update); // Print skeleton state diff --git a/spine-libgdx/.vscode/launch.json b/spine-libgdx/.vscode/launch.json new file mode 100644 index 000000000..9613f734d --- /dev/null +++ b/spine-libgdx/.vscode/launch.json @@ -0,0 +1,17 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "java", + "name": "debug-printer (java)", + "request": "launch", + "mainClass": "com.esotericsoftware.spine.DebugPrinter", + "projectName": "spine-libgdx-tests", + "args": [ + "${workspaceFolder}/../examples/spineboy/export/spineboy-pro.json", + "${workspaceFolder}/../examples/spineboy/export/spineboy-pma.atlas", + "run" + ], + } + ] +} \ No newline at end of file diff --git a/spine-libgdx/spine-libgdx-tests/.vscode/launch.json b/spine-libgdx/spine-libgdx-tests/.vscode/launch.json new file mode 100644 index 000000000..394d6c2e4 --- /dev/null +++ b/spine-libgdx/spine-libgdx-tests/.vscode/launch.json @@ -0,0 +1,16 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "java", + "name": "DebugPrinter", + "request": "launch", + "mainClass": "com.esotericsoftware.spine.DebugPrinter", + "args": [ + "../../spine-godot/example-v4-extension/assets/spineboy/spineboy-pro.spine-json", + "../../spine-godot/example-v4-extension/assets/spineboy/spineboy.atlas", + "idle" + ] + } + ] +} \ No newline at end of file diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/DebugPrinter.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/DebugPrinter.java index abde2e401..ec2b55989 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/DebugPrinter.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/DebugPrinter.java @@ -222,18 +222,22 @@ public class DebugPrinter implements ApplicationListener { AnimationStateData stateData = new AnimationStateData(skeletonData); AnimationState state = new AnimationState(stateData); - // Find and set animation - Animation animation = skeletonData.findAnimation(animationName); - if (animation == null) { - System.err.println("Animation not found: " + animationName); - System.exit(1); + skeleton.setupPose(); + + // Set animation or setup pose + if (animationName != null) { + // Find and set animation + Animation animation = skeletonData.findAnimation(animationName); + if (animation == null) { + System.err.println("Animation not found: " + animationName); + System.exit(1); + } + state.setAnimation(0, animation, true); + // Update and apply + state.update(0.016f); + state.apply(skeleton); } - state.setAnimation(0, animation, true); - - // Update and apply - state.update(0.016f); - state.apply(skeleton); skeleton.update(0.016f); skeleton.updateWorldTransform(Physics.update); @@ -271,13 +275,14 @@ public class DebugPrinter implements ApplicationListener { } public static void main (String[] args) { - if (args.length < 3) { - System.err.println("Usage: DebugPrinter "); + if (args.length < 2) { + System.err.println("Usage: DebugPrinter [animation-name]"); System.exit(1); } HeadlessApplicationConfiguration config = new HeadlessApplicationConfiguration(); config.updatesPerSecond = 60; - new HeadlessApplication(new DebugPrinter(args[0], args[1], args[2]), config); + String animationName = args.length >= 3 ? args[2] : null; + new HeadlessApplication(new DebugPrinter(args[0], args[1], animationName), config); } } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SliderData.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SliderData.java index ff3f76a4f..01b464e32 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SliderData.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SliderData.java @@ -33,7 +33,7 @@ import com.badlogic.gdx.utils.Null; import com.esotericsoftware.spine.TransformConstraintData.FromProperty; -/** Stores the setup pose for a {@link Slider}. */ +/** Stores the setup pose for a {@link Slider}. */ public class SliderData extends ConstraintData { Animation animation; boolean additive, loop; diff --git a/spine-ts/spine-core/tests/DebugPrinter.ts b/spine-ts/spine-core/tests/DebugPrinter.ts new file mode 100755 index 000000000..c27fee2ff --- /dev/null +++ b/spine-ts/spine-core/tests/DebugPrinter.ts @@ -0,0 +1,210 @@ +#!/usr/bin/env npx tsx + +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated April 5, 2025. Replaces all prior versions. + * + * Copyright (c) 2013-2025, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, + * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +import { promises as fs } from 'fs'; +import * as path from 'path'; +import { + AnimationState, + AnimationStateData, + AtlasAttachmentLoader, + Physics, + Skeleton, + SkeletonBinary, + SkeletonData, + SkeletonJson, + TextureAtlas +} from '../src/index.js'; + +// Printer class for hierarchical output +class Printer { + private indentLevel = 0; + private readonly INDENT = " "; + + print(text: string): void { + const indent = this.INDENT.repeat(this.indentLevel); + console.log(indent + text); + } + + indent(): void { + this.indentLevel++; + } + + unindent(): void { + this.indentLevel--; + } + + printSkeletonData(data: SkeletonData): void { + this.print("SkeletonData {"); + this.indent(); + + this.print(`name: "${data.name || ""}"`); + this.print(`version: ${data.version ? `"${data.version}"` : "null"}`); + this.print(`hash: ${data.hash ? `"${data.hash}"` : "null"}`); + this.print(`x: ${this.formatFloat(data.x)}`); + this.print(`y: ${this.formatFloat(data.y)}`); + this.print(`width: ${this.formatFloat(data.width)}`); + this.print(`height: ${this.formatFloat(data.height)}`); + this.print(`referenceScale: ${this.formatFloat(data.referenceScale)}`); + this.print(`fps: ${this.formatFloat(data.fps || 0)}`); + this.print(`imagesPath: ${data.imagesPath ? `"${data.imagesPath}"` : "null"}`); + this.print(`audioPath: ${data.audioPath ? `"${data.audioPath}"` : "null"}`); + + // TODO: Add bones, slots, skins, animations, etc. in future expansion + + this.unindent(); + this.print("}"); + } + + printSkeleton(skeleton: Skeleton): void { + this.print("Skeleton {"); + this.indent(); + + this.print(`x: ${this.formatFloat(skeleton.x)}`); + this.print(`y: ${this.formatFloat(skeleton.y)}`); + this.print(`scaleX: ${this.formatFloat(skeleton.scaleX)}`); + this.print(`scaleY: ${this.formatFloat(skeleton.scaleY)}`); + this.print(`time: ${this.formatFloat(skeleton.time)}`); + + // TODO: Add runtime state (bones, slots, etc.) in future expansion + + this.unindent(); + this.print("}"); + } + + private formatFloat(value: number): string { + // Format to 6 decimal places, matching Java/C++ output + return value.toFixed(6).replace(',', '.'); + } +} + +// Main DebugPrinter class +class DebugPrinter { + static async main(args: string[]): Promise { + if (args.length < 2) { + console.error("Usage: DebugPrinter [animation-name]"); + process.exit(1); + } + + const skeletonPath = args[0]; + const atlasPath = args[1]; + const animationName = args.length >= 3 ? args[2] : null; + + try { + // Load atlas + const atlasData = await fs.readFile(atlasPath, 'utf8'); + const atlasDir = path.dirname(atlasPath); + const atlas = new TextureAtlas(atlasData); + + // Load skeleton data + const skeletonData = await this.loadSkeletonData(skeletonPath, atlas); + + // Print skeleton data + const printer = new Printer(); + console.log("=== SKELETON DATA ==="); + printer.printSkeletonData(skeletonData); + + // Create skeleton and animation state + const skeleton = new Skeleton(skeletonData); + const stateData = new AnimationStateData(skeletonData); + const state = new AnimationState(stateData); + + skeleton.setupPose(); + + // Set animation or setup pose + if (animationName) { + // Find and set animation + const animation = skeletonData.findAnimation(animationName); + if (!animation) { + console.error(`Animation not found: ${animationName}`); + process.exit(1); + } + state.setAnimation(0, animationName, true); + // Update and apply + state.update(0.016); + state.apply(skeleton); + } + + skeleton.updateWorldTransform(Physics.update); + + // Print skeleton state + console.log("\n=== SKELETON STATE ==="); + printer.printSkeleton(skeleton); + + } catch (error) { + console.error("Error:", error); + process.exit(1); + } + } + + private static async loadSkeletonData(skeletonPath: string, atlas: TextureAtlas): Promise { + const attachmentLoader = new AtlasAttachmentLoader(atlas); + const ext = path.extname(skeletonPath).toLowerCase(); + + if (ext === '.json') { + const jsonData = await fs.readFile(skeletonPath, 'utf8'); + const json = new SkeletonJson(attachmentLoader); + json.scale = 1; + const skeletonData = json.readSkeletonData(jsonData); + + // Set name from filename if not already set + if (!skeletonData.name) { + const basename = path.basename(skeletonPath); + const nameWithoutExt = basename.substring(0, basename.lastIndexOf('.')) || basename; + skeletonData.name = nameWithoutExt; + } + + return skeletonData; + } else if (ext === '.skel') { + const binaryData = await fs.readFile(skeletonPath); + const binary = new SkeletonBinary(attachmentLoader); + binary.scale = 1; + const skeletonData = binary.readSkeletonData(new Uint8Array(binaryData)); + + // Set name from filename if not already set + if (!skeletonData.name) { + const basename = path.basename(skeletonPath); + const nameWithoutExt = basename.substring(0, basename.lastIndexOf('.')) || basename; + skeletonData.name = nameWithoutExt; + } + + return skeletonData; + } else { + throw new Error(`Unsupported skeleton file format: ${ext}`); + } + } +} + +// Run if called directly +if (import.meta.url === `file://${process.argv[1]}`) { + DebugPrinter.main(process.argv.slice(2)); +} + +export default DebugPrinter; \ No newline at end of file diff --git a/spine-ts/spine-core/tsconfig.json b/spine-ts/spine-core/tsconfig.json index 994137c8c..b8946746e 100644 --- a/spine-ts/spine-core/tsconfig.json +++ b/spine-ts/spine-core/tsconfig.json @@ -9,7 +9,8 @@ "**/*.ts" ], "exclude": [ - "dist/**/*.d.ts" + "dist/**/*.d.ts", + "tests/**/*.ts" ], "references": [] } \ No newline at end of file diff --git a/tests/compare-with-reference-impl.ts b/tests/compare-with-reference-impl.ts new file mode 100755 index 000000000..6192b9132 --- /dev/null +++ b/tests/compare-with-reference-impl.ts @@ -0,0 +1,232 @@ +#!/usr/bin/env npx tsx + +import { execSync } from 'child_process'; +import * as fs from 'fs'; +import * as path from 'path'; +import { promisify } from 'util'; + +const writeFile = promisify(fs.writeFile); +const mkdir = promisify(fs.mkdir); + +// Parse command line arguments +const args = process.argv.slice(2); +if (args.length < 2) { + console.error('Usage: compare-with-reference-impl.ts [animation-name]'); + process.exit(1); +} + +const [skeletonPath, atlasPath, animationName] = args; + +// Get absolute paths +const absoluteSkeletonPath = path.resolve(skeletonPath); +const absoluteAtlasPath = path.resolve(atlasPath); + +// Script paths +const scriptDir = path.dirname(new URL(import.meta.url).pathname); +const rootDir = path.dirname(scriptDir); +const outputDir = path.join(scriptDir, 'output'); + +interface RuntimeConfig { + name: string; + buildCheck: () => boolean; + build: () => void; + run: () => string; +} + +// Runtime configurations +const runtimes: RuntimeConfig[] = [ + { + name: 'java', + buildCheck: () => { + const classPath = path.join(rootDir, 'spine-libgdx/spine-libgdx-tests/build/classes/java/main/com/esotericsoftware/spine/DebugPrinter.class'); + return fs.existsSync(classPath); + }, + build: () => { + console.log(' Building Java runtime...'); + execSync('./gradlew :spine-libgdx-tests:build', { + cwd: path.join(rootDir, 'spine-libgdx'), + stdio: 'inherit' + }); + }, + run: () => { + const args = animationName + ? `${absoluteSkeletonPath} ${absoluteAtlasPath} ${animationName}` + : `${absoluteSkeletonPath} ${absoluteAtlasPath}`; + const output = execSync( + `./gradlew -q :spine-libgdx-tests:runDebugPrinter -Pargs="${args}"`, + { + cwd: path.join(rootDir, 'spine-libgdx'), + encoding: 'utf8' + } + ); + // Find the start of actual output and return everything from there + const lines = output.split('\n'); + const startIndex = lines.findIndex(line => line === '=== SKELETON DATA ==='); + if (startIndex !== -1) { + return lines.slice(startIndex).join('\n').trim(); + } + // Fallback to full output if marker not found + return output.trim(); + } + }, + { + name: 'cpp', + buildCheck: () => { + const execPath = path.join(rootDir, 'spine-cpp/build/debug-printer'); + return fs.existsSync(execPath); + }, + build: () => { + console.log(' Building C++ runtime...'); + execSync('./build.sh clean', { + cwd: path.join(rootDir, 'spine-cpp/'), + stdio: 'inherit' + }); + }, + run: () => { + return execSync( + `./build/debug-printer "${absoluteSkeletonPath}" "${absoluteAtlasPath}" "${animationName}"`, + { + cwd: path.join(rootDir, 'spine-cpp'), + encoding: 'utf8' + } + ).trim(); + } + }, + { + name: 'c', + buildCheck: () => { + const execPath = path.join(rootDir, 'spine-c/build/debug-printer'); + return fs.existsSync(execPath); + }, + build: () => { + console.log(' Building C runtime...'); + execSync('./build.sh', { + cwd: path.join(rootDir, 'spine-c/'), + stdio: 'inherit' + }); + }, + run: () => { + return execSync( + `./build/debug-printer "${absoluteSkeletonPath}" "${absoluteAtlasPath}" "${animationName}"`, + { + cwd: path.join(rootDir, 'spine-c'), + encoding: 'utf8' + } + ).trim(); + } + }, + { + name: 'ts', + buildCheck: () => true, // No build needed + build: () => {}, // No build needed + run: () => { + return execSync( + `npx tsx tests/DebugPrinter.ts "${absoluteSkeletonPath}" "${absoluteAtlasPath}" "${animationName}"`, + { + cwd: path.join(rootDir, 'spine-ts/spine-core'), + encoding: 'utf8' + } + ).trim(); + } + } +]; + +async function main() { + // Ensure output directory exists + await mkdir(outputDir, { recursive: true }); + + console.log('Comparing DebugPrinter outputs for:'); + console.log(` Skeleton: ${absoluteSkeletonPath}`); + console.log(` Atlas: ${absoluteAtlasPath}`); + console.log(` Animation: ${animationName}`); + console.log(''); + + // Run all runtimes and collect outputs + const outputs: Record = {}; + + for (const runtime of runtimes) { + console.log(`Running ${runtime.name.toUpperCase()} DebugPrinter...`); + + try { + // Build if needed + if (!runtime.buildCheck()) { + runtime.build(); + } + + // Run and capture output + const output = runtime.run(); + outputs[runtime.name] = output; + + // Save output to file + await writeFile(path.join(outputDir, `${runtime.name}.txt`), output); + + console.log(' Done.'); + } catch (error) { + console.error(` Error: ${error instanceof Error ? error.message : JSON.stringify(error)}`); + outputs[runtime.name] = `Error: ${error instanceof Error ? error.message : JSON.stringify(error)}`; + } + } + + console.log(''); + console.log('Comparing outputs...'); + console.log(''); + + // Compare outputs + const reference = 'java'; + let allMatch = true; + + for (const runtime of runtimes) { + if (runtime.name !== reference) { + process.stdout.write(`Comparing ${reference} vs ${runtime.name}: `); + + if (outputs[reference] === outputs[runtime.name]) { + console.log('✓ MATCH'); + } else { + console.log('✗ DIFFER'); + allMatch = false; + + // Show first few differences + const refLines = outputs[reference].split('\n'); + const runtimeLines = outputs[runtime.name].split('\n'); + const maxLines = Math.max(refLines.length, runtimeLines.length); + let diffCount = 0; + + console.log(' First differences:'); + for (let i = 0; i < maxLines && diffCount < 5; i++) { + if (refLines[i] !== runtimeLines[i]) { + diffCount++; + console.log(` Line ${i + 1}:`); + if (refLines[i] !== undefined) { + console.log(` - ${refLines[i]}`); + } + if (runtimeLines[i] !== undefined) { + console.log(` + ${runtimeLines[i]}`); + } + } + } + + if (diffCount === 0 && refLines.length !== runtimeLines.length) { + console.log(` Different number of lines: ${refLines.length} vs ${runtimeLines.length}`); + } + + // Save outputs for manual diff + console.log(` Full outputs saved to: ${outputDir}/`); + } + } + } + + console.log(''); + if (allMatch) { + console.log('✓ All outputs match!'); + process.exit(0); + } else { + console.log(`✗ Outputs differ. Check the output files in ${outputDir}/`); + process.exit(1); + } +} + +// Run main function +main().catch(error => { + console.error('Error:', error); + process.exit(1); +}); \ No newline at end of file diff --git a/tests/package-lock.json b/tests/package-lock.json new file mode 100644 index 000000000..289e80c7e --- /dev/null +++ b/tests/package-lock.json @@ -0,0 +1,573 @@ +{ + "name": "spine-tests", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "spine-tests", + "devDependencies": { + "@types/node": "^20.0.0", + "tsx": "^4.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.6.tgz", + "integrity": "sha512-ShbM/3XxwuxjFiuVBHA+d3j5dyac0aEVVq1oluIDf71hUw0aRF59dV/efUsIwFnR6m8JNM2FjZOzmaZ8yG61kw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.6.tgz", + "integrity": "sha512-S8ToEOVfg++AU/bHwdksHNnyLyVM+eMVAOf6yRKFitnwnbwwPNqKr3srzFRe7nzV69RQKb5DgchIX5pt3L53xg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.6.tgz", + "integrity": "sha512-hd5zdUarsK6strW+3Wxi5qWws+rJhCCbMiC9QZyzoxfk5uHRIE8T287giQxzVpEvCwuJ9Qjg6bEjcRJcgfLqoA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.6.tgz", + "integrity": "sha512-0Z7KpHSr3VBIO9A/1wcT3NTy7EB4oNC4upJ5ye3R7taCc2GUdeynSLArnon5G8scPwaU866d3H4BCrE5xLW25A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.6.tgz", + "integrity": "sha512-FFCssz3XBavjxcFxKsGy2DYK5VSvJqa6y5HXljKzhRZ87LvEi13brPrf/wdyl/BbpbMKJNOr1Sd0jtW4Ge1pAA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.6.tgz", + "integrity": "sha512-GfXs5kry/TkGM2vKqK2oyiLFygJRqKVhawu3+DOCk7OxLy/6jYkWXhlHwOoTb0WqGnWGAS7sooxbZowy+pK9Yg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.6.tgz", + "integrity": "sha512-aoLF2c3OvDn2XDTRvn8hN6DRzVVpDlj2B/F66clWd/FHLiHaG3aVZjxQX2DYphA5y/evbdGvC6Us13tvyt4pWg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.6.tgz", + "integrity": "sha512-2SkqTjTSo2dYi/jzFbU9Plt1vk0+nNg8YC8rOXXea+iA3hfNJWebKYPs3xnOUf9+ZWhKAaxnQNUf2X9LOpeiMQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.6.tgz", + "integrity": "sha512-SZHQlzvqv4Du5PrKE2faN0qlbsaW/3QQfUUc6yO2EjFcA83xnwm91UbEEVx4ApZ9Z5oG8Bxz4qPE+HFwtVcfyw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.6.tgz", + "integrity": "sha512-b967hU0gqKd9Drsh/UuAm21Khpoh6mPBSgz8mKRq4P5mVK8bpA+hQzmm/ZwGVULSNBzKdZPQBRT3+WuVavcWsQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.6.tgz", + "integrity": "sha512-aHWdQ2AAltRkLPOsKdi3xv0mZ8fUGPdlKEjIEhxCPm5yKEThcUjHpWB1idN74lfXGnZ5SULQSgtr5Qos5B0bPw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.6.tgz", + "integrity": "sha512-VgKCsHdXRSQ7E1+QXGdRPlQ/e08bN6WMQb27/TMfV+vPjjTImuT9PmLXupRlC90S1JeNNW5lzkAEO/McKeJ2yg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.6.tgz", + "integrity": "sha512-WViNlpivRKT9/py3kCmkHnn44GkGXVdXfdc4drNmRl15zVQ2+D2uFwdlGh6IuK5AAnGTo2qPB1Djppj+t78rzw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.6.tgz", + "integrity": "sha512-wyYKZ9NTdmAMb5730I38lBqVu6cKl4ZfYXIs31Baf8aoOtB4xSGi3THmDYt4BTFHk7/EcVixkOV2uZfwU3Q2Jw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.6.tgz", + "integrity": "sha512-KZh7bAGGcrinEj4qzilJ4hqTY3Dg2U82c8bv+e1xqNqZCrCyc+TL9AUEn5WGKDzm3CfC5RODE/qc96OcbIe33w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.6.tgz", + "integrity": "sha512-9N1LsTwAuE9oj6lHMyyAM+ucxGiVnEqUdp4v7IaMmrwb06ZTEVCIs3oPPplVsnjPfyjmxwHxHMF8b6vzUVAUGw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.6.tgz", + "integrity": "sha512-A6bJB41b4lKFWRKNrWoP2LHsjVzNiaurf7wyj/XtFNTsnPuxwEBWHLty+ZE0dWBKuSK1fvKgrKaNjBS7qbFKig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.6.tgz", + "integrity": "sha512-IjA+DcwoVpjEvyxZddDqBY+uJ2Snc6duLpjmkXm/v4xuS3H+3FkLZlDm9ZsAbF9rsfP3zeA0/ArNDORZgrxR/Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.6.tgz", + "integrity": "sha512-dUXuZr5WenIDlMHdMkvDc1FAu4xdWixTCRgP7RQLBOkkGgwuuzaGSYcOpW4jFxzpzL1ejb8yF620UxAqnBrR9g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.6.tgz", + "integrity": "sha512-l8ZCvXP0tbTJ3iaqdNf3pjaOSd5ex/e6/omLIQCVBLmHTlfXW3zAxQ4fnDmPLOB1x9xrcSi/xtCWFwCZRIaEwg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.6.tgz", + "integrity": "sha512-hKrmDa0aOFOr71KQ/19JC7az1P0GWtCN1t2ahYAf4O007DHZt/dW8ym5+CUdJhQ/qkZmI1HAF8KkJbEFtCL7gw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.6.tgz", + "integrity": "sha512-+SqBcAWoB1fYKmpWoQP4pGtx+pUUC//RNYhFdbcSA16617cchuryuhOCRpPsjCblKukAckWsV+aQ3UKT/RMPcA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.6.tgz", + "integrity": "sha512-dyCGxv1/Br7MiSC42qinGL8KkG4kX0pEsdb0+TKhmJZgCUDBGmyo1/ArCjNGiOLiIAgdbWgmWgib4HoCi5t7kA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.6.tgz", + "integrity": "sha512-42QOgcZeZOvXfsCBJF5Afw73t4veOId//XD3i+/9gSkhSV6Gk3VPlWncctI+JcOyERv85FUo7RxuxGy+z8A43Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.6.tgz", + "integrity": "sha512-4AWhgXmDuYN7rJI6ORB+uU9DHLq/erBbuMoAuB4VWJTu5KtCgcKYPynF0YI1VkBNuEfjNlLrFr9KZPJzrtLkrQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.6.tgz", + "integrity": "sha512-NgJPHHbEpLQgDH2MjQu90pzW/5vvXIZ7KOnPyNBm92A6WgZ/7b6fJyUBjoumLqeOQQGqY2QjQxRo97ah4Sj0cA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@types/node": { + "version": "20.19.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.7.tgz", + "integrity": "sha512-1GM9z6BJOv86qkPvzh2i6VW5+VVrXxCLknfmTkWEqz+6DqosiY28XUWCTmBcJ0ACzKqx/iwdIREfo1fwExIlkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/esbuild": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.6.tgz", + "integrity": "sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.6", + "@esbuild/android-arm": "0.25.6", + "@esbuild/android-arm64": "0.25.6", + "@esbuild/android-x64": "0.25.6", + "@esbuild/darwin-arm64": "0.25.6", + "@esbuild/darwin-x64": "0.25.6", + "@esbuild/freebsd-arm64": "0.25.6", + "@esbuild/freebsd-x64": "0.25.6", + "@esbuild/linux-arm": "0.25.6", + "@esbuild/linux-arm64": "0.25.6", + "@esbuild/linux-ia32": "0.25.6", + "@esbuild/linux-loong64": "0.25.6", + "@esbuild/linux-mips64el": "0.25.6", + "@esbuild/linux-ppc64": "0.25.6", + "@esbuild/linux-riscv64": "0.25.6", + "@esbuild/linux-s390x": "0.25.6", + "@esbuild/linux-x64": "0.25.6", + "@esbuild/netbsd-arm64": "0.25.6", + "@esbuild/netbsd-x64": "0.25.6", + "@esbuild/openbsd-arm64": "0.25.6", + "@esbuild/openbsd-x64": "0.25.6", + "@esbuild/openharmony-arm64": "0.25.6", + "@esbuild/sunos-x64": "0.25.6", + "@esbuild/win32-arm64": "0.25.6", + "@esbuild/win32-ia32": "0.25.6", + "@esbuild/win32-x64": "0.25.6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-tsconfig": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", + "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/tsx": { + "version": "4.20.3", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.3.tgz", + "integrity": "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.25.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/tests/package.json b/tests/package.json new file mode 100644 index 000000000..440cf9f37 --- /dev/null +++ b/tests/package.json @@ -0,0 +1,12 @@ +{ + "name": "spine-tests", + "type": "module", + "private": true, + "scripts": { + "compare": "tsx compare-with-reference-impl.ts" + }, + "devDependencies": { + "@types/node": "^20.0.0", + "tsx": "^4.0.0" + } +} \ No newline at end of file diff --git a/tests/tsconfig.json b/tests/tsconfig.json new file mode 100644 index 000000000..ee3d819ff --- /dev/null +++ b/tests/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "node", + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "types": ["node"] + }, + "include": [ + "*.ts" + ] +} \ No newline at end of file