@torrent-tv/proxy 2.89.16 → 2.89.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -3
- package/package.json +10 -2
- package/routes/api/client-logs/post.js +9 -2
- package/server.js +0 -11
- package/.claude/commands/opsx/apply.md +0 -155
- package/.claude/commands/opsx/archive.md +0 -160
- package/.claude/commands/opsx/explore.md +0 -174
- package/.claude/commands/opsx/propose.md +0 -109
- package/.claude/commands/opsx/sync.md +0 -143
- package/.claude/skills/openspec-apply-change/SKILL.md +0 -159
- package/.claude/skills/openspec-archive-change/SKILL.md +0 -117
- package/.claude/skills/openspec-explore/SKILL.md +0 -289
- package/.claude/skills/openspec-propose/SKILL.md +0 -113
- package/.claude/skills/openspec-sync-specs/SKILL.md +0 -147
- package/.dockerignore +0 -5
- package/AGENTS.md +0 -363
- package/CHANGELOG.md +0 -2373
- package/Dockerfile +0 -30
- package/biome.json +0 -412
- package/corpus.tmp.json +0 -1
- package/docs/browser-proxy-contract.md +0 -155
- package/docs/container-architecture.md +0 -273
- package/docs/download-architecture.md +0 -261
- package/docs/encode-architecture.md +0 -765
- package/docs/encode-run-state.md +0 -96
- package/docs/logs.md +0 -138
- package/docs/playback-start-forecast.md +0 -113
- package/docs/socks5-egress-research.md +0 -114
- package/docs/storage-architecture.md +0 -273
- package/knip.json +0 -14
- package/main2.cpuprofile +0 -1
- package/openspec/changes/adaptive-bitrate/.openspec.yaml +0 -0
- package/openspec/changes/adaptive-bitrate/design.md +0 -92
- package/openspec/changes/adaptive-bitrate/proposal.md +0 -56
- package/openspec/changes/adaptive-bitrate/specs/adaptive-bitrate/spec.md +0 -51
- package/openspec/changes/adaptive-bitrate/tasks.md +0 -40
- package/openspec/changes/binary-distribution/.openspec.yaml +0 -2
- package/openspec/changes/binary-distribution/proposal.md +0 -53
- package/openspec/changes/chunked-request-bodies/design.md +0 -81
- package/openspec/changes/chunked-request-bodies/proposal.md +0 -53
- package/openspec/changes/chunked-request-bodies/specs/chunked-request-bodies/spec.md +0 -30
- package/openspec/changes/chunked-request-bodies/tasks.md +0 -40
- package/openspec/changes/cold-start/design.md +0 -98
- package/openspec/changes/cold-start/proposal.md +0 -62
- package/openspec/changes/cold-start/specs/cold-start/spec.md +0 -47
- package/openspec/changes/cold-start/tasks.md +0 -48
- package/openspec/changes/disk-cap/.openspec.yaml +0 -2
- package/openspec/changes/disk-cap/proposal.md +0 -37
- package/openspec/changes/disk-cap/specs/disk-cap/spec.md +0 -25
- package/openspec/changes/disk-cap/tasks.md +0 -19
- package/openspec/changes/proxy-observability/.openspec.yaml +0 -2
- package/openspec/changes/proxy-observability/design.md +0 -38
- package/openspec/changes/proxy-observability/proposal.md +0 -49
- package/openspec/changes/proxy-observability/specs/observability/spec.md +0 -33
- package/openspec/changes/proxy-observability/tasks.md +0 -21
- package/openspec/changes/seek-debounce/.openspec.yaml +0 -0
- package/openspec/changes/seek-debounce/design.md +0 -118
- package/openspec/changes/seek-debounce/proposal.md +0 -59
- package/openspec/changes/seek-debounce/specs/seek-debounce/spec.md +0 -41
- package/openspec/changes/seek-debounce/tasks.md +0 -33
- package/openspec/changes/subtitle-language/.openspec.yaml +0 -2
- package/openspec/changes/subtitle-language/proposal.md +0 -50
- package/openspec/changes/subtitle-language/specs/subtitle-language/spec.md +0 -34
- package/openspec/changes/subtitle-language/tasks.md +0 -26
- package/openspec/changes/track-selection/.openspec.yaml +0 -2
- package/openspec/changes/track-selection/design.md +0 -47
- package/openspec/changes/track-selection/proposal.md +0 -45
- package/openspec/changes/track-selection/specs/track-selection/spec.md +0 -41
- package/openspec/changes/track-selection/tasks.md +0 -20
- package/openspec/changes/transcode-quality/.openspec.yaml +0 -2
- package/openspec/changes/transcode-quality/proposal.md +0 -51
- package/openspec/changes/transcode-quality/specs/transcode-quality/spec.md +0 -139
- package/openspec/changes/transcode-quality/tasks.md +0 -63
- package/openspec/config.yaml +0 -38
- package/public/.well-known/appspecific/com.chrome.devtools.json +0 -1
- package/research/delivery-wedge-stand-ladder-2026-09-13.md +0 -329
- package/research/double-spawn-2026-09-10.md +0 -171
- package/research/handover-2026-09-13.md +0 -209
- package/research/handover-reader-claims-removal-2026-09-12.md +0 -166
- package/research/piece-withdrawn-but-still-claimed-2026-09-12.md +0 -175
- package/research/preset-benchmark-size-scaling-2026-08-22.md +0 -55
- package/research/priority-map-is-the-truth-2026-09-12.md +0 -225
- package/research/valgrind-replica-2026-08-27/logs.tar.gz +0 -0
- package/scripts/build-audio-samples.mjs +0 -31
- package/scripts/render-run-graph.js +0 -173
- package/test/asset-paths.test.js +0 -28
- package/test/audio-buffer-continuity.test.js +0 -77
- package/test/audio-calibration.test.js +0 -23
- package/test/audio-inventory.test.js +0 -212
- package/test/audio-track-bitrate.test.js +0 -40
- package/test/audio-warm-recorded.test.js +0 -63
- package/test/auto-quality-step.test.js +0 -762
- package/test/available-share.test.js +0 -76
- package/test/background-fill.test.js +0 -190
- package/test/client-log-file.test.js +0 -126
- package/test/coalesce.test.js +0 -39
- package/test/completed-files.test.js +0 -115
- package/test/concurrent-cost.test.js +0 -148
- package/test/container-media-info.test.js +0 -228
- package/test/contention.test.js +0 -76
- package/test/core-dumps.test.js +0 -57
- package/test/coverage-follows-the-disk.test.js +0 -233
- package/test/coverage-map.test.js +0 -195
- package/test/cue-push-carries-what-the-walk-found.test.js +0 -58
- package/test/cut-times-timeline.test.js +0 -65
- package/test/cuts-follow-published-grid.test.js +0 -109
- package/test/data-channel-frame.test.js +0 -63
- package/test/data-channel-viewer-lifecycle.test.js +0 -193
- package/test/dead-torrent-handle.test.js +0 -52
- package/test/declared-rates.test.js +0 -116
- package/test/declared-tracks.test.js +0 -43
- package/test/decode-cost-fit.test.js +0 -202
- package/test/decode-cost.test.js +0 -475
- package/test/delivery-probe-field.test.js +0 -173
- package/test/delivery-probe.test.js +0 -382
- package/test/demand-register.test.js +0 -206
- package/test/dht-bootstrap.test.js +0 -72
- package/test/drain-threat.test.js +0 -65
- package/test/duplicate-add.test.js +0 -48
- package/test/encode-admission.test.js +0 -170
- package/test/encode-cost.test.js +0 -275
- package/test/encode-exit.test.js +0 -111
- package/test/encode-orchestrator.test.js +0 -426
- package/test/encode-plan-viewers.test.js +0 -725
- package/test/encode-plan.test.js +0 -545
- package/test/encode-run-state.test.js +0 -314
- package/test/encode-run.test.js +0 -248
- package/test/encode-slope.test.js +0 -46
- package/test/encoded-output.test.js +0 -39
- package/test/encoder-kinds.test.js +0 -122
- package/test/encoder-readings.test.js +0 -68
- package/test/episode-naming.test.js +0 -131
- package/test/every-claimant-is-told-its-share.test.js +0 -152
- package/test/failed-encoder-still-serves-what-exists.test.js +0 -354
- package/test/failed-start-breaker.test.js +0 -202
- package/test/fastest-wires.test.js +0 -143
- package/test/ffmpeg-stream-counts.test.js +0 -101
- package/test/file-edges.test.js +0 -191
- package/test/files-in-use.test.js +0 -34
- package/test/fixtures/audio-frame-boundary.json +0 -1
- package/test/fixtures/refused-audio-20261002.json +0 -10
- package/test/fmp4-presentation-clock.test.js +0 -148
- package/test/fmp4-self-contained.test.js +0 -80
- package/test/fmp4-timestamps.test.js +0 -140
- package/test/fmp4-track-completeness.test.js +0 -41
- package/test/format-keyed-outputs.test.js +0 -78
- package/test/health-metrics.test.js +0 -36
- package/test/held-read-ownership.test.js +0 -77
- package/test/held-request-width.test.js +0 -157
- package/test/helpers/encode-run.js +0 -248
- package/test/helpers/held-file.js +0 -77
- package/test/helpers/manager.js +0 -62
- package/test/helpers/matroska-file.js +0 -296
- package/test/helpers/output-spec.js +0 -63
- package/test/host-load.test.js +0 -68
- package/test/index-accuracy-is-about-the-file.test.js +0 -128
- package/test/init-compat.test.js +0 -219
- package/test/input-lost-quiets-the-plan.test.js +0 -261
- package/test/input-unavailable.test.js +0 -48
- package/test/installation-id.test.js +0 -39
- package/test/keeping-period.test.js +0 -83
- package/test/keyframe-index-accuracy.test.js +0 -188
- package/test/keyframe-table-budget.test.js +0 -123
- package/test/keyframes-belong-to-the-file.test.js +0 -249
- package/test/learned-median.test.js +0 -37
- package/test/letting-go-asks-who-is-watching.test.js +0 -191
- package/test/limit-row-by-area.test.js +0 -60
- package/test/link-budget.test.js +0 -111
- package/test/link-probe.test.js +0 -56
- package/test/local-encoding-data.test.js +0 -273
- package/test/logger-repeats.test.js +0 -134
- package/test/look-ahead-coverage.test.js +0 -56
- package/test/manager-surface.test.js +0 -291
- package/test/matroska-blocks.test.js +0 -0
- package/test/matroska-cluster-discovery.test.js +0 -323
- package/test/matroska-cues-track.test.js +0 -192
- package/test/media-time.test.js +0 -62
- package/test/memory-budget.test.js +0 -390
- package/test/move-cost.test.js +0 -216
- package/test/mp4-composition-times.test.js +0 -0
- package/test/mp4-subtitles.test.js +0 -173
- package/test/one-authority.test.js +0 -285
- package/test/one-budget.test.js +0 -149
- package/test/one-output-one-session.test.js +0 -273
- package/test/one-resource-divided.test.js +0 -185
- package/test/one-serving-output-per-height.test.js +0 -458
- package/test/orchestrator-wired.test.js +0 -207
- package/test/output-catalog.test.js +0 -39
- package/test/output-shape.test.js +0 -21
- package/test/output-spec.test.js +0 -251
- package/test/output-speed.test.js +0 -92
- package/test/outputs-being-prepared.test.js +0 -40
- package/test/packet-witness-ring.test.js +0 -236
- package/test/packet-witness.test.js +0 -148
- package/test/piece-completeness.test.js +0 -78
- package/test/piece-disk-store.test.js +0 -401
- package/test/piece-from-whole-file.test.js +0 -129
- package/test/piece-lru.test.js +0 -319
- package/test/piece-reader.test.js +0 -162
- package/test/piece-selection-offset.test.js +0 -85
- package/test/piece-store-eviction.test.js +0 -595
- package/test/piece-store-never-refuses.test.js +0 -153
- package/test/piece-store-reservations.test.js +0 -403
- package/test/piece-store-slow-disk.test.js +0 -159
- package/test/piece-tail.test.js +0 -97
- package/test/plan-host-timings.test.js +0 -68
- package/test/plan-is-steady.test.js +0 -225
- package/test/playback-readiness.test.js +0 -518
- package/test/playback-start-regressions.test.js +0 -101
- package/test/priority-map-download.test.js +0 -174
- package/test/priority-map-emptied.test.js +0 -204
- package/test/priority-map-per-output.test.js +0 -215
- package/test/priority-map.test.js +0 -163
- package/test/probe-wedge-certainty.test.js +0 -60
- package/test/produced-copy-choice.test.js +0 -213
- package/test/published-timeline.test.js +0 -83
- package/test/quality-offer.test.js +0 -160
- package/test/quality-variants.test.js +0 -1274
- package/test/rate-control-in-the-output.test.js +0 -181
- package/test/read-survives-withdrawal.test.js +0 -164
- package/test/read-window.test.js +0 -365
- package/test/reads-not-yet-arrived.test.js +0 -218
- package/test/resume-warm.test.js +0 -39
- package/test/run-graph-drift.test.js +0 -37
- package/test/run-inputs.test.js +0 -80
- package/test/run-intervals.test.js +0 -100
- package/test/run-position-follows-published-grid.test.js +0 -89
- package/test/run-progress.test.js +0 -64
- package/test/same-height-switch.test.js +0 -663
- package/test/seek-frees-the-viewers-requests.test.js +0 -217
- package/test/seek-landing.test.js +0 -109
- package/test/seek-target-not-superseded.test.js +0 -195
- package/test/segment-closed-on-both-branches.test.js +0 -114
- package/test/segment-cut-times.test.js +0 -63
- package/test/segment-demand.test.js +0 -80
- package/test/segment-hold-supersede.test.js +0 -91
- package/test/segment-judged-by-its-own-header.test.js +0 -222
- package/test/segment-serve-wiring.test.js +0 -481
- package/test/segment-store-eviction.test.js +0 -247
- package/test/segment-store.test.js +0 -352
- package/test/segment-true-start.test.js +0 -86
- package/test/segments-are-shared.test.js +0 -176
- package/test/send-chunk-bytes.test.js +0 -86
- package/test/serving-existing-output.test.js +0 -111
- package/test/serving-output.test.js +0 -102
- package/test/shared-piece-store.test.js +0 -310
- package/test/sidecar-naming.test.js +0 -142
- package/test/soundtrack-rate-bound.test.js +0 -289
- package/test/source-file.test.js +0 -133
- package/test/source-files-route.test.js +0 -113
- package/test/source-registry.test.js +0 -18
- package/test/split-a-stretch.test.js +0 -139
- package/test/stale-request-after-seek.test.js +0 -158
- package/test/startup-readings.test.js +0 -124
- package/test/stream-route.test.js +0 -237
- package/test/subtitle-cue-framing.test.js +0 -200
- package/test/subtitle-cue-source.test.js +0 -102
- package/test/subtitle-cue-walk.test.js +0 -385
- package/test/subtitle-cursor.test.js +0 -63
- package/test/subtitle-defaults.test.js +0 -118
- package/test/subtitle-language.test.js +0 -252
- package/test/subtitle-metadata.test.js +0 -15
- package/test/subtitle-plan-waits.test.js +0 -125
- package/test/subtitle-track-numbering.test.js +0 -391
- package/test/supply-margin.test.js +0 -190
- package/test/swarm-follows-readers.test.js +0 -174
- package/test/swarm-reach.test.js +0 -130
- package/test/swarm-selection.test.js +0 -220
- package/test/tail-duplication.test.js +0 -167
- package/test/the-channel-has-both-ends.test.js +0 -91
- package/test/the-torrent-thread-serves-bytes.test.js +0 -107
- package/test/thread-pool.test.js +0 -53
- package/test/timeline.test.js +0 -84
- package/test/torrent-contents.test.js +0 -235
- package/test/torrent-cost.test.js +0 -71
- package/test/torrent-files.test.js +0 -221
- package/test/torrent-naming.test.js +0 -255
- package/test/tracks-begin-together.test.js +0 -209
- package/test/tunnel-renewal.test.js +0 -167
- package/test/two-viewers-one-picture.test.js +0 -508
- package/test/upload-hurry.test.js +0 -186
- package/test/usrsctp-state.test.js +0 -131
- package/test/video-facts.test.js +0 -102
- package/test/viewer-assignments.test.js +0 -143
- package/test/viewer-outputs.test.js +0 -285
- package/test/viewer-presence.test.js +0 -116
- package/test/viewer-requests-admission.test.js +0 -92
- package/test/viewer-starts-on-map.test.js +0 -27
- package/test/viewer-states.test.js +0 -192
- package/test/viewer-subtitle-subscription.test.js +0 -96
- package/test/viewer.test.js +0 -139
- package/test/viewing-generations.test.js +0 -390
- package/test/visible-rung.test.js +0 -37
- package/test/wait-ledger.test.js +0 -100
- package/test/webrtc-shim.test.js +0 -149
- package/test/wedge-certainty.test.js +0 -131
- package/test/whole-file-held-read.test.js +0 -30
- package/test/withdraw-piece-claim.test.js +0 -212
- package/test/worker-channel.test.js +0 -158
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file A rung is judged against the machine it will actually have.
|
|
3
|
-
*
|
|
4
|
-
* The field case of 2026-08-15, in arithmetic: a copy of the picture took about
|
|
5
|
-
* 0.125 s of work per second of video (7.9-8.0x measured), a 240p rung needed
|
|
6
|
-
* about 1.05, and the two together are more than the one second per second the
|
|
7
|
-
* machine has. The budget priced the copy at nothing, offered the rung, and the
|
|
8
|
-
* viewer watched it fail.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import assert from "node:assert/strict";
|
|
12
|
-
import test from "node:test";
|
|
13
|
-
|
|
14
|
-
import { canSustainOutput } from "../services/encode/hwaccel.js";
|
|
15
|
-
|
|
16
|
-
// A host that encodes 640x360 at 24 fps about eleven times over, and decodes
|
|
17
|
-
// 1080p24 at 2.6x — the addon host's own figures.
|
|
18
|
-
const BENCHMARK = [
|
|
19
|
-
{ preset: "fast", pixelsPerSec: 17.0e6 },
|
|
20
|
-
{ preset: "ultrafast", pixelsPerSec: 67.5e6 }
|
|
21
|
-
];
|
|
22
|
-
const DECODE_MODEL = { pixelTerm: 0.007742, bitrateTerm: 0, constantTerm: 0 };
|
|
23
|
-
const SOURCE = { megapixelsPerSecond: (1920 * 1080 * 24) / 1e6, megabitsPerSecond: 8 };
|
|
24
|
-
const RUNG_240P = 426 * 240 * 24;
|
|
25
|
-
|
|
26
|
-
test("a rung is judged on the machine it will have, not on an empty one", () => {
|
|
27
|
-
const alone = canSustainOutput({
|
|
28
|
-
benchmark: BENCHMARK,
|
|
29
|
-
decodeModel: DECODE_MODEL,
|
|
30
|
-
source: SOURCE,
|
|
31
|
-
outputPixelsPerSec: RUNG_240P
|
|
32
|
-
});
|
|
33
|
-
const beside = canSustainOutput({
|
|
34
|
-
benchmark: BENCHMARK,
|
|
35
|
-
decodeModel: DECODE_MODEL,
|
|
36
|
-
source: SOURCE,
|
|
37
|
-
outputPixelsPerSec: RUNG_240P,
|
|
38
|
-
// The picture is being copied beside it, at the cost the field measured.
|
|
39
|
-
concurrentCostSec: 0.125
|
|
40
|
-
});
|
|
41
|
-
assert.ok(alone.speed !== null && beside.speed !== null);
|
|
42
|
-
assert.ok(beside.speed < alone.speed, "sharing the machine cannot make a rung faster");
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
test("nothing else running leaves the answer exactly as it was", () => {
|
|
46
|
-
const withZero = canSustainOutput({
|
|
47
|
-
benchmark: BENCHMARK,
|
|
48
|
-
decodeModel: DECODE_MODEL,
|
|
49
|
-
source: SOURCE,
|
|
50
|
-
outputPixelsPerSec: RUNG_240P,
|
|
51
|
-
concurrentCostSec: 0
|
|
52
|
-
});
|
|
53
|
-
const without = canSustainOutput({
|
|
54
|
-
benchmark: BENCHMARK,
|
|
55
|
-
decodeModel: DECODE_MODEL,
|
|
56
|
-
source: SOURCE,
|
|
57
|
-
outputPixelsPerSec: RUNG_240P
|
|
58
|
-
});
|
|
59
|
-
assert.equal(withZero.speed, without.speed);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
test("a wiring made without the startup calibration judges nothing", () => {
|
|
63
|
-
const answer = canSustainOutput({
|
|
64
|
-
benchmark: null,
|
|
65
|
-
outputPixelsPerSec: RUNG_240P,
|
|
66
|
-
concurrentCostSec: 0.125
|
|
67
|
-
});
|
|
68
|
-
assert.equal(answer.sustainable, true);
|
|
69
|
-
assert.equal(answer.speed, null);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
test("a host calibrated with no mode qualified refuses a re-encode (roadmap item 97, step 14)", () => {
|
|
73
|
-
const answer = canSustainOutput({
|
|
74
|
-
benchmark: [],
|
|
75
|
-
outputPixelsPerSec: RUNG_240P,
|
|
76
|
-
concurrentCostSec: 0.125
|
|
77
|
-
});
|
|
78
|
-
assert.equal(answer.sustainable, false, "unknown capacity is not capacity");
|
|
79
|
-
assert.equal(answer.speed, null);
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
test("a cost heavy enough to fill the machine puts a rung below realtime", () => {
|
|
83
|
-
const answer = canSustainOutput({
|
|
84
|
-
benchmark: BENCHMARK,
|
|
85
|
-
decodeModel: DECODE_MODEL,
|
|
86
|
-
source: SOURCE,
|
|
87
|
-
outputPixelsPerSec: RUNG_240P,
|
|
88
|
-
concurrentCostSec: 0.9
|
|
89
|
-
});
|
|
90
|
-
assert.ok(answer.speed !== null && answer.speed < 1, "0.9 of the machine spent elsewhere leaves less than realtime");
|
|
91
|
-
assert.equal(answer.sustainable, false);
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
test("a soundtrack encoder is not free once it has been measured", () => {
|
|
95
|
-
// The second half of roadmap item 6. An audio rendition runs for as long as
|
|
96
|
-
// the picture does — it is a second encoder by construction, not an extra —
|
|
97
|
-
// and the budget counted it at nothing. Small beside a picture, and small is
|
|
98
|
-
// not zero: on a host where a rung needs almost the whole machine, this is
|
|
99
|
-
// the difference between offering it and refusing it.
|
|
100
|
-
const withoutAudio = canSustainOutput({
|
|
101
|
-
benchmark: BENCHMARK,
|
|
102
|
-
decodeModel: DECODE_MODEL,
|
|
103
|
-
source: SOURCE,
|
|
104
|
-
outputPixelsPerSec: RUNG_240P,
|
|
105
|
-
concurrentCostSec: 0.125
|
|
106
|
-
});
|
|
107
|
-
const withAudio = canSustainOutput({
|
|
108
|
-
benchmark: BENCHMARK,
|
|
109
|
-
decodeModel: DECODE_MODEL,
|
|
110
|
-
source: SOURCE,
|
|
111
|
-
outputPixelsPerSec: RUNG_240P,
|
|
112
|
-
// The copy, plus a soundtrack measured at about twenty times realtime.
|
|
113
|
-
concurrentCostSec: 0.125 + 0.05
|
|
114
|
-
});
|
|
115
|
-
assert.ok(
|
|
116
|
-
withAudio.speed < withoutAudio.speed,
|
|
117
|
-
"charging for the soundtrack must lower what the machine has left for the picture"
|
|
118
|
-
);
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
test("two pictures encoding at once are charged as two", () => {
|
|
122
|
-
// The warm-up that makes a quality switch seamless runs two encoders on
|
|
123
|
-
// purpose. Measured 2026-08-15: the new rung ran at 0.504x for its first four
|
|
124
|
-
// seconds, against 0.90-0.99x once it had the machine to itself and 1.58x
|
|
125
|
-
// predicted for an idle one. Priced as though it were alone, the budget
|
|
126
|
-
// describes a machine that does not exist at the moment the viewer switches.
|
|
127
|
-
const alone = canSustainOutput({
|
|
128
|
-
benchmark: BENCHMARK,
|
|
129
|
-
decodeModel: DECODE_MODEL,
|
|
130
|
-
source: SOURCE,
|
|
131
|
-
outputPixelsPerSec: RUNG_240P,
|
|
132
|
-
concurrentCostSec: 0
|
|
133
|
-
});
|
|
134
|
-
const besideAnother = canSustainOutput({
|
|
135
|
-
benchmark: BENCHMARK,
|
|
136
|
-
decodeModel: DECODE_MODEL,
|
|
137
|
-
source: SOURCE,
|
|
138
|
-
outputPixelsPerSec: RUNG_240P,
|
|
139
|
-
// Another rung of the same family, already running at about realtime.
|
|
140
|
-
concurrentCostSec: 1 / 1.05
|
|
141
|
-
});
|
|
142
|
-
assert.ok(alone.sustainable, "the rung must be offerable on an idle machine, or the case is not the one meant");
|
|
143
|
-
assert.equal(
|
|
144
|
-
besideAnother.sustainable,
|
|
145
|
-
false,
|
|
146
|
-
"a machine already spending a second per second of video has nothing left for a second picture"
|
|
147
|
-
);
|
|
148
|
-
});
|
|
@@ -1,228 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file What a file declares about ITSELF — format, duration, and where its own
|
|
3
|
-
* timeline begins — read from its header by the container layer.
|
|
4
|
-
*
|
|
5
|
-
* These fixtures are built byte by byte rather than produced by ffmpeg, on
|
|
6
|
-
* purpose: a test that runs a real encoder measures the machine it runs on, and
|
|
7
|
-
* two such tests in this suite have failed four times in one day for exactly
|
|
8
|
-
* that reason (roadmap item 54). The numbers here are checked against ffmpeg
|
|
9
|
-
* ONCE, by hand, and the result is recorded rather than re-measured on every
|
|
10
|
-
* run — 2026-09-03, a Matroska file offset by 0.130435 s: ffmpeg reported
|
|
11
|
-
* `Duration: 00:00:02.13, start: 0.130000` and this reader answered
|
|
12
|
-
* `durationSeconds 2.131, startTimeSeconds 0.13`, which is the same number at
|
|
13
|
-
* the precision each prints. The same file as MP4: `start: 0.000000` from
|
|
14
|
-
* ffmpeg, 0 from this reader.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
import assert from "node:assert/strict";
|
|
18
|
-
import test from "node:test";
|
|
19
|
-
|
|
20
|
-
import { MatroskaContainer } from "../services/media/container/MatroskaContainer.js";
|
|
21
|
-
import { Mp4Container } from "../services/media/container/Mp4Container.js";
|
|
22
|
-
import { AviContainer } from "../services/media/container/AviContainer.js";
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* An EBML element: its id bytes, a four-byte size, then the payload.
|
|
26
|
-
*
|
|
27
|
-
* @param {number[]} idBytes
|
|
28
|
-
* @param {Buffer} payload
|
|
29
|
-
* @returns {Buffer}
|
|
30
|
-
*/
|
|
31
|
-
function ebml(idBytes, payload) {
|
|
32
|
-
const size = Buffer.alloc(4);
|
|
33
|
-
// Four-byte size form: `0001xxxx` in the leading byte marks the width.
|
|
34
|
-
size.writeUInt32BE(payload.length);
|
|
35
|
-
size[0] |= 0x10;
|
|
36
|
-
return Buffer.concat([Buffer.from(idBytes), size, payload]);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/** @param {number} value @param {number} bytes @returns {Buffer} */
|
|
40
|
-
function uint(value, bytes) {
|
|
41
|
-
const out = Buffer.alloc(bytes);
|
|
42
|
-
out.writeUIntBE(value, 0, bytes);
|
|
43
|
-
return out;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/** @param {number} value @returns {Buffer} */
|
|
47
|
-
function float64(value) {
|
|
48
|
-
const out = Buffer.alloc(8);
|
|
49
|
-
out.writeDoubleBE(value);
|
|
50
|
-
return out;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const ID_EBML_HEADER = [0x1a, 0x45, 0xdf, 0xa3];
|
|
54
|
-
const ID_SEGMENT = [0x18, 0x53, 0x80, 0x67];
|
|
55
|
-
const ID_SEEK_HEAD = [0x11, 0x4d, 0x9b, 0x74];
|
|
56
|
-
const ID_SEEK = [0x4d, 0xbb];
|
|
57
|
-
const ID_SEEK_ID = [0x53, 0xab];
|
|
58
|
-
const ID_SEEK_POSITION = [0x53, 0xac];
|
|
59
|
-
const ID_INFO = [0x15, 0x49, 0xa9, 0x66];
|
|
60
|
-
const ID_TIMESTAMP_SCALE = [0x2a, 0xd7, 0xb1];
|
|
61
|
-
const ID_DURATION = [0x44, 0x89];
|
|
62
|
-
const ID_CLUSTER = [0x1f, 0x43, 0xb6, 0x75];
|
|
63
|
-
const ID_TIMESTAMP = [0xe7];
|
|
64
|
-
const ID_VOID = [0xec];
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* A reader over a buffer, in the shape the container layer takes.
|
|
68
|
-
*
|
|
69
|
-
* @param {Buffer} bytes
|
|
70
|
-
* @returns {(start: number, end: number) => Promise<Buffer>}
|
|
71
|
-
*/
|
|
72
|
-
function readerOver(bytes) {
|
|
73
|
-
return async (start, end) => bytes.subarray(start, Math.min(end + 1, bytes.length));
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
test("a Matroska file states its duration in ticks and its start in the first cluster", async () => {
|
|
77
|
-
const info = ebml(ID_INFO, Buffer.concat([
|
|
78
|
-
ebml(ID_TIMESTAMP_SCALE, uint(1_000_000, 3)),
|
|
79
|
-
// 2131 ticks of a millisecond each.
|
|
80
|
-
ebml(ID_DURATION, float64(2131))
|
|
81
|
-
]));
|
|
82
|
-
const cluster = ebml(ID_CLUSTER, ebml(ID_TIMESTAMP, uint(130, 1)));
|
|
83
|
-
const file = Buffer.concat([
|
|
84
|
-
ebml(ID_EBML_HEADER, Buffer.alloc(4)),
|
|
85
|
-
ebml(ID_SEGMENT, Buffer.concat([info, cluster]))
|
|
86
|
-
]);
|
|
87
|
-
|
|
88
|
-
const container = new MatroskaContainer({ readRange: readerOver(file), fileSize: file.length });
|
|
89
|
-
const read = await container.readMediaInfo();
|
|
90
|
-
|
|
91
|
-
assert.equal(read.format, "matroska");
|
|
92
|
-
assert.ok(Math.abs(read.durationSeconds - 2.131) < 1e-9, `duration was ${read.durationSeconds}`);
|
|
93
|
-
assert.ok(Math.abs(read.startTimeSeconds - 0.13) < 1e-9, `start was ${read.startTimeSeconds}`);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
test("a cluster past the head window is found through the SeekHead", async () => {
|
|
97
|
-
// Everything before the cluster is padded past the 64 KB the head read covers,
|
|
98
|
-
// which is the case this second path exists for: a file whose Tracks element
|
|
99
|
-
// is large enough to push the first cluster out of reach.
|
|
100
|
-
const info = ebml(ID_INFO, ebml(ID_TIMESTAMP_SCALE, uint(1_000_000, 3)));
|
|
101
|
-
const padding = ebml(ID_VOID, Buffer.alloc(70 * 1024));
|
|
102
|
-
const cluster = ebml(ID_CLUSTER, ebml(ID_TIMESTAMP, uint(2500, 2)));
|
|
103
|
-
// The SeekHead is written first, so its own length is known before the
|
|
104
|
-
// position it names can be computed — build it with a placeholder, measure,
|
|
105
|
-
// then write the real position.
|
|
106
|
-
const seekHeadFor = (position) => ebml(ID_SEEK_HEAD, ebml(ID_SEEK, Buffer.concat([
|
|
107
|
-
ebml(ID_SEEK_ID, Buffer.from(ID_CLUSTER)),
|
|
108
|
-
ebml(ID_SEEK_POSITION, uint(position, 4))
|
|
109
|
-
])));
|
|
110
|
-
const seekHeadLength = seekHeadFor(0).length;
|
|
111
|
-
const clusterPosition = seekHeadLength + info.length + padding.length;
|
|
112
|
-
const segmentPayload = Buffer.concat([seekHeadFor(clusterPosition), info, padding, cluster]);
|
|
113
|
-
const file = Buffer.concat([
|
|
114
|
-
ebml(ID_EBML_HEADER, Buffer.alloc(4)),
|
|
115
|
-
ebml(ID_SEGMENT, segmentPayload)
|
|
116
|
-
]);
|
|
117
|
-
|
|
118
|
-
const container = new MatroskaContainer({ readRange: readerOver(file), fileSize: file.length });
|
|
119
|
-
const read = await container.readMediaInfo();
|
|
120
|
-
|
|
121
|
-
assert.ok(Math.abs(read.startTimeSeconds - 2.5) < 1e-9, `start was ${read.startTimeSeconds}`);
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
test("a Matroska file that declares no duration says so, rather than saying zero", async () => {
|
|
125
|
-
const cluster = ebml(ID_CLUSTER, ebml(ID_TIMESTAMP, uint(0, 1)));
|
|
126
|
-
const file = Buffer.concat([
|
|
127
|
-
ebml(ID_EBML_HEADER, Buffer.alloc(4)),
|
|
128
|
-
ebml(ID_SEGMENT, cluster)
|
|
129
|
-
]);
|
|
130
|
-
|
|
131
|
-
const container = new MatroskaContainer({ readRange: readerOver(file), fileSize: file.length });
|
|
132
|
-
const read = await container.readMediaInfo();
|
|
133
|
-
|
|
134
|
-
assert.equal(read.durationSeconds, null);
|
|
135
|
-
assert.equal(read.startTimeSeconds, 0);
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* An ISO/IEC 14496-12 box.
|
|
140
|
-
*
|
|
141
|
-
* @param {string} type
|
|
142
|
-
* @param {Buffer} payload
|
|
143
|
-
* @returns {Buffer}
|
|
144
|
-
*/
|
|
145
|
-
function box(type, payload) {
|
|
146
|
-
const header = Buffer.alloc(8);
|
|
147
|
-
header.writeUInt32BE(payload.length + 8);
|
|
148
|
-
header.write(type, 4, "latin1");
|
|
149
|
-
return Buffer.concat([header, payload]);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
test("an MP4 states its duration in mvhd and its start in an empty edit", async () => {
|
|
153
|
-
const mvhd = box("mvhd", Buffer.concat([
|
|
154
|
-
Buffer.alloc(4), // version 0 + flags
|
|
155
|
-
Buffer.alloc(8), // creation, modification
|
|
156
|
-
uint(1000, 4), // timescale: ticks per second
|
|
157
|
-
uint(2000, 4), // duration: two seconds
|
|
158
|
-
Buffer.alloc(80)
|
|
159
|
-
]));
|
|
160
|
-
const elst = box("elst", Buffer.concat([
|
|
161
|
-
Buffer.alloc(4), // version 0 + flags
|
|
162
|
-
uint(1, 4), // one entry
|
|
163
|
-
uint(130, 4), // segment_duration: 0.130 s at the movie timescale
|
|
164
|
-
Buffer.from([0xff, 0xff, 0xff, 0xff]), // media_time -1: an EMPTY edit
|
|
165
|
-
uint(0x00010000, 4) // media_rate 1.0
|
|
166
|
-
]));
|
|
167
|
-
const trak = box("trak", box("edts", elst));
|
|
168
|
-
const file = Buffer.concat([
|
|
169
|
-
box("ftyp", Buffer.from("isom", "latin1")),
|
|
170
|
-
box("moov", Buffer.concat([mvhd, trak]))
|
|
171
|
-
]);
|
|
172
|
-
|
|
173
|
-
const container = new Mp4Container({ readRange: readerOver(file), fileSize: file.length });
|
|
174
|
-
const read = await container.readMediaInfo();
|
|
175
|
-
|
|
176
|
-
assert.equal(read.format, "mp4");
|
|
177
|
-
assert.ok(Math.abs(read.durationSeconds - 2) < 1e-9, `duration was ${read.durationSeconds}`);
|
|
178
|
-
assert.ok(Math.abs(read.startTimeSeconds - 0.13) < 1e-9, `start was ${read.startTimeSeconds}`);
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
test("an MP4 with no edit list begins at zero, and that is an answer", async () => {
|
|
182
|
-
const mvhd = box("mvhd", Buffer.concat([
|
|
183
|
-
Buffer.alloc(4),
|
|
184
|
-
Buffer.alloc(8),
|
|
185
|
-
uint(600, 4),
|
|
186
|
-
uint(1200, 4),
|
|
187
|
-
Buffer.alloc(80)
|
|
188
|
-
]));
|
|
189
|
-
const file = Buffer.concat([
|
|
190
|
-
box("ftyp", Buffer.from("isom", "latin1")),
|
|
191
|
-
box("moov", mvhd)
|
|
192
|
-
]);
|
|
193
|
-
|
|
194
|
-
const container = new Mp4Container({ readRange: readerOver(file), fileSize: file.length });
|
|
195
|
-
const read = await container.readMediaInfo();
|
|
196
|
-
|
|
197
|
-
assert.ok(Math.abs(read.durationSeconds - 2) < 1e-9, `duration was ${read.durationSeconds}`);
|
|
198
|
-
assert.equal(read.startTimeSeconds, 0);
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
test("an AVI states its length as microseconds per frame times the frame count", async () => {
|
|
202
|
-
const avih = Buffer.concat([
|
|
203
|
-
Buffer.from("avih", "latin1"),
|
|
204
|
-
uint(56, 4),
|
|
205
|
-
Buffer.from(new Uint8Array(new Uint32Array([
|
|
206
|
-
40_000, // dwMicroSecPerFrame: 25 fps
|
|
207
|
-
0, 0, 0,
|
|
208
|
-
50 // dwTotalFrames: two seconds of them
|
|
209
|
-
]).buffer)),
|
|
210
|
-
Buffer.alloc(36)
|
|
211
|
-
]);
|
|
212
|
-
const file = Buffer.concat([
|
|
213
|
-
Buffer.from("RIFF", "latin1"),
|
|
214
|
-
uint(0, 4),
|
|
215
|
-
Buffer.from("AVI ", "latin1"),
|
|
216
|
-
Buffer.from("LIST", "latin1"),
|
|
217
|
-
uint(avih.length + 4, 4),
|
|
218
|
-
Buffer.from("hdrl", "latin1"),
|
|
219
|
-
avih
|
|
220
|
-
]);
|
|
221
|
-
|
|
222
|
-
const container = new AviContainer({ readRange: readerOver(file), fileSize: file.length });
|
|
223
|
-
const read = await container.readMediaInfo();
|
|
224
|
-
|
|
225
|
-
assert.equal(read.format, "avi");
|
|
226
|
-
assert.ok(Math.abs(read.durationSeconds - 2) < 1e-9, `duration was ${read.durationSeconds}`);
|
|
227
|
-
assert.equal(read.startTimeSeconds, 0);
|
|
228
|
-
});
|
package/test/contention.test.js
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file The price of a second job, with the readings it was derived from.
|
|
3
|
-
*
|
|
4
|
-
* Measured on the addon host 2026-08-18: decoding the same clip ran at 2.20x
|
|
5
|
-
* alone, 0.85x with one encoder beside it and 0.60x with two. The budget has
|
|
6
|
-
* been adding independent prices, and these say two jobs that each fit alone do
|
|
7
|
-
* not fit together.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import assert from "node:assert/strict";
|
|
11
|
-
import test from "node:test";
|
|
12
|
-
|
|
13
|
-
import { contentionPenalty, costWithContention, penaltiesFrom } from "../services/encode/contention.js";
|
|
14
|
-
|
|
15
|
-
/** The addon host's own readings. */
|
|
16
|
-
const ALONE = 2.2;
|
|
17
|
-
const BESIDE = [
|
|
18
|
-
{ others: 1, speed: 0.85 },
|
|
19
|
-
{ others: 2, speed: 0.6 }
|
|
20
|
-
];
|
|
21
|
-
|
|
22
|
-
test("the penalties are the ratios of the measured speeds", () => {
|
|
23
|
-
const penalties = penaltiesFrom(ALONE, BESIDE);
|
|
24
|
-
|
|
25
|
-
assert.ok(penalties);
|
|
26
|
-
// 2.2 / 0.85 = 2.59: the same work costs two and a half times more with one
|
|
27
|
-
// encoder beside it.
|
|
28
|
-
assert.ok(Math.abs(penalties.get(1) - 2.588) < 0.01, `${penalties.get(1)}`);
|
|
29
|
-
assert.ok(Math.abs(penalties.get(2) - 3.667) < 0.01, `${penalties.get(2)}`);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
test("alone on the machine is what the benchmarks measure, so nothing is corrected", () => {
|
|
33
|
-
const penalties = penaltiesFrom(ALONE, BESIDE);
|
|
34
|
-
const answer = contentionPenalty(0, penalties);
|
|
35
|
-
assert.equal(answer.penalty, 1);
|
|
36
|
-
assert.equal(answer.measured, true);
|
|
37
|
-
assert.equal(costWithContention(0.45, 0, penalties), 0.45);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
test("a cost beside one other job is the measured multiple, not the sum of two prices", () => {
|
|
41
|
-
const penalties = penaltiesFrom(ALONE, BESIDE);
|
|
42
|
-
// Decoding priced at 0.45 s/s alone becomes 1.17 s/s beside an encoder —
|
|
43
|
-
// which is what the field measured (1.18), and what additivity cannot say.
|
|
44
|
-
const corrected = costWithContention(0.45, 1, penalties);
|
|
45
|
-
assert.ok(Math.abs(corrected - 1.165) < 0.01, `${corrected}`);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
test("beyond the readings it holds the largest instead of extrapolating", () => {
|
|
49
|
-
const penalties = penaltiesFrom(ALONE, BESIDE);
|
|
50
|
-
const four = contentionPenalty(4, penalties);
|
|
51
|
-
assert.equal(four.penalty, penalties.get(2), "two readings say nothing about a fourth job");
|
|
52
|
-
assert.equal(four.from, 2, "and the answer says which reading it came from");
|
|
53
|
-
assert.equal(four.measured, true);
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
test("with nothing measured the prediction is left alone, not guessed at", () => {
|
|
57
|
-
const answer = contentionPenalty(1, null);
|
|
58
|
-
assert.equal(answer.penalty, 1);
|
|
59
|
-
assert.equal(answer.measured, false, "the caller must be able to say the figure is uncorrected");
|
|
60
|
-
assert.equal(costWithContention(0.45, 1, null), 0.45);
|
|
61
|
-
assert.equal(contentionPenalty(1, new Map()).measured, false);
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
test("a machine that got faster for being busier has no penalty to measure", () => {
|
|
65
|
-
// Noise on a fast host, not a discovery. Measured on the developer's desktop,
|
|
66
|
-
// the difference between one and two jobs is inside the scatter.
|
|
67
|
-
const penalties = penaltiesFrom(20, [{ others: 1, speed: 21 }]);
|
|
68
|
-
assert.equal(penalties.get(1), 1);
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
test("readings that are not measurements are left out", () => {
|
|
72
|
-
assert.equal(penaltiesFrom(0, BESIDE), null, "every penalty is relative to the alone reading");
|
|
73
|
-
assert.equal(penaltiesFrom(ALONE, []), null);
|
|
74
|
-
assert.equal(penaltiesFrom(ALONE, [{ others: 0, speed: 2 }]), null, "zero others is not a penalty");
|
|
75
|
-
assert.equal(penaltiesFrom(ALONE, [{ others: 1, speed: Number.NaN }]), null);
|
|
76
|
-
});
|
package/test/core-dumps.test.js
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file Keep the last few core dumps and no more.
|
|
3
|
-
*
|
|
4
|
-
* Each is the worker thread's whole address space — 4.18 GB on the field host —
|
|
5
|
-
* and four of them had nearly filled a 235 GB disk by 2026-08-21. The newest
|
|
6
|
-
* are evidence for a fault that is still open, so they stay; the rest go.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import assert from "node:assert/strict";
|
|
10
|
-
import test from "node:test";
|
|
11
|
-
|
|
12
|
-
import { dumpsToRemove, isCoreDump } from "../services/storage/core-dumps.js";
|
|
13
|
-
|
|
14
|
-
const dumps = [
|
|
15
|
-
{ name: "core.WorkerThread.81.1787176646", writtenAt: 1787176646000 },
|
|
16
|
-
{ name: "core.WorkerThread.81.1787224562", writtenAt: 1787224562000 },
|
|
17
|
-
{ name: "core.WorkerThread.81.1787237468", writtenAt: 1787237468000 },
|
|
18
|
-
{ name: "core.WorkerThread.81.1787243278", writtenAt: 1787243278000 },
|
|
19
|
-
{ name: "core.WorkerThread.81.1787292750", writtenAt: 1787292750000 }
|
|
20
|
-
];
|
|
21
|
-
|
|
22
|
-
test("the newest two stay and the rest go, oldest first", () => {
|
|
23
|
-
assert.deepEqual(dumpsToRemove(dumps), [
|
|
24
|
-
"core.WorkerThread.81.1787176646",
|
|
25
|
-
"core.WorkerThread.81.1787224562",
|
|
26
|
-
"core.WorkerThread.81.1787237468"
|
|
27
|
-
]);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
test("fewer than the limit leaves everything alone", () => {
|
|
31
|
-
assert.deepEqual(dumpsToRemove(dumps.slice(0, 2)), []);
|
|
32
|
-
assert.deepEqual(dumpsToRemove([]), []);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
test("order on disk does not decide; the time written does", () => {
|
|
36
|
-
const shuffled = [dumps[3], dumps[0], dumps[4], dumps[2], dumps[1]];
|
|
37
|
-
assert.deepEqual(dumpsToRemove(shuffled, 1), [
|
|
38
|
-
"core.WorkerThread.81.1787176646",
|
|
39
|
-
"core.WorkerThread.81.1787224562",
|
|
40
|
-
"core.WorkerThread.81.1787237468",
|
|
41
|
-
"core.WorkerThread.81.1787243278"
|
|
42
|
-
]);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
test("only core dumps are considered", () => {
|
|
46
|
-
assert.equal(isCoreDump("core.WorkerThread.81.1787292750"), true);
|
|
47
|
-
assert.equal(isCoreDump("proxy.log"), false);
|
|
48
|
-
assert.equal(isCoreDump("host-timings.json"), false);
|
|
49
|
-
// Not a path, and not a directory called core.
|
|
50
|
-
assert.equal(isCoreDump("core.foo/bar"), false);
|
|
51
|
-
assert.equal(isCoreDump(""), false);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
test("nothing readable is not a reason to delete anything", () => {
|
|
55
|
-
assert.deepEqual(dumpsToRemove(null), []);
|
|
56
|
-
assert.deepEqual(dumpsToRemove([{ name: 5, writtenAt: "x" }]), []);
|
|
57
|
-
});
|