☰
dash.js 功能测试套件深度指南:基于 Karma 的 functional 测试架构、配置与实战运行
2026/10/12 22:21:25 网站建设 项目流程
  • 音视频
  • 前端

【免费下载链接】dash.js

A reference client implementation for the playback of MPEG DASH via Javascript and compliant browsers.

项目地址:https://gitcode.com/gh_mirrors/da/dash.js
点击查看免费下载

dash.js 是 DASH Industry Forum 提供的 MPEG-DASH 参考播放器实现,其功能正确性由一套运行在浏览器环境中的功能测试(functional tests)保障。本文以仓库内 test/functional/index.md 为核心,系统讲解这套基于 Karma Testrunner 的functional-karma测试套件的目录结构、配置机制、测试用例组织方式、完整运行步骤,并深入剖析 Windows 环境下 Edge 浏览器运行 Widevine DRM 测试时"必须使用持久化浏览器 profile"这一关键工程问题。读完本文,你将能够理解 dash.js 功能测试的完整脉络,并掌握在本地与云端多浏览器环境中配置、执行与结果分析的具体方法。

一、功能测试套件概览:测试什么、用什么测

功能测试(functional tests)与单元测试(unit tests)定位不同:单元测试聚焦于某个类或函数的内部逻辑,而功能测试面向真实浏览器环境,验证用户可感知的播放器行为,例如 play(播放)、pause(暂停)、seek(跳转)、ended(结束)、缓冲管理、轨切换、DRM 授权等端到端流程。

functional-karma测试套件使用Karma Testrunner作为测试执行引擎,配合 Mocha + Chai 编写断言、webpack 完成测试代码打包,并可在真实浏览器(Chrome、Firefox、Safari、Edge)或云端浏览器服务(BrowserStack、LambdaTest)中运行。测试的输入是真实的 MPD 流媒体清单(既有互联网上公开的测试向量,也有仓库内置的静态 MPD),输出是 JUnit XML、HTML 报告与覆盖率数据。

整套体系由 test/functional/config/karma.functional.conf.cjs 这一 Karma 配置文件驱动,其核心机制将在后文展开。

二、目录结构:七个角色的职责划分

原文档定义了功能测试套件的目录职责,结合当前仓库的实际布局,各目录的真实作用如下:

目录原文档定义仓库中的实际内容
adapter封装测试所需附加逻辑的适配器类,如DashJsAdapter.js作为 dash.js 功能的包装层test/functional/adapter/DashJsAdapter.js 封装了 MediaPlayer 的初始化、attachSource、seek、play/pause、事件监听与缓冲查询等 API;另有 GoogleAdManagerAdapter.js 用于广告场景
config定义待执行测试用例集合的测试配置文件,目标配置文件在Utils.js中导入test/functional/config/test-configurations/ 存放浏览器/环境配置(local.json、local-windows.json、browserstack.json、lambdatest.json等),streams/ 存放测试向量流清单(smoke.json、drm.json、text.json、all.json等)
content作为测试输入的内置静态 MPD,由 Karma 自动以带 CORS 头的方式伺服test/functional/content/ 下有gap/、multi-period/、preload/三类 MPD,通过/base/test/functional/content/路径访问
helper定义常量值并为特定用例过滤相关测试向量对应 test/functional/src/Constants.js(测试用例清单与超时阈值常量)与 test/functional/src/Utils.js(getTestvectorsForTestcase过滤逻辑)
results测试结果汇总输出目录报告由配置写入test/functional/results/(JUnit XML、HTML 报告、覆盖率)
test测试用例的实现test/functional/test/ 按功能类别分子目录:advanced/、audio/、buffer/、drm/、feature-support/、live/、playback/、playback-advanced/、text/、video/等,另含common/common.js公共断言辅助
view定制化的测试执行视图,包含 video 元素test/functional/view/index.html 提供<video id="video-element">、TTML 渲染 div 与广告 UI div,作为 Karma 的 customContextFile 被加载

需要特别说明的是,helper目录在原文档中的描述为"定义常量值和过滤测试向量",当前仓库将其实现为src/下的Constants.js与Utils.js;results目录在运行测试后由报告插件自动生成。从源码结构看,这套命名与实现存在一定演进差异,阅读时以实际文件为准。

2.1 测试执行上下文:view/index.html

test/functional/view/index.html 是整个测试的"舞台":它创建一个 640×480 的 muted<video>元素、一个用于 TTML 字幕渲染的#ttml-rendering-div和一个广告 UI 容器。每个测试用例都会通过DashJsAdapter拿到这个视频元素并执行真实播放,这也是功能测试区别于单元测试的关键——它不 mock video 元素,而是驱动真实的媒体管道。

三、配置机制:两层配置如何驱动一次测试运行

原文档指出:"主配置定义在karma.functional.conf.js,调整 testvectors 或 testcases 可修改config/content.js,或者换用src/Utils.js中引入的不同配置文件。" 当前仓库的实现方式如下:

  • Karma 主配置:test/functional/config/karma.functional.conf.cjs;
  • 测试向量(testvectors)清单:按场景拆分为多个 JSON 文件,存放在 test/functional/config/test-configurations/streams/;
  • 浏览器与环境配置:存放在 test/functional/config/test-configurations/。

3.1 karma.functional.conf.cjs 的工作方式

配置文件并非写死一套参数,而是通过命令行参数--configfile与--streamsfile动态加载 JSON,实现"环境配置 × 流配置"的组合:

const argv = yargs(hideBin(process.argv)).parse() const configFileName = argv.configfile const streamsFileName = argv.streamsfile // 加载环境配置(浏览器、端口、自定义 launcher、报告器) let testConfiguration = JSON.parse(fs.readFileSync(`test/functional/config/test-configurations/${configFileName}.json`, 'utf-8')); // 加载流配置(测试文件包含/排除列表、测试向量) const streamsConfiguration = JSON.parse(fs.readFileSync(`test/functional/config/test-configurations/streams/${streamsFileName}.json`, 'utf-8'));

核心行为包括:

  • 测试文件筛选:根据 streams 配置中的testfiles.included/testfiles.excluded决定加载哪些用例文件(_getIncludedTestfiles/_getExcludedTestfiles)。included含"all"时加载全部test/functional/test/**/*.js,否则按条目拼出对应文件路径。
  • 静态内容伺服:将 test/functional/content/ 下的*.mpd以served: true, included: false方式交给 Karma 静态服务器,并针对test/functional/content/.*\.mpd$追加Access-Control-Allow-Origin: *等 CORS 头,保证浏览器能跨源读取这些 MPD 及其引用的分片。
  • 打包与覆盖率:测试用例通过webpack预处理,dist/dash.all.debug.js与dist/dash.mss.min.js走coverage预处理以收集覆盖率。
  • 运行参数:singleRun: true(CI 模式,跑完即退出)、browserNoActivityTimeout/browserDisconnectTimeout均为 120000ms、默认并发度concurrency为 2(可由环境配置覆盖)、Mocha 超时 100000ms、默认不使用 iframe(useIframe: false)。
  • 云端适配:当环境配置type为lambdatest时,通过_adjustConfigurationForLambdatest注入 LambdaTest 的用户名、访问密钥与 hub 地址;BrowserStack 的凭据则取自环境变量BROWSERSTACK_USER/BROWSERSTACK_ACCESS_KEY。
  • 报告输出:JUnit XML 写入test/functional/results/test/karma/junit/,HTML 报告写入test/functional/results/test/karma/htmlreporter/,覆盖率写入test/functional/results/coverage/karma/。

3.2 环境配置文件(test-configurations/*.json)

以本地通用配置 local.json 为例,其结构展示了自定义浏览器 launcher 的标准写法:

{ "browsers": ["firefox_custom", "chrome_custom", "safari_custom"], "hostname": "localhost", "port": 4200, "protocol": "http", "concurrency": 3, "customLaunchers": { "chrome_custom": { "base": "Chrome", "flags": [ "--disable-web-security", "--autoplay-policy=no-user-gesture-required", "--disable-popup-blocking", "--disable-search-engine-choice-screen" ] }, "firefox_custom": { "base": "Firefox", "prefs": {} }, "safari_custom": { "base": "SafariNative", "flags": ["--disable-popup-blocking"] } }, "reporters": ["junit", "mocha", "html", "coverage"] }

关键点:

  • --autoplay-policy=no-user-gesture-required允许无用户手势自动播放,这是功能测试能够自动起播的前提;
  • --disable-web-security放宽同源限制,配合 CORS 头保证跨域 MPD 可加载;
  • 报告器组合junit(机器可读 XML)、mocha(终端进度)、html(可视化报告)、coverage(覆盖率)。

其他环境配置的差异点:local-ubuntu.json为 Firefox 设置了media.eme.enabled: true前缀并指定固定profile;local-singlebrowser.json只启动 Chrome 且concurrency: 1;browserstack.json通过base: "BrowserStack"定义云端 launcher(如bs_chrome_win_11、bs_safari_mac),凭据来自环境变量;tizen.json/webos.json则通过 launchers/ 下的自定义 Karma launcher 支持三星 Tizen 与 LG webOS 电视平台。

3.3 测试向量配置文件(streams/*.json)

每个 streams 文件由testfiles(决定跑哪些用例文件)与testvectors(决定喂给用例哪些流)两部分组成。以 smoke.json 为例,其 testvectors 覆盖:Smooth Streaming(MSS)VOD、Akamai 多周期 MPD、Axinom 多 DRM(Widevine + PlayReady)单密钥/多密钥向量、Shaka Angel-One Widevine、LiveSim2 的 EZDRM cbcs 直播、ClearKey 向量、SCTE-35 emsg 事件、Annex I(LL-DASH chunk 编码)、MPD Patching、动态转静态(dynamic-to-static)等。

单个测试向量的典型字段:

{ "name": "Single-period, 1080p, H.264, 5 video, 3 audio, 3 text tracks, CMAF, cbcs encryption, single key, Widevine+PlayReady", "type": "vod", "url": "https://media.axprod.net/TestVectors/Dash/protected_dash_1080p_h264_singlekey/manifest.mpd", "includedTestfiles": ["playback/*", "audio/*", "video/*", "text/*"], "excludedPlatforms": [{ "browser": "safari" }], "drm": { "com.widevine.alpha": { "serverURL": "https://drm-widevine-licensing.axprod.net/AcquireLicense" }, "com.microsoft.playready": { "serverURL": "https://drm-playready-licensing.axprod.net/AcquireLicense" } } }

各字段含义:

字段作用
name测试向量的可读名称,出现在用例 describe 标题中
type内容类型:vod或live,供用例区分动态/静态流行为(见 Constants.js 的CONTENT_TYPES)
urlMPD 地址;以/base/test/functional/content/开头的为仓库内置静态 MPD
includedTestfiles/excludedTestfiles该向量参与哪些用例(支持playback/*通配类别),与testfiles.included的全局筛选配合
excludedPlatforms在哪些浏览器上跳过该向量(如 Safari 不支持部分 DRM 场景)
drm按 Key System 配置的授权信息:serverURL(license 服务器)、httpRequestHeaders(如 Axinom 的X-AxDRM-MessageJWT)、httpTimeout、priority等
platformOverrides按浏览器覆盖drm或settings,实现同一向量在不同平台的差异化配置
settings直接注入 dash.js 的updateSettings配置(如streaming.retryAttempts)
testdata传给用例的附加参数(如 emsg 事件期望次数、周期切换等待时间、MPD patching 标志等)

3.4 测试向量过滤的源码实现

includedTestfiles/excludedPlatforms等筛选逻辑由 test/functional/src/Utils.js 的getTestvectorsForTestcase(testcase)执行,每个用例文件都会在顶层用Utils.getTestvectorsForTestcase(TESTCASE).forEach(...)为自己的用例名拉取匹配的向量,规则包括:

  • 向量声明了excludedPlatforms且当前浏览器命中(通过CoreUtils.parseUserAgent解析 UA 判断)则跳过;
  • 向量同时没有 include/exclude 约束时,对所有用例生效;
  • 向量includedTestfiles精确包含用例名,或包含该用例所属类别通配(如playback/*)时生效;
  • 向量includedTestfiles含"all"且用例不在其excludedTestfiles中时生效。

此外,_applyPlatformOverrides会在命中platformOverrides.browser时对向量的drm与settings做深合并克隆,从而在不污染共享配置的前提下实现 Edge 等平台的特化(例如 smoke.json 中为 Edge 强制com.microsoft.playready.recommendation系统优先级与audioRobustness: "2000")。

四、测试用例的组织方式:从常量到断言

4.1 用例清单与参数常量

test/functional/src/Constants.js 以TESTCASES.CATEGORIES定义十个用例类别(advanced/、audio/、buffer/、drm/、feature-support/、live/、playback/、playback-advanced/、text/、video/),并为每个具体用例生成如playback/play、buffer/buffer-cleanup、feature-support/cmcd-v2这样的完整键名。文件还集中定义了:

  • TEST_TIMEOUT_THRESHOLDS:各类断言等待阈值,例如IS_PLAYING: 5000、IS_PROGRESSING: 15000、BUFFER_CLEANUP: 45000、EVENT_WAITING_TIME: 10000;
  • TEST_INPUTS:各类用例的行为参数,例如随机 seek 次数NUMBER_OF_RANDOM_SEEKS: 3、允许的 seek 误差MAXIMUM_ALLOWED_SEEK_DIFFERENCE: 0.5、直播延迟目标LIVE_DELAY.VALUE: 10、初始缓冲目标INITIAL_BUFFER_TARGET.VALUE: 10;
  • DRM_SYSTEMS(Widevine / PlayReady 的 Key System 字符串)、SEGMENT_TYPES(InitializationSegment/MediaSegment)、ROLES(forced-subtitle 角色)等。

4.2 用例文件的结构范式

以 test/functional/test/playback/play.js 为模板,可看出每个用例文件遵循统一范式:引入Constants与Utils→ 用Utils.getTestvectorsForTestcase遍历匹配的向量 → 对每个向量生成一个describe(标题含用例名 + 向量名 + MPD URL)→before中通过 common.js 的initializeDashJsAdapter初始化适配器并 attachSource → 逐个it断言。play用例依次断言:进入播放状态(checkIsPlaying)、播放进度推进(checkIsProgressing)、无关键错误(checkNoCriticalErrors)。

test/functional/test/playback/seek.js 则展示了更复杂的行为验证:seek 到 0、seek 到负值(应钳制到 0)、seek 到超大值(应先注册PLAYBACK_SEEKED事件监听再 seek,断言"seek 必须真正完成而非永久挂起",随后按动态流/静态流分别验证落点:直播应贴近 live edge,VOD 应停在duration - seekDurationBackoff)、以及 3 次随机位置 seek,每次 seek 后都要重新验证播放与推进状态。

4.3 公共断言辅助与适配器

test/functional/test/common/common.js 提供checkIsPlaying、checkIsProgressing、checkIsNotProgressing、checkNoCriticalErrors、checkEventHasBeenTriggered、checkForEndedEvent、reachedTargetForwardBuffer、checkLiveDelay、checkTimeWithinThresholdForDvrWindow等可复用断言。其中checkNoCriticalErrors会将收集到的 error 事件与 error 级别日志序列化为诊断 JSON(对 URL 中的敏感查询串做脱敏),在断言失败时输出,便于定位问题。

DashJsAdapter.js 是测试与播放器的桥梁,它直接import {MediaPlayer, Debug} from '../../../dist/modern/esm/dash.all.min.js',因此功能测试依赖npm run build产出的 dist 构建产物。适配器内部:

  • init()中设置debug.logLevel: 3且dispatchEvent: true,使播放器把日志通过事件分发出来,供测试收集;
  • 注册FRAGMENT_LOADING_STARTED、LOG、ERROR三个内部事件,分别记录已开始下载的分片 URL、各级别日志与错误事件;
  • 提供isInPlayingState(监听PLAYBACK_PLAYING事件,超时则判定失败)、isProgressing(通过PLAYBACK_TIME_UPDATED事件判断在超时窗口内播放时间是否推进超过最小量)、waitForEvent、reachedTargetDelay(每 100ms 轮询getCurrentLiveLatency是否进入容差)等异步等待原语;
  • 包装attachSource、seek、seekToPresentationTime、setProtectionData、getTracksFor、setCurrentTrack、getDvrWindow、getCurrentLiveLatency等播放器 API。

五、测试执行:从安装到结果分析

原文档给出的执行步骤为:npm install→npm run build→npm run test-functional-mocha→ 在test/functional-karma/results查看结果。对照当前仓库的实际脚本(见 package.json),需要注意以下几点以仓库现状为准:

  1. 安装依赖:npm install(Node 版本需满足engines.node >= 20)。
  2. 构建 dist:npm run build,产出功能测试所依赖的dist/modern/esm/dash.all.min.js与dist/dash.mss.min.js。
  3. 执行功能测试:当前仓库的脚本名为test-functional,而非文档中的test-functional-mocha:
npm run test-functional # 等价于: karma start test/functional/config/karma.functional.conf.cjs --configfile=local --streamsfile=smoke

即默认使用local环境配置与smoke流配置。若需换用其他组合,直接以命令行参数覆盖:

karma start test/functional/config/karma.functional.conf.cjs --configfile=local-ubuntu --streamsfile=drm karma start test/functional/config/karma.functional.conf.cjs --configfile=browserstack --streamsfile=smoke

原文档提到"未来应允许直接通过命令行参数指定测试文件",目前该能力已部分落地:--configfile与--streamsfile正是通过命令行选配,而streams/*.json的testfiles.included/excluded已可精确控制测试文件集合。

  1. 查看结果:结果目录为 test/functional/results/,而非文档中的test/functional-karma/results。运行后生成:
    • test/functional/results/test/karma/junit/<browser>.xml(JUnit 格式,供 CI 解析);
    • test/functional/results/test/karma/htmlreporter/<timestamp>.html(HTML 可视化报告);
    • test/functional/results/coverage/karma/(覆盖率报告)。

另外仓库还提供了结果仪表盘:npm run test-functional-dashboard会启动 test/functional/results-dashboard/server.mjs 用于聚合展示历史结果。

5.1 一个用例的执行链路示例

以playback/play用例在本地跑 smoke 配置为例,其执行链路为:Karma 启动 Chrome → 加载view/index.html上下文 → webpack 打包用例文件 → 用例顶层调用Utils.getTestvectorsForTestcase('playback/play')筛选出 smoke.json 中includedTestfiles命中playback/play的向量 → 对每个向量创建DashJsAdapter、init(true)自动播放、setDrmData注入授权信息、attachSource(mpd)→checkIsPlaying等待PLAYBACK_PLAYING→checkIsProgressing观察PLAYBACK_TIME_UPDATED推进 →checkNoCriticalErrors校验日志与错误事件为空。任一步超时或断言失败,报告会携带脱敏后的请求/响应诊断信息输出。

六、Windows 特例:Edge 跑 Widevine DRM 测试必须使用持久化 profile

这是原文档着墨最多、也最具工程价值的专题。默认情况下 Karma 为每个浏览器启动一个临时(throwaway)profile,这在 Windows 的 Microsoft Edge 上会直接破坏 Widevine DRM 测试,原因链如下:

  1. Edge 的 Widevine CDM 是按 profile 下载的组件(存放在<user data dir>\WidevineCdm\<version>\);
  2. 全新临时 profile 会回退到 Edge 安装包自带的过期 CDM,而该 CDM 的客户端证书已被 Google 吊销;
  3. 强制吊销校验的 license 服务器会拒绝一切授权请求——例如cwip-shaka-proxy.appspot.com返回 500ACCESS_DENIED,Axinom 返回 400 并附x-axdrm-errormessage: ... DRM client models with revoked certificates are not allowed to receive licenses。

此外 Edge 通过Media Foundation渲染受保护内容,并通过显示驱动(OPM/HDCP)执行 license 的输出保护策略。因此Edge 上的 DRM 测试必须在解锁的控制台会话且显示器开启的状态下运行:若工作站被锁定、显示器关闭或处于 RDP 会话,任何 license 要求输出保护的向量都会以MEDIA_ERR_DECODE ... MediaFoundationRenderer error: kOnPlaybackError (0xC0262500 / "The driver does not support OPM")失败。在 smoke 套件中,受影响的包括 Axinom 的v7-MultiDRM-*系列与 LiveSim2 EZDRM 向量;不受影响的是 Angel-One 和 Axinom cbcs 单密钥向量——它们的 license 允许未受保护输出。这是环境条件而非播放器或测试缺陷。

6.1 解决方案:edgeDataDir 持久化 profile

为此,local-windows.json 中的edge_customlauncher 通过edgeDataDir指定了一个持久化 profile 目录:

"edge_custom": { "base": "Edge", "edgeDataDir": "C:\\Users\\local_root\\.dashjs-edge-test-profile", "flags": [ "--disable-web-security", "--autoplay-policy=no-user-gesture-required", "--disable-popup-blocking", "--disable-search-engine-choice-screen" ] }

新机器的一次性初始化步骤:

  1. 把当前版本的组件 CDM 拷贝进该目录:复制%LOCALAPPDATA%\Microsoft\Edge\User Data\WidevineCdm\<version>到<edgeDataDir>\WidevineCdm\<version>;
  2. 或者先用--user-data-dir=<edgeDataDir>手动启动一次 Edge,并在edge://components页面更新 "Widevine Content Decryption Module";
  3. 之后由于 profile 持久存在,Edge 会自动持续更新 CDM,无需再次人工干预。

Chrome 与 Firefox 不受此问题影响:其当前 CDM 随浏览器安装分发,或在需要时按需拉取进临时 profile。

七、常见问题与排障要点

  • 功能测试报错找不到 dist 产物:DashJsAdapter直接 importdist/modern/esm/dash.all.min.js,务必先执行npm run build。
  • DRM 向量在 Edge 上大面积失败:优先排查是否为持久化 profile 未初始化(见第六节),其次确认是否处于锁定/RDP 会话。
  • 同一向量在不同浏览器行为不一致:检查platformOverrides与excludedPlatforms是否按浏览器做了正确分流;从源码结构看,这是测试框架为跨浏览器差异提供的标准手段。
  • 只想跑某个用例:修改 streams 配置的testfiles.included为具体条目(如["playback/play"]),或在向量级用includedTestfiles收窄;Karma 也支持通过--grep(mocha 的 grep 能力,参考npm run test-refplayer的用法)按用例名过滤。
  • 本地网络受限跑不了外网向量:可将向量url换成/base/test/functional/content/下的内置 MPD(如gap/、multi-period/、preload/目录中的文件),这些 MPD 由 Karma 静态伺服并带 CORS 头。

八、小结

dash.js 的功能测试套件是一套以"真实浏览器 + 真实媒体流 + 播放器公开 API"为核心的三层体系:配置层(karma.functional.conf.cjs+ 环境配置 + 流配置)决定"在哪跑、跑什么";用例层(Constants.js定义清单与阈值、Utils.js负责向量筛选、各test/目录下的用例文件与common.js负责断言)决定"怎么验证";适配层(DashJsAdapter桥接 dist 构建产物与播放器 API、view/index.html提供执行上下文)决定"如何驱动"。同时,Windows + Edge + Widevine 的组合也提醒我们:功能测试的正确性不仅取决于代码,还与浏览器 profile 生命周期、显示会话状态等环境因素强相关。理解这套机制后,无论是为 dash.js 贡献新用例、排查回归,还是在 CI 中落地跨浏览器功能验证,都有了完整的入手路径。

  • 音视频
  • 前端

【免费下载链接】dash.js

A reference client implementation for the playback of MPEG DASH via Javascript and compliant browsers.

项目地址:https://gitcode.com/gh_mirrors/da/dash.js
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询