99 lines
6.6 KiB
JavaScript
99 lines
6.6 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import crypto from "node:crypto";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import { execFileSync } from "node:child_process";
|
|
import { fileURLToPath } from "node:url";
|
|
import ts from "../../web/node_modules/typescript/lib/typescript.js";
|
|
|
|
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
|
const inventory = JSON.parse(fs.readFileSync(path.join(root, "tests/golden/M12-02A/asset-preview-inventory.json"), "utf8"));
|
|
const sha256 = (file) => crypto.createHash("sha256").update(fs.readFileSync(path.join(root, file))).digest("hex");
|
|
|
|
assert.equal(inventory.task, "M12-02A");
|
|
assert.equal(inventory.enablingTask, true);
|
|
assert.equal(inventory.parityStateChange, false);
|
|
assert.equal(inventory.blenderVersion, "5.2.0");
|
|
assert.equal(inventory.nextTask, "M12-02B");
|
|
for (const item of [...inventory.sources, ...inventory.fixtures]) assert.equal(sha256(item.path), item.sha256, item.path);
|
|
|
|
const source = (role) => fs.readFileSync(path.join(root, inventory.sources.find((item) => item.role === role).path), "utf8");
|
|
const dna = source("PREVIEW_DNA");
|
|
for (const token of ["unsigned int w[2]", "unsigned int h[2]", "short flag[2]", "short changed_timestamp[2]", "unsigned int *rect[2]", "PreviewImageRuntime *runtime"]) {
|
|
assert.ok(dna.includes(token), `PreviewImage DNA token is missing: ${token}`);
|
|
}
|
|
const slots = source("PREVIEW_SLOT_ENUM");
|
|
assert.match(slots, /ICON_SIZE_ICON = 0/);
|
|
assert.match(slots, /ICON_SIZE_PREVIEW = 1/);
|
|
assert.equal(inventory.storage.slotCount, 2);
|
|
|
|
const implementation = source("PREVIEW_IMPLEMENTATION");
|
|
assert.match(implementation, /PreviewImage assumes pre-multiplied alpha/);
|
|
assert.match(implementation, /writer->write_uint32_array\(prv_copy\.w\[0\] \* prv_copy\.h\[0\]/);
|
|
assert.match(implementation, /writer->write_uint32_array\(prv_copy\.w\[1\] \* prv_copy\.h\[1\]/);
|
|
assert.match(implementation, /prv->flag\[i\] &= ~PRV_RENDERING/);
|
|
assert.equal(inventory.storage.colorSpace, null);
|
|
assert.ok(!inventory.storage.fields.some((field) => /color/i.test(field.name)));
|
|
|
|
const rnaSource = source("PREVIEW_RNA");
|
|
assert.match(rnaSource, /Image pixels, as bytes \(always 32-bit RGBA\)/);
|
|
assert.match(rnaSource, /length\[0\] = prv_img->w\[size\] \* prv_img->h\[size\] \* 4/);
|
|
assert.match(rnaSource, /values\[i\] = data\[i\] \* \(1\.0f \/ 255\.0f\)/);
|
|
assert.doesNotMatch(rnaSource.slice(rnaSource.indexOf("static void rna_def_image_preview"), rnaSource.indexOf("static void rna_def_image_user")), /color.?space/i);
|
|
|
|
const parseInterface = (file, name) => {
|
|
const absolute = path.join(root, file);
|
|
const parsed = ts.createSourceFile(absolute, fs.readFileSync(absolute, "utf8"), ts.ScriptTarget.Latest, true);
|
|
const declaration = parsed.statements.find((statement) => ts.isInterfaceDeclaration(statement) && statement.name.text === name);
|
|
assert.ok(declaration, `${name} is missing`);
|
|
return declaration.members.filter(ts.isPropertySignature).map((member) => member.name.getText(parsed));
|
|
};
|
|
assert.deepEqual(parseInterface("web/protocol/asset-library-io.ts", "AssetPreviewIR"), inventory.currentWeb.v1Fields);
|
|
assert.deepEqual(parseInterface("web/protocol/asset-catalog-v2.ts", "AssetCatalogV2PreviewIR"), inventory.currentWeb.v2Fields);
|
|
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
assert.match(execFileSync(blender, ["--version"], { encoding: "utf8" }), /^Blender 5\.2\.0 LTS/m);
|
|
const rnaProbe = [
|
|
"import bpy,json",
|
|
"props=[{'identifier':p.identifier,'type':p.type,'readOnly':p.is_readonly,'arrayLength':p.array_length} for p in bpy.types.ImagePreview.bl_rna.properties if p.identifier != 'rna_type']",
|
|
"idp=bpy.types.ID.bl_rna.properties['preview']",
|
|
"print('M12_PREVIEW_RNA='+json.dumps({'idProperty':{'identifier':idp.identifier,'type':idp.type,'readOnly':idp.is_readonly,'nullWhenAbsent':True},'imagePreviewProperties':props},separators=(',',':')))",
|
|
].join(";");
|
|
const rnaOutput = execFileSync(blender, ["--factory-startup", "--background", "--python-expr", rnaProbe], { encoding: "utf8" });
|
|
const rnaLine = rnaOutput.split(/\r?\n/).find((line) => line.startsWith("M12_PREVIEW_RNA="));
|
|
assert.ok(rnaLine);
|
|
assert.deepEqual(JSON.parse(rnaLine.slice("M12_PREVIEW_RNA=".length)), {
|
|
idProperty: inventory.rna.idProperty,
|
|
imagePreviewProperties: inventory.rna.imagePreviewProperties,
|
|
});
|
|
|
|
const noPreviewScript = [
|
|
"import bpy,json",
|
|
"items=[]",
|
|
"groups=(bpy.data.objects,bpy.data.materials,bpy.data.worlds)",
|
|
"[items.append({'type':item.bl_rna.identifier,'preview':None if item.preview is None else list(item.preview.image_size)}) for group in groups for item in group if item.asset_data]",
|
|
"print('M12_NO_PREVIEW='+json.dumps(items,separators=(',',':')))",
|
|
].join(";");
|
|
const noPreviewOutput = execFileSync(blender, ["--factory-startup", "--background", path.join(root, inventory.fixtures[1].path), "--python-expr", noPreviewScript], { encoding: "utf8" });
|
|
const noPreviewLine = noPreviewOutput.split(/\r?\n/).find((line) => line.startsWith("M12_NO_PREVIEW="));
|
|
assert.ok(noPreviewLine);
|
|
const noPreview = JSON.parse(noPreviewLine.slice("M12_NO_PREVIEW=".length));
|
|
assert.deepEqual(noPreview.map((item) => item.type), inventory.runtimeStates.noPreview.assetTypes);
|
|
assert.deepEqual(noPreview.map((item) => item.preview), inventory.runtimeStates.noPreview.previewValues);
|
|
|
|
const png = path.join(root, inventory.fixtures[0].path);
|
|
const loadedScript = [
|
|
"import bpy,json,bpy.utils.previews",
|
|
"collection=bpy.utils.previews.new()",
|
|
`preview=collection.load('m12-preview',${JSON.stringify(png)},'IMAGE',True)`,
|
|
"result={'sourceSize':[8,8],'imageSize':list(preview.image_size),'iconSize':list(preview.icon_size),'imagePackedPixelCount':len(preview.image_pixels),'imageFloatComponentCount':len(preview.image_pixels_float),'iconPackedPixelCount':len(preview.icon_pixels),'imageCustom':preview.is_image_custom,'iconCustom':preview.is_icon_custom}",
|
|
"print('M12_LOADED_PREVIEW='+json.dumps(result,separators=(',',':')))",
|
|
"bpy.utils.previews.remove(collection)",
|
|
].join(";");
|
|
const loadedOutput = execFileSync(blender, ["--factory-startup", "--background", "--python-expr", loadedScript], { encoding: "utf8" });
|
|
const loadedLine = loadedOutput.split(/\r?\n/).find((line) => line.startsWith("M12_LOADED_PREVIEW="));
|
|
assert.ok(loadedLine);
|
|
assert.deepEqual(JSON.parse(loadedLine.slice("M12_LOADED_PREVIEW=".length)), inventory.runtimeStates.loadedPreview);
|
|
|
|
process.stdout.write(`asset-preview-inventory-ok slots=${inventory.storage.slotCount} rna=${inventory.rna.imagePreviewProperties.length} absent=${noPreview.length} loaded=${inventory.runtimeStates.loadedPreview.imageSize.join("x")} next=${inventory.nextTask}\n`);
|