mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 13:05:32 +09:00
chore(repo): remove dead code and useless tests (#3375)
This commit is contained in:
1 parent
7e177a1c90
commit
1cd62eb661
1391 files changed
+783
-120415
No files matched your search
@@ -80,8 +80,6 @@ export interface FrameSinkDiagnostics {
|
||||
|
||||
export declare const loadError: Error | null;
|
||||
|
||||
export declare function __setBindingForTests(binding: unknown): void;
|
||||
|
||||
export declare interface ScreenCapture {
|
||||
on(event: 'error', listener: (err: Error) => void): this;
|
||||
on(event: 'closed', listener: () => void): this;
|
||||
|
||||
@@ -93,11 +93,6 @@ function probeScreenRecordingAccess() {
|
||||
return Promise.resolve(binding.probeScreenRecordingAccess());
|
||||
}
|
||||
|
||||
function __setBindingForTests(nextBinding) {
|
||||
binding = nextBinding;
|
||||
loadError = null;
|
||||
}
|
||||
|
||||
class ScreenCapture extends EventEmitter {
|
||||
constructor(options = {}) {
|
||||
super();
|
||||
@@ -235,5 +230,4 @@ module.exports = {
|
||||
listSources,
|
||||
loadError,
|
||||
probeScreenRecordingAccess,
|
||||
__setBindingForTests,
|
||||
};
|
||||
@@ -1,260 +0,0 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import assert from 'node:assert/strict';
|
||||
import {createRequire} from 'node:module';
|
||||
import {afterEach, describe, test} from 'node:test';
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const macScreenCapture = require('./index.js');
|
||||
|
||||
function makeFakeBinding({sources = [], availability = {sck: {supported: true}, screenPermission: 'authorized'}} = {}) {
|
||||
const calls = [];
|
||||
const frameSinkHandleCalls = [];
|
||||
const natives = [];
|
||||
const frameSinkDiagnostics = {
|
||||
accepted: 0,
|
||||
coalesced: 0,
|
||||
rejected: 0,
|
||||
mediaFramesDroppedWithoutSink: 0,
|
||||
};
|
||||
class FakeNative {
|
||||
constructor() {
|
||||
this.lifecycleCallback = undefined;
|
||||
this.stopCount = 0;
|
||||
natives.push(this);
|
||||
}
|
||||
|
||||
setLifecycleCallback(callback) {
|
||||
this.lifecycleCallback = callback;
|
||||
}
|
||||
|
||||
setFrameSinkHandle(handle) {
|
||||
frameSinkHandleCalls.push(handle);
|
||||
}
|
||||
|
||||
async start(sourceId, sourceKind, width, height, frameRate, captureId, captureOptions) {
|
||||
calls.push({sourceId, sourceKind, width, height, frameRate, captureId, captureOptions});
|
||||
return {width: width || 1920, height: height || 1080, frameRate: frameRate || 30, pixelFormat: 'nv12'};
|
||||
}
|
||||
|
||||
async stop() {
|
||||
this.stopCount += 1;
|
||||
}
|
||||
|
||||
getFrameSinkDiagnostics() {
|
||||
return {...frameSinkDiagnostics};
|
||||
}
|
||||
}
|
||||
return {
|
||||
binding: {
|
||||
ScreenCapture: FakeNative,
|
||||
listSources: async () => sources,
|
||||
getBackendAvailability: async () => availability,
|
||||
getBackendInfo: () => ({
|
||||
backend: 'mac-screen-capture',
|
||||
supported: true,
|
||||
reason: '',
|
||||
minMacosVersion: '12.3',
|
||||
detectedMacosVersion: '14.0',
|
||||
sckAvailable: true,
|
||||
}),
|
||||
},
|
||||
calls,
|
||||
frameSinkHandleCalls,
|
||||
frameSinkDiagnostics,
|
||||
natives,
|
||||
};
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
macScreenCapture.__setBindingForTests(null);
|
||||
});
|
||||
|
||||
describe('mac-screen-capture loader wrapper', () => {
|
||||
test('forwards source id, kind, and dimensions to native binding', async () => {
|
||||
const {binding, calls} = makeFakeBinding();
|
||||
macScreenCapture.__setBindingForTests(binding);
|
||||
const capture = new macScreenCapture.ScreenCapture({
|
||||
sourceId: '12345',
|
||||
sourceKind: 'window',
|
||||
width: 1280,
|
||||
height: 720,
|
||||
frameRate: 30,
|
||||
colorRange: 'full',
|
||||
colorSpace: 'rec709',
|
||||
showCursorClicks: true,
|
||||
captureRect: {x: 10, y: 20, width: 300, height: 200},
|
||||
});
|
||||
capture.on('error', () => {});
|
||||
const result = await capture.start();
|
||||
assert.deepEqual(calls, [
|
||||
{
|
||||
sourceId: '12345',
|
||||
sourceKind: 'window',
|
||||
width: 1280,
|
||||
height: 720,
|
||||
frameRate: 30,
|
||||
captureId: undefined,
|
||||
captureOptions: {
|
||||
colorRange: 'full',
|
||||
colorSpace: 'rec709',
|
||||
showCursorClicks: true,
|
||||
captureRect: {x: 10, y: 20, width: 300, height: 200},
|
||||
},
|
||||
},
|
||||
]);
|
||||
assert.equal(result.pixelFormat, 'nv12');
|
||||
assert.equal(result.width, 1280);
|
||||
assert.equal(result.height, 720);
|
||||
});
|
||||
test('defaults sourceKind to screen and frameRate to 30', async () => {
|
||||
const {binding, calls} = makeFakeBinding();
|
||||
macScreenCapture.__setBindingForTests(binding);
|
||||
const capture = new macScreenCapture.ScreenCapture({sourceId: '1'});
|
||||
capture.on('error', () => {});
|
||||
await capture.start();
|
||||
assert.equal(calls[0].sourceKind, 'screen');
|
||||
assert.equal(calls[0].frameRate, 30);
|
||||
});
|
||||
test('forwards display and window sources from native binding without rewriting ids', async () => {
|
||||
const {binding} = makeFakeBinding({
|
||||
sources: [
|
||||
{kind: 'screen', id: 'display:69733632', name: 'Studio Display', width: 5120, height: 2880},
|
||||
{
|
||||
kind: 'window',
|
||||
id: 'window:4242',
|
||||
name: 'Fluxer',
|
||||
width: 1440,
|
||||
height: 900,
|
||||
appName: 'Fluxer',
|
||||
bundleId: 'app.fluxer.desktop',
|
||||
targetPid: 1234,
|
||||
},
|
||||
],
|
||||
});
|
||||
macScreenCapture.__setBindingForTests(binding);
|
||||
|
||||
const sources = await macScreenCapture.listSources();
|
||||
|
||||
assert.deepEqual(sources, [
|
||||
{kind: 'screen', id: 'display:69733632', name: 'Studio Display', width: 5120, height: 2880},
|
||||
{
|
||||
kind: 'window',
|
||||
id: 'window:4242',
|
||||
name: 'Fluxer',
|
||||
width: 1440,
|
||||
height: 900,
|
||||
appName: 'Fluxer',
|
||||
bundleId: 'app.fluxer.desktop',
|
||||
targetPid: 1234,
|
||||
},
|
||||
]);
|
||||
});
|
||||
test('reports ScreenCaptureKit support and permission from native binding', async () => {
|
||||
const {binding} = makeFakeBinding({
|
||||
availability: {
|
||||
sck: {supported: true, macosVersion: '15.0'},
|
||||
screenPermission: 'authorized',
|
||||
},
|
||||
});
|
||||
macScreenCapture.__setBindingForTests(binding);
|
||||
|
||||
assert.deepEqual(await macScreenCapture.getBackendAvailability(), {
|
||||
sck: {supported: true, macosVersion: '15.0'},
|
||||
screenPermission: 'authorized',
|
||||
});
|
||||
});
|
||||
test('forwards the screen recording probe result from the native binding', async () => {
|
||||
const {binding} = makeFakeBinding();
|
||||
binding.probeScreenRecordingAccess = async () => 'granted';
|
||||
macScreenCapture.__setBindingForTests(binding);
|
||||
assert.equal(await macScreenCapture.probeScreenRecordingAccess(), 'granted');
|
||||
});
|
||||
test('reports the screen recording probe as unsupported without a binding that has it', async () => {
|
||||
macScreenCapture.__setBindingForTests(null);
|
||||
assert.equal(await macScreenCapture.probeScreenRecordingAccess(), 'unsupported');
|
||||
macScreenCapture.__setBindingForTests(makeFakeBinding().binding);
|
||||
assert.equal(await macScreenCapture.probeScreenRecordingAccess(), 'unsupported');
|
||||
});
|
||||
test('installs a native frame sink handle once before start', async () => {
|
||||
const {binding, calls, frameSinkHandleCalls} = makeFakeBinding();
|
||||
macScreenCapture.__setBindingForTests(binding);
|
||||
const frameSinkHandle = {native: true};
|
||||
const capture = new macScreenCapture.ScreenCapture({
|
||||
sourceId: '12345',
|
||||
sourceKind: 'window',
|
||||
frameSinkHandle,
|
||||
nativeFrameSinkRequired: true,
|
||||
});
|
||||
capture.on('error', () => {});
|
||||
|
||||
await capture.start();
|
||||
|
||||
assert.deepEqual(frameSinkHandleCalls, [frameSinkHandle]);
|
||||
assert.equal(calls.length, 1);
|
||||
});
|
||||
test('fails before native start when a native frame sink is required but missing', async () => {
|
||||
const {binding, calls, frameSinkHandleCalls} = makeFakeBinding();
|
||||
macScreenCapture.__setBindingForTests(binding);
|
||||
const capture = new macScreenCapture.ScreenCapture({
|
||||
sourceId: '12345',
|
||||
sourceKind: 'window',
|
||||
nativeFrameSinkRequired: true,
|
||||
});
|
||||
capture.on('error', () => {});
|
||||
|
||||
await assert.rejects(() => capture.start(), /native frame sink handle is required/i);
|
||||
assert.deepEqual(frameSinkHandleCalls, []);
|
||||
assert.deepEqual(calls, []);
|
||||
});
|
||||
test('frame sink diagnostics are forwarded by the wrapper', () => {
|
||||
const {binding, frameSinkDiagnostics} = makeFakeBinding();
|
||||
macScreenCapture.__setBindingForTests(binding);
|
||||
const capture = new macScreenCapture.ScreenCapture({sourceId: '1'});
|
||||
|
||||
frameSinkDiagnostics.accepted = 5;
|
||||
frameSinkDiagnostics.coalesced = 1;
|
||||
frameSinkDiagnostics.rejected = 2;
|
||||
frameSinkDiagnostics.mediaFramesDroppedWithoutSink = 3;
|
||||
|
||||
assert.deepEqual(capture.getFrameSinkDiagnostics(), {
|
||||
accepted: 5,
|
||||
coalesced: 1,
|
||||
rejected: 2,
|
||||
mediaFramesDroppedWithoutSink: 3,
|
||||
});
|
||||
});
|
||||
test('emits closed once when native lifecycle closes and stop is called later', async () => {
|
||||
const {binding, natives} = makeFakeBinding();
|
||||
macScreenCapture.__setBindingForTests(binding);
|
||||
const capture = new macScreenCapture.ScreenCapture({sourceId: '1'});
|
||||
let closed = 0;
|
||||
capture.on('closed', () => {
|
||||
closed += 1;
|
||||
});
|
||||
natives[0].lifecycleCallback('closed', '');
|
||||
await Promise.resolve();
|
||||
await capture.stop();
|
||||
assert.equal(closed, 1);
|
||||
assert.equal(natives[0].stopCount, 1);
|
||||
});
|
||||
test('lifecycle error emits Error event', async () => {
|
||||
const {binding, natives} = makeFakeBinding();
|
||||
macScreenCapture.__setBindingForTests(binding);
|
||||
const capture = new macScreenCapture.ScreenCapture({sourceId: '1'});
|
||||
const errors = [];
|
||||
capture.on('error', (err) => errors.push(err));
|
||||
natives[0].lifecycleCallback('error', 'permission lost mid-stream');
|
||||
assert.equal(errors.length, 1);
|
||||
assert.equal(errors[0].message, 'permission lost mid-stream');
|
||||
});
|
||||
test('lifecycle diagnostic emits diagnostic event', () => {
|
||||
const {binding, natives} = makeFakeBinding();
|
||||
macScreenCapture.__setBindingForTests(binding);
|
||||
const capture = new macScreenCapture.ScreenCapture({sourceId: '1'});
|
||||
const diagnostics = [];
|
||||
capture.on('diagnostic', (message) => diagnostics.push(message));
|
||||
natives[0].lifecycleCallback('diagnostic', 'frame sink rejected a frame');
|
||||
assert.deepEqual(diagnostics, ['frame sink rejected a frame']);
|
||||
});
|
||||
});
|
||||
@@ -22,8 +22,7 @@
|
||||
],
|
||||
"scripts": {
|
||||
"build": "cargo run --locked --quiet --manifest-path ../../../tools/ci/Cargo.toml -- build-desktop-native-addon",
|
||||
"test": "pnpm test:cargo && node --test index.test.mjs",
|
||||
"test:cargo": "cargo test --manifest-path Cargo.toml"
|
||||
"test": "cargo test --manifest-path Cargo.toml"
|
||||
},
|
||||
"binary": {
|
||||
"napi_versions": [
|
||||
|
||||
@@ -465,305 +465,3 @@ impl MacScreenShareAudioFrameWithBytes {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn builder_defaults_produce_valid_config() {
|
||||
let cfg = SckCaptureConfig::builder()
|
||||
.build()
|
||||
.expect("default builder");
|
||||
assert_eq!(cfg.target_fps(), FPS_DEFAULT);
|
||||
assert_eq!(cfg.queue_depth(), QUEUE_DEPTH_DEFAULT);
|
||||
assert_eq!(cfg.pixel_format(), SckPixelFormat::Nv12VideoRange);
|
||||
assert_eq!(cfg.color_space(), SckColorSpace::SrgbBt709);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builder_rejects_fps_zero() {
|
||||
let err = SckCaptureConfig::builder()
|
||||
.target_fps(0)
|
||||
.build()
|
||||
.unwrap_err();
|
||||
assert_eq!(err, SckError::InvalidFps(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builder_rejects_fps_above_max() {
|
||||
let err = SckCaptureConfig::builder()
|
||||
.target_fps(FPS_MAX + 1)
|
||||
.build()
|
||||
.unwrap_err();
|
||||
assert_eq!(err, SckError::InvalidFps(FPS_MAX + 1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builder_accepts_fps_120() {
|
||||
let cfg = SckCaptureConfig::builder()
|
||||
.target_fps(FPS_MAX)
|
||||
.build()
|
||||
.expect("120 fps ok");
|
||||
assert_eq!(cfg.target_fps(), FPS_MAX);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builder_rejects_queue_depth_zero() {
|
||||
let err = SckCaptureConfig::builder()
|
||||
.queue_depth(0)
|
||||
.build()
|
||||
.unwrap_err();
|
||||
assert_eq!(err, SckError::InvalidQueueDepth(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builder_rejects_queue_depth_above_max() {
|
||||
let err = SckCaptureConfig::builder()
|
||||
.queue_depth(QUEUE_DEPTH_MAX + 1)
|
||||
.build()
|
||||
.unwrap_err();
|
||||
assert_eq!(err, SckError::InvalidQueueDepth(QUEUE_DEPTH_MAX + 1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builder_rejects_hdr_without_displayp3() {
|
||||
let err = SckCaptureConfig::builder()
|
||||
.pixel_format(SckPixelFormat::L10rHdr)
|
||||
.color_space(SckColorSpace::SrgbBt709)
|
||||
.build()
|
||||
.unwrap_err();
|
||||
assert_eq!(err, SckError::HdrRequiresWideColorSpace);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builder_accepts_hdr_with_displayp3() {
|
||||
let cfg = SckCaptureConfig::builder()
|
||||
.pixel_format(SckPixelFormat::L10rHdr)
|
||||
.color_space(SckColorSpace::DisplayP3)
|
||||
.build()
|
||||
.expect("hdr with p3 ok");
|
||||
assert!(cfg.pixel_format().is_hdr());
|
||||
assert_eq!(cfg.color_space(), SckColorSpace::DisplayP3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn minimum_frame_interval_60_fps_is_15_ms() {
|
||||
let cfg = SckCaptureConfig::builder()
|
||||
.target_fps(60)
|
||||
.build()
|
||||
.expect("60 fps");
|
||||
let ns = cfg.minimum_frame_interval_ns();
|
||||
let expected = (1_000_000_000_u64 / 60) * 9 / 10;
|
||||
assert_eq!(ns, expected);
|
||||
assert!((14_000_000..=16_000_000).contains(&ns));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn minimum_frame_interval_30_fps_factor_applied() {
|
||||
let cfg = SckCaptureConfig::builder()
|
||||
.target_fps(30)
|
||||
.build()
|
||||
.expect("30 fps");
|
||||
let ns = cfg.minimum_frame_interval_ns();
|
||||
let base = 1_000_000_000_u64 / 30;
|
||||
assert_eq!(ns, base * 9 / 10);
|
||||
assert!(ns < base);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn minimum_frame_interval_120_fps_under_8_3ms() {
|
||||
let cfg = SckCaptureConfig::builder()
|
||||
.target_fps(120)
|
||||
.build()
|
||||
.expect("120 fps");
|
||||
let ns = cfg.minimum_frame_interval_ns();
|
||||
assert!(ns < 8_400_000);
|
||||
assert!(ns > 6_000_000);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pixel_format_fourcc_matches_obs_constants() {
|
||||
assert_eq!(SckPixelFormat::Bgra8.as_fourcc(), PIXEL_FORMAT_BGRA_FOURCC);
|
||||
assert_eq!(
|
||||
SckPixelFormat::L10rHdr.as_fourcc(),
|
||||
PIXEL_FORMAT_L10R_FOURCC
|
||||
);
|
||||
assert_eq!(
|
||||
SckPixelFormat::Nv12VideoRange.as_fourcc(),
|
||||
PIXEL_FORMAT_420V_FOURCC
|
||||
);
|
||||
assert_eq!(
|
||||
SckPixelFormat::Nv12FullRange.as_fourcc(),
|
||||
PIXEL_FORMAT_420F_FOURCC
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn color_space_names_present() {
|
||||
assert_eq!(
|
||||
SckColorSpace::DisplayP3.as_cf_name(),
|
||||
"kCGColorSpaceDisplayP3"
|
||||
);
|
||||
assert_eq!(SckColorSpace::SrgbBt709.as_cf_name(), "kCGColorSpaceSRGB");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn failure_reason_strings_non_empty() {
|
||||
let f = SckCaptureFailure::StreamStoppedWithError("oops".into());
|
||||
assert_eq!(f.reason(), "oops");
|
||||
let f = SckCaptureFailure::SystemDeniedAccess;
|
||||
assert!(!f.reason().is_empty());
|
||||
let f = SckCaptureFailure::DisplayDisconnected;
|
||||
assert!(!f.reason().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_config_validates() {
|
||||
let cfg = SckCaptureConfig::default();
|
||||
assert_eq!(cfg.target_fps(), FPS_DEFAULT);
|
||||
assert_eq!(cfg.queue_depth(), QUEUE_DEPTH_DEFAULT);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn captures_audio_defaults_to_false() {
|
||||
let cfg = SckCaptureConfig::default();
|
||||
assert!(!cfg.captures_audio());
|
||||
assert_eq!(cfg.audio_sample_rate_hz(), AUDIO_SAMPLE_RATE_DEFAULT_HZ);
|
||||
assert_eq!(cfg.audio_channels(), AUDIO_CHANNEL_COUNT_DEFAULT);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builder_captures_audio_toggle_preserves_frame_interval() {
|
||||
let cfg_off = SckCaptureConfig::builder()
|
||||
.target_fps(60)
|
||||
.build()
|
||||
.expect("60 fps off");
|
||||
let cfg_on = SckCaptureConfig::builder()
|
||||
.target_fps(60)
|
||||
.captures_audio(true)
|
||||
.build()
|
||||
.expect("60 fps on");
|
||||
assert!(!cfg_off.captures_audio());
|
||||
assert!(cfg_on.captures_audio());
|
||||
assert_eq!(
|
||||
cfg_off.minimum_frame_interval_ns(),
|
||||
cfg_on.minimum_frame_interval_ns(),
|
||||
"captures_audio toggle must not alter minimum_frame_interval_ns"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builder_rejects_audio_sample_rate_zero() {
|
||||
let err = SckCaptureConfig::builder()
|
||||
.audio_sample_rate_hz(0)
|
||||
.build()
|
||||
.unwrap_err();
|
||||
assert_eq!(err, SckError::InvalidAudioSampleRate(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builder_rejects_audio_channel_count_zero() {
|
||||
let err = SckCaptureConfig::builder()
|
||||
.audio_channels(0)
|
||||
.build()
|
||||
.unwrap_err();
|
||||
assert_eq!(err, SckError::InvalidAudioChannelCount(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builder_accepts_48khz_stereo_audio() {
|
||||
let cfg = SckCaptureConfig::builder()
|
||||
.captures_audio(true)
|
||||
.audio_sample_rate_hz(48_000)
|
||||
.audio_channels(2)
|
||||
.build()
|
||||
.expect("48k stereo ok");
|
||||
assert!(cfg.captures_audio());
|
||||
assert_eq!(cfg.audio_sample_rate_hz(), 48_000);
|
||||
assert_eq!(cfg.audio_channels(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mac_screen_share_audio_frame_constructs_valid() {
|
||||
let frame =
|
||||
MacScreenShareAudioFrame::new(48_000, 2, 1024, 12_345).expect("valid audio frame");
|
||||
assert_eq!(frame.sample_rate_hz, 48_000);
|
||||
assert_eq!(frame.channels, 2);
|
||||
assert_eq!(frame.num_samples_per_channel, 1024);
|
||||
assert_eq!(frame.pts_us, 12_345);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mac_screen_share_audio_frame_rejects_invalid_sample_rate() {
|
||||
let err = MacScreenShareAudioFrame::new(0, 2, 1024, 0).expect_err("0 hz invalid");
|
||||
assert_eq!(err, SckError::InvalidAudioSampleRate(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mac_screen_share_audio_frame_rejects_too_many_channels() {
|
||||
let err = MacScreenShareAudioFrame::new(48_000, 16, 1024, 0).expect_err("16 ch invalid");
|
||||
assert_eq!(err, SckError::InvalidAudioChannelCount(16));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn audio_sample_format_strings_and_widths() {
|
||||
assert_eq!(AudioSampleFormat::F32Planar.as_str(), "f32_planar");
|
||||
assert_eq!(
|
||||
AudioSampleFormat::F32Interleaved.as_str(),
|
||||
"f32_interleaved"
|
||||
);
|
||||
assert_eq!(
|
||||
AudioSampleFormat::I16Interleaved.as_str(),
|
||||
"i16_interleaved"
|
||||
);
|
||||
assert_eq!(AudioSampleFormat::Unknown.as_str(), "unknown");
|
||||
assert_eq!(AudioSampleFormat::F32Planar.bytes_per_sample(), 4);
|
||||
assert_eq!(AudioSampleFormat::F32Interleaved.bytes_per_sample(), 4);
|
||||
assert_eq!(AudioSampleFormat::I16Interleaved.bytes_per_sample(), 2);
|
||||
assert_eq!(AudioSampleFormat::Unknown.bytes_per_sample(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn audio_sample_format_codes_are_stable_and_bounded() {
|
||||
assert_eq!(AudioSampleFormat::F32Planar.code(), 0);
|
||||
assert_eq!(AudioSampleFormat::F32Interleaved.code(), 1);
|
||||
assert_eq!(AudioSampleFormat::I16Interleaved.code(), 2);
|
||||
assert_eq!(AudioSampleFormat::Unknown.code(), 3);
|
||||
assert!(AudioSampleFormat::Unknown.code() <= AUDIO_SAMPLE_FORMAT_CODE_MAX);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mac_screen_share_audio_frame_with_bytes_round_trip() {
|
||||
let payload = vec![0xAA_u8; 16];
|
||||
let f = MacScreenShareAudioFrameWithBytes::new(
|
||||
48_000,
|
||||
2,
|
||||
4,
|
||||
999,
|
||||
AudioSampleFormat::F32Planar,
|
||||
payload.clone(),
|
||||
)
|
||||
.expect("valid frame with bytes");
|
||||
assert_eq!(f.sample_rate_hz, 48_000);
|
||||
assert_eq!(f.channels, 2);
|
||||
assert_eq!(f.num_samples_per_channel, 4);
|
||||
assert_eq!(f.pts_us, 999);
|
||||
assert_eq!(f.format, AudioSampleFormat::F32Planar);
|
||||
assert_eq!(f.samples, payload);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mac_screen_share_audio_frame_with_bytes_rejects_bad_sample_rate() {
|
||||
let err = MacScreenShareAudioFrameWithBytes::new(
|
||||
0,
|
||||
2,
|
||||
4,
|
||||
0,
|
||||
AudioSampleFormat::F32Planar,
|
||||
Vec::new(),
|
||||
)
|
||||
.expect_err("zero hz invalid");
|
||||
assert_eq!(err, SckError::InvalidAudioSampleRate(0));
|
||||
}
|
||||
}
|
||||
@@ -545,13 +545,6 @@ mod tests {
|
||||
));
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
#[test]
|
||||
fn off_macos_init_fails() {
|
||||
let err = EncoderAttachment::try_new(640, 480).err();
|
||||
assert!(matches!(err, Some(EncoderAttachError::RingInitFailed)));
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[test]
|
||||
fn macos_attach_then_detach() {
|
||||
|
||||
@@ -77,55 +77,3 @@ pub fn classify_support(detected: Option<(i64, i64, i64)>) -> SupportClassificat
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn meets_floor_exact_match() {
|
||||
assert!(meets_floor((12, 3, 0), SCK_MIN_MACOS));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn meets_floor_higher_major() {
|
||||
assert!(meets_floor((14, 0, 0), SCK_MIN_MACOS));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_below_floor() {
|
||||
assert!(!meets_floor((12, 2, 9), SCK_MIN_MACOS));
|
||||
assert!(!meets_floor((11, 7, 10), SCK_MIN_MACOS));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_version_trims_zero_patch() {
|
||||
assert_eq!("12.3", format_version((12, 3, 0)));
|
||||
assert_eq!("14.2.1", format_version((14, 2, 1)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn classify_unknown_version_is_unsupported() {
|
||||
let c = classify_support(None);
|
||||
assert!(!c.supported);
|
||||
assert!(!c.sck_available);
|
||||
assert!(c.reason.contains("could not detect"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn classify_macos_11_is_unsupported() {
|
||||
let c = classify_support(Some((11, 7, 10)));
|
||||
assert!(!c.supported);
|
||||
assert!(!c.sck_available);
|
||||
assert!(c.reason.contains("macOS 12.3+"));
|
||||
assert!(c.reason.contains("macOS 11.7.10"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn classify_macos_12_3_is_supported() {
|
||||
let c = classify_support(Some((12, 3, 0)));
|
||||
assert!(c.supported);
|
||||
assert!(c.sck_available);
|
||||
assert!(c.reason.contains("ScreenCaptureKit"));
|
||||
}
|
||||
}
|
||||
@@ -288,35 +288,3 @@ pub fn sc_stream_add_stream_output(
|
||||
) -> Result<(), Retained<objc2_foundation::NSError>> {
|
||||
unsafe { stream.addStreamOutput_type_sampleHandlerQueue_error(output, kind, queue) }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::obs_minimum_frame_interval;
|
||||
use crate::config::{FPS_MAX, FPS_MIN, SckCaptureConfig};
|
||||
|
||||
#[test]
|
||||
fn minimum_frame_interval_is_strictly_shorter_than_frame_time() {
|
||||
for fps in [FPS_MIN, 30, 60, FPS_MAX] {
|
||||
let t = obs_minimum_frame_interval(fps);
|
||||
assert!(t.value > 0);
|
||||
assert!(t.timescale > 0);
|
||||
let interval_ns = (t.value as u64) * 1_000_000_000 / (t.timescale as u64);
|
||||
let frame_ns = 1_000_000_000 / (fps as u64);
|
||||
assert!(interval_ns < frame_ns, "fps={fps}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn minimum_frame_interval_matches_config_minimum_frame_interval_ns() {
|
||||
for fps in [FPS_MIN, 30, 60, FPS_MAX] {
|
||||
let t = obs_minimum_frame_interval(fps);
|
||||
let cm_interval_ns = (t.value as u64) * 1_000_000_000 / (t.timescale as u64);
|
||||
let cfg = SckCaptureConfig::builder()
|
||||
.target_fps(fps)
|
||||
.build()
|
||||
.expect("config builds");
|
||||
let cfg_interval_ns = cfg.minimum_frame_interval_ns();
|
||||
assert!(cm_interval_ns.abs_diff(cfg_interval_ns) <= 1, "fps={fps}");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user