Setting the file. One moment.
TTS Cleanup Test · Media Use · heygen-com/hyperframes · Skills Docs
ContentsBack to the top of the page Resolve
audio/scripts/lib/tts.cleanup.test.mjs
audio/scripts/lib/ tts.cleanup.test.mjs
JavaScript · 175 lines · 7 KB
const
ttsUrl
=
new
URL
(
"./tts.mjs"
,
import
.
meta
.url).href;
9 const stub = `#!${ process . execPath }
10 const fs = require("node:fs");
11 const path = require("node:path");
12 const args = process.argv.slice(2);
13 if (args[0] === "--version") {
14 console.log("Python 3.12.0");
15 process.exit(0);
16 }
17 const input = args[2];
18 const output = args[0] === "-c" ? args[4] : args[args.indexOf("--output") + 1];
19 const text = fs.readFileSync(input, "utf8");
20 fs.writeFileSync(output + ".started", input);
21 const finish = () => {
22 if (text === "slow" && !fs.existsSync(process.env.RELEASE_FILE)) {
23 setTimeout(finish, 5);
24 return;
25 }
26 const consumed = fs.readFileSync(input, "utf8");
27 fs.writeFileSync(output + ".json", JSON.stringify({ input, text, consumed, args }));
28 if (process.env.CASE_MODE === "nonzero") process.exit(7);
29 if (process.env.CASE_MODE !== "no-output") fs.writeFileSync(output, consumed);
30 };
31 setTimeout(finish, 10);
32 ` ;
33
34 const exercise = `
35 import assert from "node:assert/strict";
36 import fs from "node:fs";
37 import childProcess from "node:child_process";
38 import { syncBuiltinESMExports } from "node:module";
39 import { join, dirname } from "node:path";
40 const { CASE_ROOT: root, CASE_MODE: mode, CASE_PROVIDER: provider } = process.env;
41 const temp = join(root, "tmp");
42 const preexisting = join(temp, "hf-txt-preexisting");
43 fs.mkdirSync(preexisting);
44 fs.writeFileSync(join(preexisting, "marker"), "keep");
45 const injected = Object.assign(new Error("synthetic " + mode), { code: "EACCES" });
46 const originalMkdtemp = fs.mkdtempSync;
47 const originalWrite = fs.writeFileSync;
48 const originalRm = fs.rmSync;
49 if (mode === "allocation") fs.mkdtempSync = (prefix, ...args) => {
50 if (prefix.startsWith(join(temp, "hf-txt-"))) throw injected;
51 return originalMkdtemp(prefix, ...args);
52 };
53 if (mode === "write") fs.writeFileSync = (file, ...args) => {
54 if (String(file).endsWith("line.txt")) {
55 originalWrite(file, "partial");
56 throw injected;
57 }
58 return originalWrite(file, ...args);
59 };
60 if (mode === "spawn-throw") childProcess.spawn = () => { throw injected; };
61 if (mode === "cleanup") fs.rmSync = (dir, ...args) => {
62 if (String(dir).startsWith(join(temp, "hf-txt-"))) throw injected;
63 return originalRm(dir, ...args);
64 };
65 syncBuiltinESMExports();
66 const { synthesizeOne } = await import(${ JSON . stringify ( ttsUrl ) });
67 const project = join(root, "project");
68 const wavAbs = join(project, "line.wav");
69 const request = (chosen = provider, text = "synthetic café", output = wavAbs) =>
70 synthesizeOne({ provider: chosen, text, voiceId: "synthetic-voice", lang: "es",
71 speed: 0.8, wavAbs: output, hyperframesDir: project });
72 const owned = () => fs.readdirSync(temp).filter(name => name !== "hf-txt-preexisting");
73 if (["allocation", "write", "spawn-throw"].includes(mode)) {
74 await assert.rejects(request(), error => error === injected);
75 assert.equal(fs.existsSync(wavAbs), false);
76 } else if (mode === "concurrent") {
77 const slowWav = join(project, "slow.wav");
78 const slow = request("elevenlabs", "slow", slowWav);
79 try {
80 const deadline = Date.now() + 5000;
81 while (!fs.existsSync(slowWav + ".started")) {
82 assert.ok(Date.now() < deadline, "slow subprocess must start");
83 await new Promise(resolve => setTimeout(resolve, 5));
84 }
85 const slowInput = fs.readFileSync(slowWav + ".started", "utf8");
86 assert.deepEqual(await request("kokoro"), { ok: true, words: null });
87 assert.deepEqual(owned(), [dirname(slowInput).split(/[ \\\\ /]/).pop()]);
88 assert.equal(fs.readFileSync(slowInput, "utf8"), "slow");
89 } finally {
90 fs.writeFileSync(process.env.RELEASE_FILE, "release");
91 await slow;
92 }
93 assert.equal(fs.readFileSync(slowWav, "utf8"), "slow");
94 } else {
95 const result = await request();
96 const label = provider === "elevenlabs" ? "elevenlabs (python)" : "kokoro (npx hyperframes tts)";
97 if (["nonzero", "enoent", "no-output"].includes(mode)) {
98 const error = mode === "no-output" ? label + " produced no wav file" :
99 label + " exited with status " + (mode === "enoent" ? -1 : 7);
100 assert.deepEqual(result, { ok: false, words: null, error });
101 assert.equal(fs.existsSync(wavAbs), false);
102 } else {
103 assert.deepEqual(result, { ok: true, words: null });
104 assert.equal(fs.readFileSync(wavAbs, "utf8"), "synthetic café");
105 }
106 if (mode !== "enoent") {
107 const log = JSON.parse(fs.readFileSync(wavAbs + ".json", "utf8"));
108 assert.equal(log.text, "synthetic café");
109 assert.equal(log.consumed, log.text);
110 if (provider === "kokoro") assert.deepEqual(log.args.slice(-4), ["--lang", "es", "--speed", "0.8"]);
111 }
112 }
113 if (mode === "cleanup") assert.equal(owned().length, 1);
114 else assert.deepEqual(owned(), [], "owned text directories must be removed");
115 assert.equal(fs.readFileSync(join(preexisting, "marker"), "utf8"), "keep");
116 ` ;
117
118 function runCase ( provider , mode ) {
119 const root = mkdtempSync ( join ( tmpdir (), "tts-cleanup-test-" ));
120 try {
121 for ( const dir of [ "home" , "tmp" , "bin" , "project" ]) mkdirSync ( join (root, dir));
122 if (mode !== "enoent" ) {
123 for ( const name of [ "npx" , "python3" ]) {
124 const executable = join (root, "bin" , name);
125 writeFileSync (executable, stub);
126 chmodSync (executable, 0o755 );
127 }
128 }
129 const result = spawnSync (process.execPath, [ "--input-type=module" , "-e" , exercise], {
130 encoding: "utf8" ,
131 timeout: 15000 ,
132 env: {
133 HOME: join (root, "home" ),
134 USERPROFILE: join (root, "home" ),
135 TMPDIR: join (root, "tmp" ),
136 TMP: join (root, "tmp" ),
137 TEMP: join (root, "tmp" ),
138 PATH: join (root, "bin" ),
139 CASE_ROOT: root,
140 CASE_PROVIDER: provider,
141 CASE_MODE: mode,
142 RELEASE_FILE: join (root, "release" ),
143 },
144 });
145 assert. equal (result.error, undefined );
146 assert. equal (result.status, 0 , result.stdout + result.stderr);
147 } finally {
148 rmSync (root, { recursive: true , force: true });
149 }
150 }
151
152 for ( const provider of [ "elevenlabs" , "kokoro" ]) {
153 for ( const mode of [
154 "success" ,
155 "nonzero" ,
156 "no-output" ,
157 "enoent" ,
158 "spawn-throw" ,
159 "allocation" ,
160 "write" ,
161 "cleanup" ,
162 ]) {
163 test (
164 `synthesizeOne(${ provider }) preserves ${ mode } behavior and cleans only its text directory` ,
165 { skip: process.platform === "win32" },
166 () => runCase (provider, mode),
167 );
168 }
169 }
170
171 test (
172 "concurrent providers retain each other's text until their subprocess finishes" ,
173 { skip: process.platform === "win32" },
174 () => runCase ( "kokoro" , "concurrent" ),
175 );