UNPKG

exiftool-vendored

Version:
267 lines 11.8 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) { if (value !== null && value !== void 0) { if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected."); var dispose, inner; if (async) { if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined."); dispose = value[Symbol.asyncDispose]; } if (dispose === void 0) { if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined."); dispose = value[Symbol.dispose]; if (async) inner = dispose; } if (typeof dispose !== "function") throw new TypeError("Object not disposable."); if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } }; env.stack.push({ value: value, dispose: dispose, async: async }); } else if (async) { env.stack.push({ async: true }); } return value; }; var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) { return function (env) { function fail(e) { env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e; env.hasError = true; } var r, s = 0; function next() { while (r = env.stack.pop()) { try { if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next); if (r.dispose) { var result = r.dispose.call(r.value); if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); }); } else s |= 1; } catch (e) { fail(e); } } if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve(); if (env.hasError) throw env.error; } return next(); }; })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { var e = new Error(message); return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; }); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.UnicodeTestMessage = exports.NonAlphaStrings = exports.IPTC_JPG = exports.tmpdir = exports.testDir = exports.expect = void 0; exports.mkdirp = mkdirp; exports.end = end; exports.randomChar = randomChar; exports.randomChars = randomChars; exports.tmpname = tmpname; exports.renderTagsWithISO = renderTagsWithISO; exports.renderTagsWithRawValues = renderTagsWithRawValues; exports.testImg = testImg; exports.testFile = testFile; exports.sha1 = sha1; exports.sha1buffer = sha1buffer; exports.assertEqlDateish = assertEqlDateish; exports.measureSpawnTime = measureSpawnTime; const batch_cluster_1 = require("batch-cluster"); const chai_1 = require("chai"); const chai_as_promised_1 = __importDefault(require("chai-as-promised")); const node_crypto_1 = require("node:crypto"); const node_fs_1 = require("node:fs"); const promises_1 = require("node:fs/promises"); const node_path_1 = require("node:path"); const node_process_1 = __importStar(require("node:process")); const tmp_1 = require("tmp"); const Array_1 = require("./Array"); const DateTime_1 = require("./DateTime"); const ExifTool_1 = require("./ExifTool"); const IsWin32_1 = require("./IsWin32"); const Lazy_1 = require("./Lazy"); const Object_1 = require("./Object"); const String_1 = require("./String"); const Times_1 = require("./Times"); // Tests create many ExifTool instances, each adds a beforeExit listener via // batch-cluster. Increase the limit to avoid spurious warnings. node_process_1.default.setMaxListeners(20); (0, chai_1.use)(chai_as_promised_1.default); function mkdirp(dir) { return (0, promises_1.mkdir)(dir, { recursive: true }); } // Tests should be quiet unless LOG is set (0, batch_cluster_1.setLogger)(batch_cluster_1.Log.withLevels(batch_cluster_1.Log.withTimestamps(batch_cluster_1.Log.filterLevels({ trace: console.log, debug: console.log, info: console.log, warn: console.warn, error: console.error, }, node_process_1.env.LOG ?? "error")))); var chai_2 = require("chai"); Object.defineProperty(exports, "expect", { enumerable: true, get: function () { return chai_2.expect; } }); exports.testDir = (0, node_path_1.join)(__dirname, "..", "test"); const LOWERCASE_A_CHAR_CODE = "a".charCodeAt(0); const ALPHABET_LENGTH = "z".charCodeAt(0) - "a".charCodeAt(0) + 1; async function end(et) { await et?.end(); if (et != null) (0, chai_1.expect)(et.batchCluster.internalErrorCount).to.eql(0); } function randomChar() { return String.fromCharCode(LOWERCASE_A_CHAR_CODE + (0, node_crypto_1.randomInt)(ALPHABET_LENGTH)); } function randomChars(chars = 8) { return (0, Times_1.times)(chars, randomChar).join(""); } exports.tmpdir = (0, Lazy_1.lazy)(() => (0, tmp_1.dirSync)().name); function tmpname(prefix = "") { return (0, node_path_1.join)((0, exports.tmpdir)(), prefix + randomChars()); } function renderTagsWithISO(t) { return (0, Object_1.fromEntries)(Object.entries(t).map(([k, v]) => k === "SourceFile" ? undefined : [k, v["toISOString"]?.() ?? v])); } function renderTagsWithRawValues(t) { return (0, Object_1.fromEntries)(Object.entries(t).map(([k, v]) => [k, v["rawValue"]])); } /** * Copy a test image to a tmp directory and return the path */ async function testImg({ srcBasename = "img.jpg", parentDir, destBasename, } = {}) { const parent = tmpname(); const dir = parentDir == null ? parent : (0, node_path_1.join)(parent, parentDir); await mkdirp(dir); const dest = (0, node_path_1.join)(dir, destBasename ?? srcBasename); await (0, promises_1.copyFile)((0, node_path_1.join)(exports.testDir, srcBasename), dest); return dest; } exports.IPTC_JPG = "iptc.jpg"; async function testFile(name) { const dir = tmpname(); await mkdirp(dir); return (0, node_path_1.join)(dir, name); } function sha1(path) { const d = new batch_cluster_1.Deferred(); const readStream = (0, node_fs_1.createReadStream)(path, { autoClose: true }); const sha = (0, node_crypto_1.createHash)("sha1"); readStream.on("data", (ea) => sha.update(ea)); readStream.on("error", (err) => d.reject(err)); readStream.on("end", () => d.resolve(sha.digest().toString("hex"))); return d.promise; } function sha1buffer(input) { return (0, node_crypto_1.createHash)("sha1").update(input).digest().toString("hex"); } function dateishToExifString(d) { return d == null ? undefined : (0, String_1.isString)(d) ? d : (0, DateTime_1.toExifString)(d); } function assertEqlDateish(a, b) { return (0, chai_1.expect)(dateishToExifString(a)).to.eql(dateishToExifString(b)); } exports.NonAlphaStrings = (0, Array_1.compact)([ { str: `'`, desc: "straight single quote" }, // windows doesn't support double-quotes in filenames (!!) (0, IsWin32_1.isWin32)() ? undefined : { str: `"`, desc: "straight double quote" }, { str: `‘’“”«»`, desc: "curly quotes" }, { str: "ñöᵽȅ", desc: "latin extended" }, { str: "✋", desc: "dingbats block" }, // These emoji tests fail on node 18.18+ and node 20 on Windows, but it's a // bug in node, as it passes on macOS and Linux. (0, IsWin32_1.isWin32)() ? undefined : { str: "😤", desc: "emoticons block" }, (0, IsWin32_1.isWin32)() ? undefined : { str: "🚵🏿‍♀", desc: "transport block" }, { str: "你好", desc: "Mandarin" }, { str: "ようこそ", desc: "Japanese" }, { str: "ברוך הבא", desc: "Hebrew" }, { str: "ਸੁਆਗਤ ਹੈ", desc: "Punjabi" }, ]); // Using Unicode escapes for quote characters to prevent editor/tool mangling: // \u201c = " (left double quotation mark) // \u201d = " (right double quotation mark) // \u00ab = « (left-pointing double angle quotation mark) // \u00bb = » (right-pointing double angle quotation mark) // \u2018 = ' (left single quotation mark) // \u2019 = ' (right single quotation mark) // \u2039 = ‹ (single left-pointing angle quotation mark) // \u203a = › (single right-pointing angle quotation mark) exports.UnicodeTestMessage = `Double quotes("\u201c\u201d\u00ab\u00bb) and single quotes('\u2018\u2019\u2039\u203a) and backquotes(\`), oh my 👍🌹🐱‍👓🚵‍♀️. ਸੁਆਗਤ ਹੈ ยินดีต้อนรับ 환영하다 ようこそ 歡迎 欢迎 ברוך הבא خوش آمدید`; let _cachedSpawnTimeMs; /** * Measure how long it takes to spawn ExifTool and get a response. This provides * a baseline for setting realistic timeout values in tests. CI environments * (especially Windows) can be 10-100x slower than local dev. * * Results are cached after first measurement to avoid overhead. * * CAUTION: You probably want to schedule this in a before() block so it doesn't * impact test timeouts! */ async function measureSpawnTime() { if (_cachedSpawnTimeMs != null) { return _cachedSpawnTimeMs; } // Take 3 measurements, each with a fresh ExifTool instance // to accurately measure cold-start spawn time const measurements = []; for (let i = 0; i < 4; i++) { const env_1 = { stack: [], error: void 0, hasError: false }; try { const et = __addDisposableResource(env_1, new ExifTool_1.ExifTool({ maxProcs: 1 }), true); const start = Date.now(); await et.version(); measurements.push(Date.now() - start); } catch (e_1) { env_1.error = e_1; env_1.hasError = true; } finally { const result_1 = __disposeResources(env_1); if (result_1) await result_1; } } _cachedSpawnTimeMs = Math.ceil(measurements.reduce((a, b) => a + b, 0) / measurements.length); console.log("measureSpawnTime()", { result: _cachedSpawnTimeMs, measurements, }); return _cachedSpawnTimeMs; } //# sourceMappingURL=_chai.spec.js.map