361 lines
15 KiB
TypeScript
361 lines
15 KiB
TypeScript
/**
|
|
* Empirical Challenger Stress Test Suite for Milestone M8:
|
|
* Widescreen Docking Mathematics, Boundary Conditions, and Resolution Scaling.
|
|
*
|
|
* @author teamwork_preview_challenger
|
|
* Target: `src/ui/hud-manager.ts` (`computeDockingLayout`, `computeHudLayout`)
|
|
*/
|
|
|
|
import { describe, it, expect } from 'vitest'
|
|
import {
|
|
computeDockingLayout,
|
|
computeHudLayout,
|
|
type DockingLayout,
|
|
} from '../src/ui/hud-manager.ts'
|
|
|
|
// Mock minimal window/DOM globals if running in headless node environment
|
|
class MockWindow extends EventTarget {
|
|
innerWidth = 1920
|
|
innerHeight = 1080
|
|
devicePixelRatio = 1
|
|
}
|
|
|
|
if (typeof globalThis.window === 'undefined') {
|
|
// @ts-ignore
|
|
globalThis.window = new MockWindow()
|
|
}
|
|
if (typeof globalThis.HTMLInputElement === 'undefined') {
|
|
// @ts-ignore
|
|
globalThis.HTMLInputElement = class HTMLInputElement {}
|
|
}
|
|
if (typeof globalThis.HTMLSelectElement === 'undefined') {
|
|
// @ts-ignore
|
|
globalThis.HTMLSelectElement = class HTMLSelectElement {}
|
|
}
|
|
|
|
// Deterministic 32-bit PRNG (Mulberry32) for reproducible fuzzing
|
|
function createPrng(seed: number = 0xdeadbeef) {
|
|
let s = seed >>> 0
|
|
return function nextFloat(): number {
|
|
s = (s + 0x6d2b79f5) | 0
|
|
let t = Math.imul(s ^ (s >>> 15), 1 | s)
|
|
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t
|
|
return ((t ^ (t >>> 14)) >>> 0) / 4294967296
|
|
}
|
|
}
|
|
|
|
// Independent Reference Oracle for Differential Testing
|
|
function referenceDockingOracle(w: number, h: number, dpr: number = 1): DockingLayout {
|
|
const safeW = Number.isFinite(w) && w > 0 ? w : 1
|
|
const safeH = Number.isFinite(h) && h > 0 ? h : 1
|
|
const safeDpr = Number.isFinite(dpr) && dpr > 0 ? dpr : 1
|
|
|
|
const uiScale = Math.min(safeW / 800, safeH / 600)
|
|
const offsetX = Math.max(0, (safeW - 800 * uiScale) / 2)
|
|
const offsetY = Math.max(0, safeH - 600 * uiScale)
|
|
|
|
const marginW = uiScale > 0 ? offsetX / uiScale : 0
|
|
const leftDockX = -marginW || 0
|
|
const rightDockX = 400 + marginW
|
|
const channelLeft = 400 - marginW
|
|
const channelRight = 400 + marginW
|
|
const channelWidthLogical = 2 * marginW
|
|
const channelWidthCss = 2 * offsetX
|
|
const isWidescreen = marginW > 0
|
|
|
|
return {
|
|
uiScale,
|
|
offsetX,
|
|
offsetY,
|
|
dpr: safeDpr,
|
|
physicalScale: uiScale * safeDpr,
|
|
physicalOffsetX: Math.round(offsetX * safeDpr),
|
|
physicalOffsetY: Math.round(offsetY * safeDpr),
|
|
marginW,
|
|
leftDockX,
|
|
rightDockX,
|
|
channelLeft,
|
|
channelRight,
|
|
channelWidthLogical,
|
|
channelWidthCss,
|
|
isWidescreen,
|
|
}
|
|
}
|
|
|
|
describe('Challenger M8: Widescreen Docking Mathematics Stress Test Suite', () => {
|
|
describe('1. Full Spectrum Resolution Matrix Stress Testing', () => {
|
|
const STANDARD_RESOLUTIONS: Array<{
|
|
name: string
|
|
w: number
|
|
h: number
|
|
expectedAspect: string
|
|
expectedWidescreen: boolean
|
|
}> = [
|
|
{ name: 'SVGA 4:3 (Classic)', w: 800, h: 600, expectedAspect: '4:3', expectedWidescreen: false },
|
|
{ name: 'XGA 4:3', w: 1024, h: 768, expectedAspect: '4:3', expectedWidescreen: false },
|
|
{ name: 'HD 16:9', w: 1280, h: 720, expectedAspect: '16:9', expectedWidescreen: true },
|
|
{ name: 'WXGA 16:10', w: 1280, h: 800, expectedAspect: '16:10', expectedWidescreen: true },
|
|
{ name: 'HD+ 16:9', w: 1366, h: 768, expectedAspect: '16:9', expectedWidescreen: true },
|
|
{ name: 'WSXGA 16:10', w: 1440, h: 900, expectedAspect: '16:10', expectedWidescreen: true },
|
|
{ name: 'HD+ 16:9', w: 1600, h: 900, expectedAspect: '16:9', expectedWidescreen: true },
|
|
{ name: 'FHD 1080p 16:9', w: 1920, h: 1080, expectedAspect: '16:9', expectedWidescreen: true },
|
|
{ name: 'QHD 1440p 16:9', w: 2560, h: 1440, expectedAspect: '16:9', expectedWidescreen: true },
|
|
{ name: 'UWQHD 21:9', w: 3440, h: 1440, expectedAspect: '21:9', expectedWidescreen: true },
|
|
{ name: '4K UHD 16:9', w: 3840, h: 2160, expectedAspect: '16:9', expectedWidescreen: true },
|
|
{ name: 'Super Ultrawide 32:9', w: 5120, h: 1440, expectedAspect: '32:9', expectedWidescreen: true },
|
|
]
|
|
|
|
it('evaluates computeDockingLayout accurately across all 12 standard resolutions', () => {
|
|
for (const res of STANDARD_RESOLUTIONS) {
|
|
const layout = computeDockingLayout(res.w, res.h)
|
|
|
|
expect(layout.isWidescreen, `${res.name} (${res.w}x${res.h}) widescreen flag`).toBe(
|
|
res.expectedWidescreen,
|
|
)
|
|
|
|
// Basic sanity
|
|
expect(layout.uiScale).toBeGreaterThan(0)
|
|
expect(layout.marginW).toBeGreaterThanOrEqual(0)
|
|
|
|
// Invariant: channelRight >= channelLeft and channelWidthLogical >= 0 (zero panel overlap)
|
|
expect(layout.channelRight).toBeGreaterThanOrEqual(layout.channelLeft)
|
|
expect(layout.channelWidthLogical).toBeGreaterThanOrEqual(0)
|
|
const overlap = Math.max(0, layout.channelLeft - layout.channelRight)
|
|
expect(overlap, `${res.name} panel overlap`).toBe(0)
|
|
|
|
// Invariant: leftDockX == -marginW and rightDockX == 400 + marginW
|
|
expect(layout.leftDockX).toBeCloseTo(-layout.marginW, 6)
|
|
expect(layout.rightDockX).toBeCloseTo(400 + layout.marginW, 6)
|
|
|
|
// Invariant: channelWidthCss == 2 * offsetX == cssW - 800 * uiScale
|
|
if (res.w / res.h >= 4 / 3 - 1e-6) {
|
|
expect(layout.channelWidthCss).toBeCloseTo(2 * layout.offsetX, 6)
|
|
expect(layout.channelWidthCss).toBeCloseTo(res.w - 800 * layout.uiScale, 6)
|
|
}
|
|
}
|
|
})
|
|
|
|
it('fuzzes across 300 randomly generated resolutions (widths 320 to 7680, heights 240 to 4320)', () => {
|
|
const prng = createPrng(0x1337c0de)
|
|
const FUZZ_COUNT = 300
|
|
|
|
for (let i = 0; i < FUZZ_COUNT; i++) {
|
|
// Generate diverse viewport sizes: widths 320..7680, heights 240..4320
|
|
const cssW = Math.round(320 + prng() * (7680 - 320))
|
|
const cssH = Math.round(240 + prng() * (4320 - 240))
|
|
const dpr = 0.5 + prng() * 3.5
|
|
|
|
const layout = computeDockingLayout(cssW, cssH, dpr)
|
|
const oracle = referenceDockingOracle(cssW, cssH, dpr)
|
|
|
|
// Differential assertion against oracle
|
|
expect(layout.uiScale).toBeCloseTo(oracle.uiScale, 8)
|
|
expect(layout.offsetX).toBeCloseTo(oracle.offsetX, 8)
|
|
expect(layout.offsetY).toBeCloseTo(oracle.offsetY, 8)
|
|
expect(layout.marginW).toBeCloseTo(oracle.marginW, 8)
|
|
expect(layout.leftDockX).toBeCloseTo(oracle.leftDockX, 8)
|
|
expect(layout.rightDockX).toBeCloseTo(oracle.rightDockX, 8)
|
|
expect(layout.channelLeft).toBeCloseTo(oracle.channelLeft, 8)
|
|
expect(layout.channelRight).toBeCloseTo(oracle.channelRight, 8)
|
|
expect(layout.channelWidthLogical).toBeCloseTo(oracle.channelWidthLogical, 8)
|
|
expect(layout.channelWidthCss).toBeCloseTo(oracle.channelWidthCss, 8)
|
|
expect(layout.isWidescreen).toBe(oracle.isWidescreen)
|
|
|
|
// Invariant 1: channelRight >= channelLeft and channelWidthLogical >= 0 (zero overlap)
|
|
expect(layout.channelRight).toBeGreaterThanOrEqual(layout.channelLeft)
|
|
expect(layout.channelWidthLogical).toBeGreaterThanOrEqual(0)
|
|
const overlap = Math.max(0, layout.channelLeft - layout.channelRight)
|
|
expect(overlap).toBe(0)
|
|
|
|
// Invariant 2: channelWidthCss == 2 * offsetX == cssW - 800 * uiScale for aspect >= 4:3
|
|
const aspectRatio = cssW / cssH
|
|
if (aspectRatio >= 4 / 3) {
|
|
expect(Math.abs(layout.channelWidthCss - 2 * layout.offsetX)).toBeLessThan(1e-6)
|
|
expect(Math.abs(layout.channelWidthCss - (cssW - 800 * layout.uiScale))).toBeLessThan(1e-6)
|
|
}
|
|
|
|
// Invariant 3: leftDockX == -marginW and rightDockX == 400 + marginW
|
|
expect(Math.abs(layout.leftDockX - (-layout.marginW))).toBeLessThan(1e-6)
|
|
expect(Math.abs(layout.rightDockX - (400 + layout.marginW))).toBeLessThan(1e-6)
|
|
}
|
|
})
|
|
})
|
|
|
|
describe('2. Classic 800x600 & 4:3 Bit-Exact Parity', () => {
|
|
it('verifies bit-exact D2 classic parity at 800x600', () => {
|
|
const docking = computeDockingLayout(800, 600)
|
|
|
|
// Bit-exact value assertions per spec
|
|
expect(docking.marginW).toBe(0)
|
|
expect(docking.leftDockX).toBe(0)
|
|
expect(docking.rightDockX).toBe(400)
|
|
expect(docking.channelWidthLogical).toBe(0)
|
|
expect(docking.channelLeft).toBe(400)
|
|
expect(docking.channelRight).toBe(400)
|
|
expect(docking.channelWidthCss).toBe(0)
|
|
expect(docking.isWidescreen).toBe(false)
|
|
expect(docking.uiScale).toBe(1.0)
|
|
expect(docking.offsetX).toBe(0)
|
|
expect(docking.offsetY).toBe(0)
|
|
})
|
|
|
|
it('verifies bit-exact 4:3 scaling parity at 1024x768', () => {
|
|
const docking = computeDockingLayout(1024, 768)
|
|
|
|
// Bit-exact value assertions per spec
|
|
expect(docking.marginW).toBe(0)
|
|
expect(docking.leftDockX).toBe(0)
|
|
expect(docking.rightDockX).toBe(400)
|
|
expect(docking.channelWidthLogical).toBe(0)
|
|
expect(docking.channelLeft).toBe(400)
|
|
expect(docking.channelRight).toBe(400)
|
|
expect(docking.channelWidthCss).toBe(0)
|
|
expect(docking.isWidescreen).toBe(false)
|
|
expect(docking.uiScale).toBe(1.28)
|
|
expect(docking.offsetX).toBe(0)
|
|
expect(docking.offsetY).toBe(0)
|
|
})
|
|
|
|
it('verifies 4:3 aspect ratio maintains zero margin across multiple standard 4:3 resolutions', () => {
|
|
const fourByThreeList = [
|
|
[640, 480],
|
|
[800, 600],
|
|
[1024, 768],
|
|
[1152, 864],
|
|
[1280, 960],
|
|
[1400, 1050],
|
|
[1600, 1200],
|
|
[2048, 1536],
|
|
]
|
|
|
|
for (const [w, h] of fourByThreeList) {
|
|
const d = computeDockingLayout(w, h)
|
|
expect(d.marginW).toBe(0)
|
|
expect(d.leftDockX).toBe(0)
|
|
expect(d.rightDockX).toBe(400)
|
|
expect(d.channelLeft).toBe(400)
|
|
expect(d.channelRight).toBe(400)
|
|
expect(d.channelWidthLogical).toBe(0)
|
|
expect(d.channelWidthCss).toBe(0)
|
|
expect(d.offsetX).toBe(0)
|
|
expect(d.isWidescreen).toBe(false)
|
|
}
|
|
})
|
|
})
|
|
|
|
describe('3. Extreme Viewport Boundary Cases', () => {
|
|
it('handles narrow and portrait viewports without negative marginW or NaN', () => {
|
|
const portraitViewports = [
|
|
{ name: '600x800', w: 600, h: 800 },
|
|
{ name: 'Mobile 360x640', w: 360, h: 640 },
|
|
{ name: 'Mobile FHD 1080x1920', w: 1080, h: 1920 },
|
|
{ name: 'Mobile HD 720x1280', w: 720, h: 1280 },
|
|
{ name: 'iPhone 390x844', w: 390, h: 844 },
|
|
{ name: 'Ultra-thin column 100x1000', w: 100, h: 1000 },
|
|
]
|
|
|
|
for (const vp of portraitViewports) {
|
|
const layout = computeDockingLayout(vp.w, vp.h)
|
|
|
|
expect(Number.isNaN(layout.marginW), `${vp.name} marginW is NaN`).toBe(false)
|
|
expect(Number.isFinite(layout.marginW), `${vp.name} marginW is finite`).toBe(true)
|
|
expect(layout.marginW, `${vp.name} marginW === 0`).toBe(0)
|
|
expect(layout.leftDockX, `${vp.name} leftDockX === 0`).toBe(0)
|
|
expect(layout.rightDockX, `${vp.name} rightDockX === 400`).toBe(400)
|
|
expect(layout.channelLeft, `${vp.name} channelLeft === 400`).toBe(400)
|
|
expect(layout.channelRight, `${vp.name} channelRight === 400`).toBe(400)
|
|
expect(layout.channelWidthLogical, `${vp.name} channelWidthLogical === 0`).toBe(0)
|
|
expect(layout.channelWidthCss, `${vp.name} channelWidthCss === 0`).toBe(0)
|
|
expect(layout.isWidescreen, `${vp.name} isWidescreen is false`).toBe(false)
|
|
expect(layout.offsetX, `${vp.name} offsetX === 0`).toBe(0)
|
|
expect(layout.offsetY, `${vp.name} offsetY >= 0`).toBeGreaterThanOrEqual(0)
|
|
}
|
|
})
|
|
|
|
it('handles zero, negative, and invalid numerical inputs gracefully without crashes or division by zero', () => {
|
|
const adversarialInputs = [
|
|
{ name: '0x0', w: 0, h: 0 },
|
|
{ name: '-100x600', w: -100, h: 600 },
|
|
{ name: '800x-600', w: 800, h: -600 },
|
|
{ name: '-500x-500', w: -500, h: -500 },
|
|
{ name: 'NaN x 600', w: NaN, h: 600 },
|
|
{ name: '800 x NaN', w: 800, h: NaN },
|
|
{ name: 'Infinity x 600', w: Infinity, h: 600 },
|
|
{ name: '800 x -Infinity', w: 800, h: -Infinity },
|
|
]
|
|
|
|
for (const adv of adversarialInputs) {
|
|
expect(() => {
|
|
const layout = computeDockingLayout(adv.w, adv.h)
|
|
|
|
expect(Number.isFinite(layout.uiScale), `${adv.name} uiScale is finite`).toBe(true)
|
|
expect(layout.uiScale, `${adv.name} uiScale > 0`).toBeGreaterThan(0)
|
|
expect(Number.isFinite(layout.marginW), `${adv.name} marginW is finite`).toBe(true)
|
|
expect(layout.marginW, `${adv.name} marginW >= 0`).toBeGreaterThanOrEqual(0)
|
|
expect(Number.isFinite(layout.leftDockX), `${adv.name} leftDockX is finite`).toBe(true)
|
|
expect(Number.isFinite(layout.rightDockX), `${adv.name} rightDockX is finite`).toBe(true)
|
|
expect(Number.isFinite(layout.channelLeft), `${adv.name} channelLeft is finite`).toBe(true)
|
|
expect(Number.isFinite(layout.channelRight), `${adv.name} channelRight is finite`).toBe(true)
|
|
expect(layout.channelRight).toBeGreaterThanOrEqual(layout.channelLeft)
|
|
}).not.toThrow()
|
|
}
|
|
})
|
|
|
|
it('scales layout dimensions cleanly across fractional DPIs (1.25, 1.5, 1.75, 2.0, 2.625)', () => {
|
|
const fractionalDprs = [1.25, 1.5, 1.75, 2.0, 2.625]
|
|
const baseResolutions = [
|
|
[800, 600],
|
|
[1920, 1080],
|
|
[2560, 1440],
|
|
[3440, 1440],
|
|
]
|
|
|
|
for (const [w, h] of baseResolutions) {
|
|
for (const dpr of fractionalDprs) {
|
|
const layout = computeDockingLayout(w, h, dpr)
|
|
|
|
// Verification of physical scaling metrics
|
|
expect(layout.dpr).toBe(dpr)
|
|
expect(layout.physicalScale).toBeCloseTo(layout.uiScale * dpr, 6)
|
|
expect(layout.physicalOffsetX).toBe(Math.round(layout.offsetX * dpr))
|
|
expect(layout.physicalOffsetY).toBe(Math.round(layout.offsetY * dpr))
|
|
|
|
// DPR should not alter logical docking metrics
|
|
const baseLayout = computeDockingLayout(w, h, 1)
|
|
expect(layout.marginW).toBeCloseTo(baseLayout.marginW, 8)
|
|
expect(layout.leftDockX).toBeCloseTo(baseLayout.leftDockX, 8)
|
|
expect(layout.rightDockX).toBeCloseTo(baseLayout.rightDockX, 8)
|
|
expect(layout.channelLeft).toBeCloseTo(baseLayout.channelLeft, 8)
|
|
expect(layout.channelRight).toBeCloseTo(baseLayout.channelRight, 8)
|
|
expect(layout.channelWidthLogical).toBeCloseTo(baseLayout.channelWidthLogical, 8)
|
|
expect(layout.channelWidthCss).toBeCloseTo(baseLayout.channelWidthCss, 8)
|
|
}
|
|
}
|
|
})
|
|
|
|
it('verifies extreme gargantuan (8K, 16K) and microscopic viewports', () => {
|
|
// 8K UHD: 7680x4320 (16:9)
|
|
const uhd8k = computeDockingLayout(7680, 4320)
|
|
expect(uhd8k.uiScale).toBeCloseTo(7.2, 4)
|
|
expect(uhd8k.offsetX).toBe(960)
|
|
expect(uhd8k.marginW).toBeCloseTo(133.333333, 4)
|
|
expect(uhd8k.channelWidthCss).toBe(1920)
|
|
expect(uhd8k.channelWidthLogical).toBeCloseTo(266.666667, 4)
|
|
|
|
// 16K: 15360x8640 (16:9)
|
|
const uhd16k = computeDockingLayout(15360, 8640)
|
|
expect(uhd16k.uiScale).toBeCloseTo(14.4, 4)
|
|
expect(uhd16k.offsetX).toBe(1920)
|
|
expect(uhd16k.marginW).toBeCloseTo(133.333333, 4)
|
|
expect(uhd16k.channelWidthCss).toBe(3840)
|
|
|
|
// Microscopic viewports
|
|
const micro = computeDockingLayout(1, 1)
|
|
expect(micro.uiScale).toBeGreaterThan(0)
|
|
expect(micro.marginW).toBe(0)
|
|
expect(micro.channelWidthLogical).toBe(0)
|
|
expect(micro.channelWidthCss).toBe(0)
|
|
})
|
|
})
|
|
})
|