/** * Screen watchdog recovery for the GPU black-tile shape. * * A live-but-never-decoded receiver (videoWidth === 0) must trigger a * renegotiate request, not a re-adoption of the same receiver, which would * mark the share delivered and stall recovery until a rejoin. * * node --test test/screenWatchdog.test.js */ 'use strict'; const assert = require('node:assert/strict'); const fs = require('node:fs'); const path = require('node:path'); const test = require('node:test'); const vm = require('node:vm'); const VOICE_SOURCE = fs.readFileSync(path.join(__dirname, '..', 'public/js/voice.js'), 'utf8'); function loadVoiceManager(documentStub) { let pendingTimer = null; const context = vm.createContext({ module: { exports: {} }, navigator: { userAgent: '', platform: '', maxTouchPoints: 0 }, document: documentStub, localStorage: { getItem: () => null, setItem: () => {}, removeItem: () => {} }, console: { log() {}, warn() {}, error() {} }, RTCSessionDescription: function RTCSessionDescription(d) { return d; }, MediaStream: function MediaStream(tracks) { this.tracks = tracks || []; }, setTimeout: (fn) => { pendingTimer = fn; return 1; }, clearTimeout: () => { pendingTimer = null; }, Date }); vm.runInContext(`${VOICE_SOURCE}\nmodule.exports = VoiceManager;`, context, { filename: 'voice.js' }); return { VoiceManager: context.module.exports, fireTimer: () => pendingTimer && pendingTimer() }; } // Viewer of sharer 7 with a live, unmuted screen receiver. tileVideo is the //