diff --git a/spine-c/.vscode/launch.json b/spine-c/.vscode/launch.json index 9c01e2444..321b44654 100644 --- a/spine-c/.vscode/launch.json +++ b/spine-c/.vscode/launch.json @@ -2,10 +2,10 @@ "version": "0.2.0", "configurations": [ { - "name": "debug-printer (c)", + "name": "headless test (c)", "type": "cppdbg", "request": "launch", - "program": "${workspaceFolder}/build/debug-printer", + "program": "${workspaceFolder}/build/headless-test", "args": [ "${workspaceFolder}/../examples/spineboy/export/spineboy-pro.json", "${workspaceFolder}/../examples/spineboy/export/spineboy-pma.atlas", diff --git a/spine-c/CMakeLists.txt b/spine-c/CMakeLists.txt index 7665929c8..892c73f59 100644 --- a/spine-c/CMakeLists.txt +++ b/spine-c/CMakeLists.txt @@ -39,6 +39,6 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Create test executable only if this is the top-level project if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) - add_executable(debug-printer ${CMAKE_CURRENT_SOURCE_DIR}/tests/debug-printer.c) - target_link_libraries(debug-printer spine-c) + add_executable(headless-test ${CMAKE_CURRENT_SOURCE_DIR}/tests/headless-test.c) + target_link_libraries(headless-test spine-c) endif() diff --git a/spine-c/tests/debug-printer.c b/spine-c/tests/headless-test.c similarity index 100% rename from spine-c/tests/debug-printer.c rename to spine-c/tests/headless-test.c diff --git a/spine-cpp/.vscode/launch.json b/spine-cpp/.vscode/launch.json index 322287f0c..5bcbea24f 100644 --- a/spine-cpp/.vscode/launch.json +++ b/spine-cpp/.vscode/launch.json @@ -2,10 +2,10 @@ "version": "0.2.0", "configurations": [ { - "name": "debug-printer (cpp)", + "name": "headless test (cpp)", "type": "cppdbg", "request": "launch", - "program": "${workspaceFolder}/build/debug-printer", + "program": "${workspaceFolder}/build/headless-test", "args": [ "${workspaceFolder}/../examples/spineboy/export/spineboy-pro.json", "${workspaceFolder}/../examples/spineboy/export/spineboy-pma.atlas", diff --git a/spine-cpp/CMakeLists.txt b/spine-cpp/CMakeLists.txt index 5a055e652..8c8d0d74d 100644 --- a/spine-cpp/CMakeLists.txt +++ b/spine-cpp/CMakeLists.txt @@ -22,6 +22,6 @@ export( # Optional tests if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) - add_executable(debug-printer ${CMAKE_CURRENT_SOURCE_DIR}/tests/DebugPrinter.cpp) - target_link_libraries(debug-printer spine-cpp) + add_executable(headless-test ${CMAKE_CURRENT_SOURCE_DIR}/tests/HeadlessTest.cpp) + target_link_libraries(headless-test spine-cpp) endif() \ No newline at end of file diff --git a/spine-cpp/tests/DebugPrinter.cpp b/spine-cpp/tests/HeadlessTest.cpp similarity index 100% rename from spine-cpp/tests/DebugPrinter.cpp rename to spine-cpp/tests/HeadlessTest.cpp diff --git a/spine-libgdx/.vscode/launch.json b/spine-libgdx/.vscode/launch.json index 4b9567ece..84d0dab37 100644 --- a/spine-libgdx/.vscode/launch.json +++ b/spine-libgdx/.vscode/launch.json @@ -3,9 +3,9 @@ "configurations": [ { "type": "java", - "name": "debug-printer (java)", + "name": "headless test (java)", "request": "launch", - "mainClass": "com.esotericsoftware.spine.DebugPrinter", + "mainClass": "com.esotericsoftware.spine.HeadlessTest", "projectName": "spine-libgdx-tests", "args": [ "${workspaceFolder}/../examples/spineboy/export/spineboy-pro.json", diff --git a/spine-libgdx/build.gradle b/spine-libgdx/build.gradle index 4c4b490ae..502ddac24 100644 --- a/spine-libgdx/build.gradle +++ b/spine-libgdx/build.gradle @@ -145,9 +145,10 @@ configure(subprojects - project("spine-libgdx")) { } project("spine-libgdx-tests") { - task runDebugPrinter(type: JavaExec) { - main = 'com.esotericsoftware.spine.DebugPrinter' + task runHeadlessTest(type: JavaExec) { + main = 'com.esotericsoftware.spine.HeadlessTest' classpath = sourceSets.main.runtimeClasspath + workingDir = rootProject.projectDir if (project.hasProperty('args')) { args project.getProperty('args').split(' ') } diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/DebugPrinter.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/HeadlessTest.java similarity index 97% rename from spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/DebugPrinter.java rename to spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/HeadlessTest.java index 1d95a4c0b..69e3c0712 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/DebugPrinter.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/HeadlessTest.java @@ -41,12 +41,12 @@ import com.badlogic.gdx.graphics.g2d.TextureAtlas.TextureAtlasData; import java.util.Locale; -public class DebugPrinter implements ApplicationListener { +public class HeadlessTest implements ApplicationListener { private String skeletonPath; private String atlasPath; private String animationName; - public DebugPrinter (String skeletonPath, String atlasPath, String animationName) { + public HeadlessTest (String skeletonPath, String atlasPath, String animationName) { this.skeletonPath = skeletonPath; this.atlasPath = atlasPath; this.animationName = animationName; @@ -277,13 +277,13 @@ public class DebugPrinter implements ApplicationListener { public static void main (String[] args) { if (args.length < 2) { - System.err.println("Usage: DebugPrinter [animation-name]"); + System.err.println("Usage: HeadlessTest [animation-name]"); System.exit(1); } HeadlessApplicationConfiguration config = new HeadlessApplicationConfiguration(); config.updatesPerSecond = 60; String animationName = args.length >= 3 ? args[2] : null; - new HeadlessApplication(new DebugPrinter(args[0], args[1], animationName), config); + new HeadlessApplication(new HeadlessTest(args[0], args[1], animationName), config); } } diff --git a/spine-ts/.vscode/launch.json b/spine-ts/.vscode/launch.json index 1a8e532c8..32f9863a4 100644 --- a/spine-ts/.vscode/launch.json +++ b/spine-ts/.vscode/launch.json @@ -4,6 +4,26 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ + { + "name": "headless test (ts)", + "type": "node", + "request": "launch", + "runtimeExecutable": "npx", + "runtimeArgs": [ + "tsx" + ], + "program": "${workspaceFolder}/spine-core/tests/HeadlessTest.ts", + "args": [ + "${workspaceFolder}/../examples/spineboy/export/spineboy-pro.json", + "${workspaceFolder}/../examples/spineboy/export/spineboy-pma.atlas", + "walk" + ], + "cwd": "${workspaceFolder}/spine-core", + "console": "integratedTerminal", + "skipFiles": [ + "/**" + ] + }, { "type": "pwa-chrome", "request": "launch", @@ -39,26 +59,5 @@ "url": "http://localhost:8080/spine-phaser/example/index.html", "webRoot": "${workspaceFolder}" }, - { - "name": "debug-printer (ts)", - "type": "node", - "request": "launch", - "runtimeExecutable": "npx", - "runtimeArgs": [ - "tsx" - ], - "program": "${workspaceFolder}/spine-core/tests/DebugPrinter.ts", - "args": [ - "${workspaceFolder}/../examples/spineboy/export/spineboy-pro.json", - "${workspaceFolder}/../examples/spineboy/export/spineboy-pma.atlas", - "walk" - ], - "cwd": "${workspaceFolder}/spine-core", - "console": "integratedTerminal", - "skipFiles": [ - "/**" - ] - } - ] } diff --git a/spine-ts/spine-core/tests/DebugPrinter.ts b/spine-ts/spine-core/tests/HeadlessTest.ts similarity index 100% rename from spine-ts/spine-core/tests/DebugPrinter.ts rename to spine-ts/spine-core/tests/HeadlessTest.ts diff --git a/tests/README.md b/tests/README.md index fbbcd66b1..b80a171e0 100644 --- a/tests/README.md +++ b/tests/README.md @@ -10,41 +10,41 @@ Unlike traditional unit tests, this test suite: - Compares outputs between runtimes to detect discrepancies - Helps maintain consistency when porting changes from the reference implementation -## DebugPrinter Locations +## HeadlessTest Locations -Each runtime has a DebugPrinter program that outputs skeleton data in a standardized format: +Each runtime has a HeadlessTest program that outputs skeleton data in a standardized format: -- **Java (Reference)**: `spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/DebugPrinter.java` -- **C++**: `spine-cpp/tests/DebugPrinter.cpp` -- **C**: `spine-c/tests/debug-printer.c` -- **TypeScript**: `spine-ts/spine-core/tests/DebugPrinter.ts` +- **Java (Reference)**: `spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/HeadlessTest.java` +- **C++**: `spine-cpp/tests/HeadlessTest.cpp` +- **C**: `spine-c/tests/headless-test.c` +- **TypeScript**: `spine-ts/spine-core/tests/HeadlessTest.ts` -## Running Individual DebugPrinters +## Running Individual HeadlessTests ### Java (spine-libgdx) ```bash cd spine-libgdx -./gradlew :spine-libgdx-tests:runDebugPrinter -Pargs=" [animation-name]" +./gradlew :spine-libgdx-tests:runHeadlessTest -Pargs=" [animation-name]" ``` ### C++ (spine-cpp) ```bash cd spine-cpp ./build.sh # Build if needed -./build/debug-printer [animation-name] +./build/headless-test [animation-name] ``` ### C (spine-c) ```bash cd spine-c ./build.sh # Build if needed -./build/debug-printer [animation-name] +./build/headless-test [animation-name] ``` ### TypeScript (spine-ts) ```bash cd spine-ts/spine-core -npx tsx tests/DebugPrinter.ts [animation-name] +npx tsx tests/HeadlessTest.ts [animation-name] ``` ## Running the Comparison Test @@ -52,13 +52,13 @@ npx tsx tests/DebugPrinter.ts [animation-name] The main test runner compares all runtime outputs automatically: ```bash -./tests/compare-with-reference-impl.ts [animation-name] +./tests/headless-test-runner.ts [animation-name] ``` This script will: -1. Check if each runtime's DebugPrinter needs rebuilding -2. Build any out-of-date DebugPrinters -3. Run each DebugPrinter with the same inputs +1. Check if each runtime's HeadlessTest needs rebuilding +2. Build any out-of-date HeadlessTests +3. Run each HeadlessTest with the same inputs 4. Compare outputs and report any differences 5. Save individual outputs to `tests/output/` for manual inspection @@ -66,20 +66,20 @@ This script will: ```bash # Test with spineboy walk animation -./tests/compare-with-reference-impl.ts \ +./tests/headless-test-runner.ts \ examples/spineboy/export/spineboy-pro.json \ examples/spineboy/export/spineboy-pma.atlas \ walk # Test without animation (setup pose only) -./tests/compare-with-reference-impl.ts \ +./tests/headless-test-runner.ts \ examples/spineboy/export/spineboy-pro.json \ examples/spineboy/export/spineboy-pma.atlas ``` ## Output Format -Each DebugPrinter outputs: +Each HeadlessTest outputs: - **SKELETON DATA**: Static setup pose data (bones, slots, skins, animations metadata) - **SKELETON STATE**: Runtime state after applying animations @@ -87,4 +87,25 @@ The output uses consistent formatting: - Hierarchical structure with 2-space indentation - Float values formatted to 6 decimal places - Strings quoted, nulls explicitly shown -- Locale-independent number formatting (always uses `.` for decimals) \ No newline at end of file +- Locale-independent number formatting (always uses `.` for decimals) + +## Troubleshooting + +If outputs differ between runtimes: +1. Check `tests/output/` for the full outputs from each runtime +2. Use a diff tool to compare the files +3. Common issues: + - Number formatting differences (should be fixed by locale settings) + - Missing or extra fields in data structures + - Different default values + - Rounding differences + +## Future Expansion + +The current implementation prints basic skeleton data. Future expansions will include: +- Full bone and slot hierarchies +- All attachment types +- Animation timelines +- Constraint data +- Physics settings +- Complete runtime state after animation \ No newline at end of file diff --git a/tests/compare-with-reference-impl.ts b/tests/headless-test-runner.ts similarity index 90% rename from tests/compare-with-reference-impl.ts rename to tests/headless-test-runner.ts index 59f8edaa7..7b75063df 100755 --- a/tests/compare-with-reference-impl.ts +++ b/tests/headless-test-runner.ts @@ -41,7 +41,7 @@ async function getNewestFileTime(baseDir: string, patterns: string[]): Promise [animation-name]'); + console.error('Usage: headless-test-runner.ts [animation-name]'); process.exit(1); } @@ -68,7 +68,7 @@ const runtimes: RuntimeConfig[] = [ { name: 'java', buildCheck: async () => { - const classPath = path.join(rootDir, 'spine-libgdx/spine-libgdx-tests/build/classes/java/main/com/esotericsoftware/spine/DebugPrinter.class'); + const classPath = path.join(rootDir, 'spine-libgdx/spine-libgdx-tests/build/classes/java/main/com/esotericsoftware/spine/HeadlessTest.class'); if (!fs.existsSync(classPath)) return false; // Check if any source files are newer than the class file @@ -92,7 +92,7 @@ const runtimes: RuntimeConfig[] = [ ? `${absoluteSkeletonPath} ${absoluteAtlasPath} ${animationName}` : `${absoluteSkeletonPath} ${absoluteAtlasPath}`; const output = execSync( - `./gradlew -q :spine-libgdx-tests:runDebugPrinter -Pargs="${args}"`, + `./gradlew -q :spine-libgdx-tests:runHeadlessTest -Pargs="${args}"`, { cwd: path.join(rootDir, 'spine-libgdx'), encoding: 'utf8' @@ -111,7 +111,7 @@ const runtimes: RuntimeConfig[] = [ { name: 'cpp', buildCheck: async () => { - const execPath = path.join(rootDir, 'spine-cpp/build/debug-printer'); + const execPath = path.join(rootDir, 'spine-cpp/build/headless-test'); if (!fs.existsSync(execPath)) return false; // Check if any source files are newer than the executable @@ -133,8 +133,8 @@ const runtimes: RuntimeConfig[] = [ run: () => { return execSync( animationName - ? `./build/debug-printer "${absoluteSkeletonPath}" "${absoluteAtlasPath}" "${animationName}"` - : `./build/debug-printer "${absoluteSkeletonPath}" "${absoluteAtlasPath}"`, + ? `./build/headless-test "${absoluteSkeletonPath}" "${absoluteAtlasPath}" "${animationName}"` + : `./build/headless-test "${absoluteSkeletonPath}" "${absoluteAtlasPath}"`, { cwd: path.join(rootDir, 'spine-cpp'), encoding: 'utf8' @@ -145,7 +145,7 @@ const runtimes: RuntimeConfig[] = [ { name: 'c', buildCheck: async () => { - const execPath = path.join(rootDir, 'spine-c/build/debug-printer'); + const execPath = path.join(rootDir, 'spine-c/build/headless-test'); if (!fs.existsSync(execPath)) return false; // Check if any source files are newer than the executable @@ -167,8 +167,8 @@ const runtimes: RuntimeConfig[] = [ run: () => { return execSync( animationName - ? `./build/debug-printer "${absoluteSkeletonPath}" "${absoluteAtlasPath}" "${animationName}"` - : `./build/debug-printer "${absoluteSkeletonPath}" "${absoluteAtlasPath}"`, + ? `./build/headless-test "${absoluteSkeletonPath}" "${absoluteAtlasPath}" "${animationName}"` + : `./build/headless-test "${absoluteSkeletonPath}" "${absoluteAtlasPath}"`, { cwd: path.join(rootDir, 'spine-c'), encoding: 'utf8' @@ -179,16 +179,16 @@ const runtimes: RuntimeConfig[] = [ { name: 'ts', buildCheck: async () => { - // For TypeScript, just check if the DebugPrinter.ts file exists - const debugPrinterPath = path.join(rootDir, 'spine-ts/spine-core/tests/DebugPrinter.ts'); - return fs.existsSync(debugPrinterPath); + // For TypeScript, just check if the HeadlessTest.ts file exists + const headlessTestPath = path.join(rootDir, 'spine-ts/spine-core/tests/HeadlessTest.ts'); + return fs.existsSync(headlessTestPath); }, build: () => {}, // No build needed run: () => { return execSync( animationName - ? `npx tsx tests/DebugPrinter.ts "${absoluteSkeletonPath}" "${absoluteAtlasPath}" "${animationName}"` - : `npx tsx tests/DebugPrinter.ts "${absoluteSkeletonPath}" "${absoluteAtlasPath}"`, + ? `npx tsx tests/HeadlessTest.ts "${absoluteSkeletonPath}" "${absoluteAtlasPath}" "${animationName}"` + : `npx tsx tests/HeadlessTest.ts "${absoluteSkeletonPath}" "${absoluteAtlasPath}"`, { cwd: path.join(rootDir, 'spine-ts/spine-core'), encoding: 'utf8' @@ -202,7 +202,7 @@ async function main() { // Ensure output directory exists await mkdir(outputDir, { recursive: true }); - console.log('Comparing DebugPrinter outputs for:'); + console.log('Comparing HeadlessTest outputs for:'); console.log(` Skeleton: ${absoluteSkeletonPath}`); console.log(` Atlas: ${absoluteAtlasPath}`); console.log(` Animation: ${animationName}`); @@ -212,7 +212,7 @@ async function main() { const outputs: Record = {}; for (const runtime of runtimes) { - console.log(`Running ${runtime.name.toUpperCase()} DebugPrinter...`); + console.log(`Running ${runtime.name.toUpperCase()} HeadlessTest...`); try { // Build if needed