Files
cell/tests/chipmunk2d.js
John Alanbrook ad4f3d3f58
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
correct deletion
2025-02-26 10:07:32 -06:00

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);
}