From 44264db8ba4bb2d2cd5af1b274914ad483c17d4a Mon Sep 17 00:00:00 2001 From: Joywayer Date: Thu, 27 Aug 2026 21:32:01 +0800 Subject: [PATCH] =?UTF-8?q?fix(tools):=20skin.mjs=20=E6=98=BE=E5=BC=8F?= =?UTF-8?q?=E6=8A=A5=E9=94=99=E5=B0=BA=E5=AF=B8=E4=B8=8D=E5=8F=AF=E8=AF=BB?= =?UTF-8?q?=EF=BC=8C=E8=A1=A5=E5=85=A8=E6=B5=8B=E8=AF=95=E8=A6=86=E7=9B=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- cocoscreator_projects/scripts/lib/skin.mjs | 13 +++++++- .../scripts/test/skin.test.mjs | 32 +++++++++++++++++-- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/cocoscreator_projects/scripts/lib/skin.mjs b/cocoscreator_projects/scripts/lib/skin.mjs index b1698a6..7cb56a6 100644 --- a/cocoscreator_projects/scripts/lib/skin.mjs +++ b/cocoscreator_projects/scripts/lib/skin.mjs @@ -69,7 +69,18 @@ export function validateOverrides(frameworkUiDir, overrideDir) { if (needSizeCheck) { const a = readPngSize(join(frameworkUiDir, ...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({ path: rel, code: 'size-mismatch', diff --git a/cocoscreator_projects/scripts/test/skin.test.mjs b/cocoscreator_projects/scripts/test/skin.test.mjs index e413bae..4022e3a 100644 --- a/cocoscreator_projects/scripts/test/skin.test.mjs +++ b/cocoscreator_projects/scripts/test/skin.test.mjs @@ -147,13 +147,41 @@ test('图片尺寸不同但自带 meta(例外通道)→ 通过', () => { test('非图片资源(音效)不做尺寸校验', () => { const d = makeDirs(); try { - writeFile(d.fw, 'audio/click.wav', 'aaaa'); - writeFile(d.ov, 'audio/click.wav', 'bbbbbbbbbbbb'); + // 用真实的、有效的、尺寸不同的 PNG 字节,但扩展名是 .wav + // 如果 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); assert.deepEqual(r.errors, []); } 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 ---- test('Spine 整套覆盖 → 通过,且贴图豁免尺寸校验', () => { const d = makeDirs();