mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2025-12-20 17:26:01 +08:00
[tests] DebugPrinter -> HeadlessTest
This commit is contained in:
parent
99f9aca731
commit
73a17e88c9
4
spine-c/.vscode/launch.json
vendored
4
spine-c/.vscode/launch.json
vendored
@ -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",
|
||||
|
||||
@ -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()
|
||||
|
||||
4
spine-cpp/.vscode/launch.json
vendored
4
spine-cpp/.vscode/launch.json
vendored
@ -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",
|
||||
|
||||
@ -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()
|
||||
4
spine-libgdx/.vscode/launch.json
vendored
4
spine-libgdx/.vscode/launch.json
vendored
@ -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",
|
||||
|
||||
@ -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(' ')
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
41
spine-ts/.vscode/launch.json
vendored
41
spine-ts/.vscode/launch.json
vendored
@ -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>/**"
|
||||
]
|
||||
}
|
||||
|
||||
]
|
||||
}
|
||||
|
||||
@ -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
|
||||
@ -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
|
||||
Loading…
x
Reference in New Issue
Block a user