Some checks failed
Build and Deploy / build-linux (push) Failing after 39s
Build and Deploy / build-windows (CLANG64) (push) Failing after 8m19s
Build and Deploy / package-dist (push) Has been skipped
Build and Deploy / deploy-itch (push) Has been skipped
Build and Deploy / deploy-gitea (push) Has been skipped
291 lines
9.3 KiB
JavaScript
291 lines
9.3 KiB
JavaScript
// chipmunk_test.js
|
|
var chipmunk = use('chipmunk2d');
|
|
var os = use('os');
|
|
|
|
// Constants
|
|
var EPSILON = 1e-12; // Tolerance for floating-point comparisons
|
|
|
|
// Helper function to create a vector
|
|
function vec(x, y) {
|
|
return { x: x, y: y };
|
|
}
|
|
|
|
// Deep comparison function for objects and physics properties
|
|
function deepCompare(expected, actual, path = '') {
|
|
if (expected === actual) return { passed: true, messages: [] };
|
|
|
|
// Handle vector comparison
|
|
if (expected && expected.x !== undefined && expected.y !== undefined &&
|
|
actual && actual.x !== undefined && actual.y !== undefined) {
|
|
const dx = Math.abs(expected.x - actual.x);
|
|
const dy = Math.abs(expected.y - actual.y);
|
|
if (dx <= EPSILON && dy <= EPSILON) {
|
|
return { passed: true, messages: [] };
|
|
}
|
|
return {
|
|
passed: false,
|
|
messages: [
|
|
`Vector mismatch at ${path}: expected (${expected.x}, ${expected.y}), got (${actual.x}, ${actual.y})`,
|
|
`Differences: x=${dx}, y=${dy} exceed tolerance ${EPSILON}`
|
|
]
|
|
};
|
|
}
|
|
|
|
// Number comparison with tolerance
|
|
if (typeof expected === 'number' && typeof actual === 'number') {
|
|
const diff = Math.abs(expected - actual);
|
|
if (diff <= EPSILON) {
|
|
return { passed: true, messages: [] };
|
|
}
|
|
return {
|
|
passed: false,
|
|
messages: [
|
|
`Number mismatch at ${path}: expected ${expected}, got ${actual}`,
|
|
`Difference ${diff} exceeds tolerance ${EPSILON}`
|
|
]
|
|
};
|
|
}
|
|
|
|
// Array comparison
|
|
if (Array.isArray(expected) && Array.isArray(actual)) {
|
|
if (expected.length !== actual.length) {
|
|
return {
|
|
passed: false,
|
|
messages: [`Array length mismatch at ${path}: expected ${expected.length}, got ${actual.length}`]
|
|
};
|
|
}
|
|
let messages = [];
|
|
for (let i = 0; i < expected.length; i++) {
|
|
const result = deepCompare(expected[i], actual[i], `${path}[${i}]`);
|
|
if (!result.passed) messages.push(...result.messages);
|
|
}
|
|
return { passed: messages.length === 0, messages };
|
|
}
|
|
|
|
// Object comparison
|
|
if (typeof expected === 'object' && expected !== null &&
|
|
typeof actual === 'object' && actual !== null) {
|
|
const expKeys = Object.keys(expected).sort();
|
|
const actKeys = Object.keys(actual).sort();
|
|
if (JSON.stringify(expKeys) !== JSON.stringify(actKeys)) {
|
|
return {
|
|
passed: false,
|
|
messages: [`Object keys mismatch at ${path}: expected ${expKeys}, got ${actKeys}`]
|
|
};
|
|
}
|
|
let messages = [];
|
|
for (let key of expKeys) {
|
|
const result = deepCompare(expected[key], actual[key], `${path}.${key}`);
|
|
if (!result.passed) messages.push(...result.messages);
|
|
}
|
|
return { passed: messages.length === 0, messages };
|
|
}
|
|
|
|
return {
|
|
passed: false,
|
|
messages: [`Value mismatch at ${path}: expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`]
|
|
};
|
|
}
|
|
|
|
// Test cases for Chipmunk functionality
|
|
var testCases = [
|
|
// Space tests
|
|
{
|
|
name: "Space creation and gravity",
|
|
run: () => {
|
|
const space = chipmunk.make_space();
|
|
space.gravity = vec(0, -9.81);
|
|
return deepCompare(vec(0, -9.81), space.gravity);
|
|
}
|
|
},
|
|
{
|
|
name: "Space iterations",
|
|
run: () => {
|
|
const space = chipmunk.make_space();
|
|
space.iterations = 5;
|
|
return deepCompare(5, space.iterations);
|
|
}
|
|
},
|
|
|
|
// Body tests
|
|
{
|
|
name: "Body creation and position",
|
|
run: () => {
|
|
const space = chipmunk.make_space();
|
|
const body = space.body();
|
|
body.position = vec(10, 20);
|
|
return deepCompare(vec(10, 20), body.position);
|
|
}
|
|
},
|
|
{
|
|
name: "Body mass and moment",
|
|
run: () => {
|
|
const space = chipmunk.make_space();
|
|
const body = space.body();
|
|
body.type = 0
|
|
body.mass = 10;
|
|
body.moment = 100;
|
|
return {
|
|
passed: deepCompare(10, body.mass).passed && deepCompare(100, body.moment).passed,
|
|
messages: [
|
|
...deepCompare(10, body.mass).messages,
|
|
...deepCompare(100, body.moment).messages
|
|
]
|
|
};
|
|
}
|
|
},
|
|
{
|
|
name: "Body velocity",
|
|
run: () => {
|
|
const space = chipmunk.make_space();
|
|
const body = space.body();
|
|
body.velocity = vec(5, -5);
|
|
return deepCompare(vec(5, -5), body.velocity);
|
|
}
|
|
},
|
|
{
|
|
name: "Body force application",
|
|
run: () => {
|
|
const space = chipmunk.make_space();
|
|
const body = space.body();
|
|
body.type = 0
|
|
body.mass = 1;
|
|
body.moment = 1
|
|
body.applyForceAtWorldPoint(vec(10, 0), vec(0, 0));
|
|
space.step(1/60); // One frame at 60 FPS
|
|
const expectedVelocity = vec(10/60, 0); // F = ma, v = at
|
|
return deepCompare(expectedVelocity, body.velocity);
|
|
}
|
|
},
|
|
|
|
// Shape tests
|
|
{
|
|
name: "Circle shape creation",
|
|
run: () => {
|
|
const space = chipmunk.make_space();
|
|
const body = space.body();
|
|
const shape = body.circle({radius:5, offset:vec(0, 0)});
|
|
const radiusResult = deepCompare(5, shape.radius);
|
|
const offsetResult = deepCompare(vec(0, 0), shape.offset);
|
|
return {
|
|
passed: radiusResult.passed && offsetResult.passed,
|
|
messages: [...radiusResult.messages, ...offsetResult.messages]
|
|
};
|
|
}
|
|
},
|
|
{
|
|
name: "Segment shape creation",
|
|
run: () => {
|
|
const space = chipmunk.make_space();
|
|
const body = space.body();
|
|
const shape = body.add_segment_shape(vec(0, 0), vec(10, 10), 2);
|
|
for (var i in shape) console.log(i)
|
|
shape.setEndpoints(vec(1, 1), vec(11, 11));
|
|
const radiusResult = deepCompare(2, shape.radius);
|
|
// Note: Chipmunk doesn't provide endpoint getters, so we test indirectly
|
|
return radiusResult;
|
|
}
|
|
},
|
|
|
|
// Constraint tests
|
|
{
|
|
name: "Pin joint",
|
|
run: () => {
|
|
const space = chipmunk.make_space();
|
|
const body1 = space.body();
|
|
const body2 = space.body();
|
|
body1.position = vec(0, 0);
|
|
body2.position = vec(10, 0);
|
|
const joint = space.pin(body1, body2);
|
|
joint.distance = 10;
|
|
return deepCompare(10, joint.distance);
|
|
}
|
|
},
|
|
{
|
|
name: "Pivot joint",
|
|
run: () => {
|
|
const space = chipmunk.make_space();
|
|
const body1 = space.body();
|
|
const body2 = space.body();
|
|
const joint = space.pivot(body1, body2, vec(5, 5));
|
|
const anchorAResult = deepCompare(vec(5, 5), joint.anchor_a);
|
|
const anchorBResult = deepCompare(vec(5, 5), joint.anchor_b);
|
|
return {
|
|
passed: anchorAResult.passed && anchorBResult.passed,
|
|
messages: [...anchorAResult.messages, ...anchorBResult.messages]
|
|
};
|
|
}
|
|
},
|
|
|
|
// Simulation test
|
|
{
|
|
name: "Basic gravity simulation",
|
|
run: () => {
|
|
const space = chipmunk.make_space();
|
|
console.log(space.gravity)
|
|
space.gravity = vec(0, -9.81);
|
|
console.log(space.gravity)
|
|
const body = space.body();
|
|
body.circle(5);
|
|
body.mass = 1;
|
|
body.moment = 1;
|
|
body.position = vec(0, 100);
|
|
for (var i = 0; i < 61; i++) space.step(1/60)
|
|
const expectedPos = vec(0, 100 - (9.81/2)); // s = ut + (1/2)at^2
|
|
return deepCompare(expectedPos, body.position);
|
|
}
|
|
}
|
|
];
|
|
|
|
// Run tests and collect results
|
|
let results = [];
|
|
let testCount = 0;
|
|
|
|
for (let test of testCases) {
|
|
testCount++;
|
|
let testName = `Test ${testCount}: ${test.name}`;
|
|
try {
|
|
const result = test.run();
|
|
results.push({
|
|
testName,
|
|
passed: result.passed,
|
|
messages: result.messages
|
|
});
|
|
|
|
if (!result.passed) {
|
|
console.log(`\nDetailed Failure Report for ${testName}:`);
|
|
console.log(result.messages.join("\n"));
|
|
console.log("");
|
|
}
|
|
} catch (e) {
|
|
results.push({
|
|
testName,
|
|
passed: false,
|
|
messages: [`Test threw exception: ${e.message}`]
|
|
});
|
|
console.log(`\nException in ${testName}:`);
|
|
console.log(e.stack || e.message);
|
|
console.log("");
|
|
}
|
|
}
|
|
|
|
// Summary
|
|
console.log("\nTest Summary:");
|
|
results.forEach(result => {
|
|
console.log(`${result.testName} - ${result.passed ? "Passed" : "Failed"}`);
|
|
if (!result.passed) {
|
|
console.log(result.messages.map(m => " " + m).join("\n"));
|
|
}
|
|
});
|
|
|
|
let passedCount = results.filter(r => r.passed).length;
|
|
console.log(`\nResult: ${passedCount}/${testCount} tests passed`);
|
|
|
|
if (passedCount < testCount) {
|
|
console.log("Overall: FAILED");
|
|
os.exit(1);
|
|
} else {
|
|
console.log("Overall: PASSED");
|
|
os.exit(0);
|
|
}
|