Browse Source

[tests] DebugPrinter -> HeadlessTest

Mario Zechner 1 month ago
parent
commit
73a17e88c9

+ 2 - 2
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",

+ 2 - 2
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()

+ 0 - 0
spine-c/tests/debug-printer.c → spine-c/tests/headless-test.c


+ 2 - 2
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",

+ 2 - 2
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()

+ 0 - 0
spine-cpp/tests/DebugPrinter.cpp → spine-cpp/tests/HeadlessTest.cpp


+ 2 - 2
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",

+ 3 - 2
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(' ')
 		}

+ 4 - 4
spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/DebugPrinter.java → 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 <skeleton-path> <atlas-path> [animation-name]");
+			System.err.println("Usage: HeadlessTest <skeleton-path> <atlas-path> [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);
 	}
 }

+ 20 - 21
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": [
+				"<node_internals>/**"
+			]
+		},
 		{
 			"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": [
-				"<node_internals>/**"
-			]
-		}
-
 	]
 }

+ 0 - 0
spine-ts/spine-core/tests/DebugPrinter.ts → spine-ts/spine-core/tests/HeadlessTest.ts


+ 40 - 19
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="<skeleton-path> <atlas-path> [animation-name]"
+./gradlew :spine-libgdx-tests:runHeadlessTest -Pargs="<skeleton-path> <atlas-path> [animation-name]"
 ```
 
 ### C++ (spine-cpp)
 ```bash
 cd spine-cpp
 ./build.sh  # Build if needed
-./build/debug-printer <skeleton-path> <atlas-path> [animation-name]
+./build/headless-test <skeleton-path> <atlas-path> [animation-name]
 ```
 
 ### C (spine-c)
 ```bash
 cd spine-c
 ./build.sh  # Build if needed
-./build/debug-printer <skeleton-path> <atlas-path> [animation-name]
+./build/headless-test <skeleton-path> <atlas-path> [animation-name]
 ```
 
 ### TypeScript (spine-ts)
 ```bash
 cd spine-ts/spine-core
-npx tsx tests/DebugPrinter.ts <skeleton-path> <atlas-path> [animation-name]
+npx tsx tests/HeadlessTest.ts <skeleton-path> <atlas-path> [animation-name]
 ```
 
 ## Running the Comparison Test
@@ -52,13 +52,13 @@ npx tsx tests/DebugPrinter.ts <skeleton-path> <atlas-path> [animation-name]
 The main test runner compares all runtime outputs automatically:
 
 ```bash
-./tests/compare-with-reference-impl.ts <skeleton-path> <atlas-path> [animation-name]
+./tests/headless-test-runner.ts <skeleton-path> <atlas-path> [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)
+- 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

+ 16 - 16
tests/compare-with-reference-impl.ts → tests/headless-test-runner.ts

@@ -41,7 +41,7 @@ async function getNewestFileTime(baseDir: string, patterns: string[]): Promise<n
 // Parse command line arguments
 const args = process.argv.slice(2);
 if (args.length < 2) {
-    console.error('Usage: compare-with-reference-impl.ts <skeleton-path> <atlas-path> [animation-name]');
+    console.error('Usage: headless-test-runner.ts <skeleton-path> <atlas-path> [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<string, string> = {};
 
     for (const runtime of runtimes) {
-        console.log(`Running ${runtime.name.toUpperCase()} DebugPrinter...`);
+        console.log(`Running ${runtime.name.toUpperCase()} HeadlessTest...`);
 
         try {
             // Build if needed