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:
@@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user