mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-13 07:46:17 -08:00
refactor(core): [CORE-22] validate compatible core and defer initial layout
This commit is contained in:
1 parent
5efc7f7699
commit
adec20213f
24 files changed
+77377
-50
No files matched your search
@@ -0,0 +1,150 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { spawn } from 'node:child_process'
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import process from 'node:process'
|
||||
import { chromium } from '@playwright/test'
|
||||
|
||||
const directory = fs.mkdtempSync('refactor/.cache/core22-profile-')
|
||||
const port = 8086
|
||||
const env = { ...process.env, ARTPLAYER_BROWSER_PORT: String(port) }
|
||||
delete env.ARTPLAYER_BROWSER_ARTIFACTS
|
||||
const server = spawn(process.execPath, ['test/browser/server.mjs'], { env, windowsHide: true, stdio: ['ignore', 'pipe', 'pipe'] })
|
||||
let serverLog = ''
|
||||
server.stdout.on('data', (chunk) => {
|
||||
serverLog += chunk
|
||||
})
|
||||
server.stderr.on('data', (chunk) => {
|
||||
serverLog += chunk
|
||||
})
|
||||
let browser
|
||||
try {
|
||||
await new Promise((resolve, reject) => {
|
||||
const timer = setTimeout(() => reject(new Error('Server startup timeout')), 60000)
|
||||
const close = (code) => {
|
||||
clearTimeout(timer)
|
||||
reject(new Error(`Server exited ${code}: ${serverLog}`))
|
||||
}
|
||||
server.once('exit', close)
|
||||
server.stdout.on('data', () => {
|
||||
if (serverLog.includes(`Browser test server: http://127.0.0.1:${port}`)) {
|
||||
clearTimeout(timer)
|
||||
server.removeListener('exit', close)
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
})
|
||||
browser = await chromium.launch()
|
||||
const page = await browser.newPage({ viewport: { width: 960, height: 720 } })
|
||||
const errors = []
|
||||
page.on('pageerror', error => errors.push(error.message))
|
||||
await page.goto(`http://127.0.0.1:${port}/test/player.html?core=candidate&chapter=published`)
|
||||
const manifest = await (await page.request.get(`http://127.0.0.1:${port}/test/manifest.json`)).json()
|
||||
fs.writeFileSync(path.join(directory, 'candidate.js'), await (await page.request.get(`http://127.0.0.1:${port}/candidate/artplayer.js`)).body())
|
||||
await page.evaluate(() => {
|
||||
window.profileSamples = []
|
||||
window.runProfile = async function (count) {
|
||||
function constructProfilePlayer() {
|
||||
return new window.Artplayer({ container: '.player', url: '/assets/sample/video.mp4', muted: true })
|
||||
}
|
||||
function destroyProfilePlayer(art) {
|
||||
art.destroy()
|
||||
}
|
||||
for (let index = 0; index < count; index++) {
|
||||
const start = performance.now()
|
||||
const art = constructProfilePlayer()
|
||||
const constructed = performance.now()
|
||||
try {
|
||||
await new Promise((resolve, reject) => {
|
||||
const timer = setTimeout(() => reject(new Error('Ready timed out')), 10000)
|
||||
art.once('ready', () => {
|
||||
clearTimeout(timer)
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
await art.play()
|
||||
await new Promise(resolve => art.video.requestVideoFrameCallback(() => resolve()))
|
||||
art.pause()
|
||||
const beforeDestroy = performance.now()
|
||||
destroyProfilePlayer(art)
|
||||
window.profileSamples.push({ constructorMs: constructed - start, destroyMs: performance.now() - beforeDestroy })
|
||||
}
|
||||
finally {
|
||||
art.destroy()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
await page.evaluate(() => window.runProfile(10))
|
||||
await page.evaluate(() => {
|
||||
window.profileSamples = []
|
||||
})
|
||||
const client = await page.context().newCDPSession(page)
|
||||
await client.send('Profiler.enable')
|
||||
await client.send('Profiler.setSamplingInterval', { interval: 100 })
|
||||
await client.send('Profiler.start')
|
||||
await page.evaluate(() => window.runProfile(60))
|
||||
const { profile } = await client.send('Profiler.stop')
|
||||
const nodes = new Map(profile.nodes.map(node => [node.id, node]))
|
||||
const parents = new Map()
|
||||
for (const node of profile.nodes) {
|
||||
for (const child of node.children || []) parents.set(child, node.id)
|
||||
}
|
||||
const totals = { constructor: 0, destroy: 0, other: 0 }
|
||||
const groups = { constructor: new Map(), destroy: new Map() }
|
||||
for (let index = 0; index < profile.samples.length; index++) {
|
||||
const leaf = nodes.get(profile.samples[index])
|
||||
let node = leaf
|
||||
let group = 'other'
|
||||
while (node) {
|
||||
if (node.callFrame.functionName === 'constructProfilePlayer') {
|
||||
group = 'constructor'
|
||||
break
|
||||
}
|
||||
if (node.callFrame.functionName === 'destroyProfilePlayer') {
|
||||
group = 'destroy'
|
||||
break
|
||||
}
|
||||
node = nodes.get(parents.get(node.id))
|
||||
}
|
||||
const duration = profile.timeDeltas[index]
|
||||
totals[group] += duration
|
||||
if (group !== 'other') {
|
||||
const frame = leaf.callFrame
|
||||
const key = `${frame.functionName || '(anonymous)'}:${frame.url}:${frame.lineNumber + 1}`
|
||||
const result = groups[group].get(key) || { frame, sampledUs: 0, samples: 0 }
|
||||
result.sampledUs += duration
|
||||
result.samples++
|
||||
groups[group].set(key, result)
|
||||
}
|
||||
}
|
||||
const summary = {
|
||||
kind: 'diagnostic-cpu-sampling',
|
||||
browser: browser.version(),
|
||||
node: process.version,
|
||||
manifest,
|
||||
intervalRequestedUs: 100,
|
||||
warmup: 10,
|
||||
repetitions: 60,
|
||||
totalsUs: totals,
|
||||
groups: Object.fromEntries(Object.entries(groups).map(([name, values]) => [name, [...values.values()].sort((a, b) => b.sampledUs - a.sampledUs)])),
|
||||
timings: await page.evaluate(() => window.profileSamples),
|
||||
errors,
|
||||
limit: 'Instrumented Chromium source-build diagnostic only; not an uninstrumented published/candidate performance comparison. Native frames and sampling gaps may be charged to their JS caller. Timings must not be used for performance acceptance.',
|
||||
}
|
||||
assert.deepEqual(errors, [])
|
||||
assert.equal(await page.evaluate(() => window.Artplayer.instances.length), 0)
|
||||
fs.writeFileSync(path.join(directory, 'profile.cpuprofile'), JSON.stringify(profile))
|
||||
fs.writeFileSync(path.join(directory, 'summary.json'), `${JSON.stringify(summary, null, 2)}\n`)
|
||||
console.log(directory)
|
||||
for (const [name, rows] of Object.entries(summary.groups)) console.log(name, totals[name], rows.slice(0, 12).map(row => ({ name: row.frame.functionName, line: row.frame.lineNumber + 1, sampledUs: row.sampledUs, samples: row.samples })))
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
await browser?.close()
|
||||
}
|
||||
finally {
|
||||
server.kill()
|
||||
fs.writeFileSync(path.join(directory, 'server.log'), serverLog)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user