fix(tools): skin.mjs 显式报错尺寸不可读,补全测试覆盖

Finding 1: 尺寸校验不能无声跳过(null 被当作通过)。
新增 size-unreadable 错误码,当图片无法读取尺寸时显式报错。

Finding 2: 测试 10 是伪测试(.wav 太短,被长度守卫拦下)。
改用真实 PNG 字节但 .wav 扩展名,验证 isImage() 门控在。

新增两条用例:
- size-unreadable 错误场景(override 侧非 PNG)
- PNG 签名检查(长度足够但签名不对)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-27 21:32:01 +08:00
co-authored by Claude Opus 5
parent 000f9e4290
commit 44264db8ba
2 changed files with 42 additions and 3 deletions
+12 -1
View File
@@ -69,7 +69,18 @@ export function validateOverrides(frameworkUiDir, overrideDir) {
if (needSizeCheck) { if (needSizeCheck) {
const a = readPngSize(join(frameworkUiDir, ...rel.split(posix.sep))); const a = readPngSize(join(frameworkUiDir, ...rel.split(posix.sep)));
const b = readPngSize(join(overrideDir, ...rel.split(posix.sep))); const b = readPngSize(join(overrideDir, ...rel.split(posix.sep)));
if (a && b && (a.width !== b.width || a.height !== b.height)) { if (!a || !b) {
const side = !a ? '框架' : 'override';
errors.push({
path: rel,
code: 'size-unreadable',
message:
`框架 UI 图片必须是 PNG(无法读取尺寸:${side})。` +
`请改用 PNG,或连 .meta 一起提供(例外通道)`,
});
continue;
}
if (a.width !== b.width || a.height !== b.height) {
errors.push({ errors.push({
path: rel, path: rel,
code: 'size-mismatch', code: 'size-mismatch',
@@ -147,13 +147,41 @@ test('图片尺寸不同但自带 meta(例外通道)→ 通过', () => {
test('非图片资源(音效)不做尺寸校验', () => { test('非图片资源(音效)不做尺寸校验', () => {
const d = makeDirs(); const d = makeDirs();
try { try {
writeFile(d.fw, 'audio/click.wav', 'aaaa'); // 用真实的、有效的、尺寸不同的 PNG 字节,但扩展名是 .wav
writeFile(d.ov, 'audio/click.wav', 'bbbbbbbbbbbb'); // 如果 isImage() 门控被删掉,因为尺寸不同会报 size-mismatch,用例失败
writeFile(d.fw, 'audio/click.wav', makePng(64, 64));
writeFile(d.ov, 'audio/click.wav', makePng(256, 256));
const r = validateOverrides(d.fw, d.ov); const r = validateOverrides(d.fw, d.ov);
assert.deepEqual(r.errors, []); assert.deepEqual(r.errors, []);
} finally { cleanup(d.root); } } finally { cleanup(d.root); }
}); });
// ---- validateOverrides:尺寸不可读 ----
test('图片尺寸无法读取(非 PNG)→ size-unreadable', () => {
const d = makeDirs();
try {
png(d.fw, 'atlas-hall/btn.png', 64, 64);
writeFile(d.ov, 'atlas-hall/btn.png', 'this is not a png but it is long enough to pass the 24-byte length check');
const r = validateOverrides(d.fw, d.ov);
assert.equal(r.errors.length, 1);
assert.equal(r.errors[0].code, 'size-unreadable');
assert.match(r.errors[0].message, /无法读取尺寸/);
assert.match(r.errors[0].message, /PNG/);
assert.equal(r.matched.length, 0);
} finally { cleanup(d.root); }
});
test('readPngSize 长度足够但签名不对 → 返回 null', () => {
const d = makeDirs();
try {
// 构造一个 24 字节以上、但 PNG 签名不对的数据
const buf = Buffer.alloc(30, 0x00);
buf.write('NOTPNG', 1, 'ascii');
const p = writeFile(d.fw, 'test.png', buf);
assert.equal(readPngSize(p), null);
} finally { cleanup(d.root); }
});
// ---- validateOverrides:Spine ---- // ---- validateOverrides:Spine ----
test('Spine 整套覆盖 → 通过,且贴图豁免尺寸校验', () => { test('Spine 整套覆盖 → 通过,且贴图豁免尺寸校验', () => {
const d = makeDirs(); const d = makeDirs();