Files
asepharyana 1cdb82a76f Sync config from arch
- hypr/apps.lua
- hypr/autostart.lua
- hypr/envs.lua
- hypr/hyprland.lua
- hypr/hyprsunset.conf
- hypr/input.lua
- hypr/looknfeel.lua
- hypr/omasettings.lua
- hypr/xdph.conf
- omarchy/branding/about.txt
- omarchy/branding/screensaver.txt
- omarchy/extensions/omarchy-menu.jsonc
- omarchy/hooks/battery-low.d/play-warning-sound.sample
- omarchy/hooks/font-set.d/show-font-notification.sample
- omarchy/hooks/post-boot.d/weather.sample
- omarchy/hooks/post-update.d/install-voxtype.hook
- omarchy/hooks/post-update.d/setup-agent.hook
- omarchy/hooks/post-update.d/setup-fingerprint.hook
- omarchy/hooks/post-update.d/show-update-notification.sample
- omarchy/hooks/pre-refresh-pacman.d/add-custom-repo.sample
- omarchy/hooks/theme-set.d/show-theme-notification.sample
- omarchy/shell.json
- omarchy/shell.toml
- omarchy/theme.name
- omarchy/themes/azure-glow/README.md
- omarchy/themes/azure-glow/alacritty.toml
- omarchy/themes/azure-glow/btop.theme
- omarchy/themes/azure-glow/hyprland.conf
- omarchy/themes/azure-glow/hyprlock.conf
- omarchy/themes/azure-glow/icons.theme
- … 269 more
2026-09-23 15:19:12 +07:00

873 lines
46 KiB
Bash

#!/bin/bash
set -euo pipefail
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
run_node_test <<'JS'
const activity = requireFromRoot('Model.js')
const first = activity.parseSnapshot([
'schema\tactivity-resources\t1',
'sample\t1000',
'frequency\tcpu\t2400\tMHz',
'frequency\tmemory\t6400\tMT/s',
'cpu\tcpu\t1000\t700',
'cpu\tcpu0\t500\t350',
'cpu\tcpu1\t500\t350',
'memory\t1000\t400\t200\t150\t125',
'tasks\t2\t100',
'network\teth0\t10000\t20000\tup\t1\t1',
'network\ttailscale0\t500000\t600000\tup\t0\t0',
'disk\t8:0\tsda\t100\t200',
''
].join('\n'))
assertEqual(first.sample, 1000, 'activity parses the monotonic resource sample')
assertEqual(first.cpuFrequencyMHz, 2400, 'activity parses the average current CPU clock')
assertEqual(first.memorySpeedMTs, 6400, 'activity parses the configured DDR transfer rate')
assertEqual(first.cores.length, 2, 'activity parses per-core counters')
assertEqual(first.memory.cached, 125 * 1024, 'activity parses reclaimable cache')
assertEqual(first.memory.swapTotal, 200 * 1024, 'activity parses swap counters')
assertEqual(
activity.parseSnapshot('schema\tactivity-processes\t1\nsample\t1000\n').schema,
0,
'activity rejects a mismatched snapshot contract'
)
const second = activity.parseSnapshot([
'schema\tactivity-resources\t1',
'sample\t1001',
'frequency\tcpu\t2600\tMHz',
'frequency\tmemory\t6400\tMT/s',
'cpu\tcpu\t1200\t800',
'cpu\tcpu0\t600\t390',
'cpu\tcpu1\t600\t410',
'memory\t1000\t350\t200\t100\t120',
'tasks\t3\t110',
'network\teth0\t12048\t21024\tup\t1\t1',
'network\ttailscale0\t900000\t1000000\tup\t0\t0',
'disk\t8:0\tsda\t108\t204',
''
].join('\n'))
const metrics = activity.nextMetrics(first, second, {}, 2)
assertEqual(metrics.cpu, 50, 'activity derives CPU usage from counter deltas')
assertEqual(metrics.memory, 65, 'activity derives memory usage')
assertEqual(metrics.swap, 50, 'activity derives swap usage')
assertEqual(metrics.download, 2048, 'activity derives network receive rate')
assertEqual(metrics.upload, 1024, 'activity derives network transmit rate')
assertEqual(metrics.diskRead, 4096, 'activity derives disk read rate')
assertEqual(metrics.diskWrite, 2048, 'activity derives disk write rate')
assertEqual(metrics.diskDevices.length, 1, 'activity keeps a single disk as one device')
assertEqual(metrics.diskDevices[0].name, 'sda', 'activity names the sampled disk')
assertEqual(activity.diskCycleItems(metrics).length, 1, 'activity does not page a single disk')
assertEqual(metrics.cores[0].usage, 60, 'activity derives per-core usage')
assertEqual(metrics.networkInterface, 'eth0', 'activity follows the default interface without double-counting tunnels')
const firstMultiDisk = activity.parseSnapshot([
'schema\tactivity-resources\t1',
'sample\t2000',
'disk\t8:0\tsda\t100\t200',
'disk\t259:0\tnvme0n1\t50\t80',
''
].join('\n'))
const secondMultiDisk = activity.parseSnapshot([
'schema\tactivity-resources\t1',
'sample\t2001',
'disk\t8:0\tsda\t108\t204',
'disk\t259:0\tnvme0n1\t58\t88',
''
].join('\n'))
const multiDisk = activity.nextMetrics(firstMultiDisk, secondMultiDisk, {}, 4)
assertEqual(multiDisk.diskRead, 8192, 'activity sums read rates across disks')
assertEqual(multiDisk.diskWrite, 6144, 'activity sums write rates across disks')
assertEqual(multiDisk.diskDevices.length, 2, 'activity keeps per-disk rates')
assertEqual(multiDisk.diskDevices[0].name, 'nvme0n1', 'activity sorts disks by name')
assertEqual(multiDisk.diskDevices[0].read, 4096, 'activity derives the first disk read rate')
assertEqual(multiDisk.diskDevices[1].write, 2048, 'activity derives the second disk write rate')
const diskItems = activity.diskCycleItems(multiDisk)
assertEqual(diskItems.length, 3, 'activity prepends an All slot when there are multiple disks')
assertEqual(diskItems[0].id, 'all', 'activity starts the disk pager on the combined total')
assertEqual(diskItems[0].read, 8192, 'activity All slot uses the combined read rate')
assertEqual(activity.selectDiskItem(diskItems, '8:0').name, 'sda', 'activity can select a named disk')
assertEqual(activity.cycleItemId(diskItems, 'all', 'id', 1), '259:0', 'activity cycles from All to the first named disk')
assertEqual(activity.cycleItemId(diskItems, '8:0', 'id', 1), 'all', 'activity wraps the disk pager')
const hotplugDisk = activity.parseSnapshot([
'schema\tactivity-resources\t1',
'sample\t2002',
'disk\t8:0\tsda\t116\t208',
'disk\t8:16\tsdb\t10\t20',
'disk\t259:0\tnvme0n1\t66\t96',
''
].join('\n'))
const afterHotplug = activity.nextMetrics(secondMultiDisk, hotplugDisk, multiDisk, 4)
assertEqual(afterHotplug.diskRead, 8192, 'activity keeps existing disk rates when a disk appears')
assertEqual(afterHotplug.diskDevices.length, 3, 'activity adds a newly attached disk')
assertEqual(
afterHotplug.diskDevices.find(disk => disk.name === 'sdb').read,
0,
'activity baselines a newly attached disk at zero'
)
assertEqual(afterHotplug.diskHistories['259:0'].length, 2, 'activity keeps per-disk sparkline history')
const withHistory = activity.nextMetrics(second, second, {
cpuHistory: [10, 20],
memoryHistory: [30, 40]
}, 2)
assertDeepEqual(withHistory.cpuHistory, [20, 0], 'activity caps history buffers')
assertEqual(activity.counterPercent({ total: 100, idle: 20 }, { total: 50, idle: 10 }), 0, 'activity handles reset CPU counters')
assertEqual(activity.counterRate(100, 50, 1), 0, 'activity handles reset byte counters')
const baseline = activity.baselineMetrics(second, {
cpu: 42,
download: 100,
upload: 50,
diskRead: 25,
diskWrite: 10,
cpuHistory: [20, 42],
memoryHistory: [60, 65]
})
assertEqual(baseline.cpu, 42, 'activity retains the last CPU rate while taking a fresh baseline')
assertEqual(baseline.memory, 65, 'activity immediately refreshes single-sample memory usage')
assertDeepEqual(baseline.cpuHistory, [20, 42], 'activity does not add zeroes while taking a fresh baseline')
const firstProcesses = activity.parseProcessSnapshot([
'schema\tactivity-processes\t1',
'sample\t100\t100',
'process\t42\talice\tS\t1000\t500\t100\tcode helper',
''
].join('\n'))
const secondProcesses = activity.parseProcessSnapshot([
'schema\tactivity-processes\t1',
'sample\t102\t100',
'process\t42\talice\tR\t1000\t600\t100\tcode helper',
''
].join('\n'))
const sampledProcesses = activity.nextProcesses(firstProcesses, secondProcesses, 1000 * 1024)
assertEqual(sampledProcesses[0].name, 'code helper', 'activity preserves process names with spaces')
assertEqual(sampledProcesses[0].user, 'alice', 'activity resolves process users')
assertEqual(sampledProcesses[0].cpu, 50, 'activity derives sampled process CPU')
assertEqual(sampledProcesses[0].memory, 10, 'activity derives process memory share')
assertEqual(sampledProcesses[0].elapsed, 92, 'activity derives process runtime from monotonic uptime')
const reusedPid = activity.parseProcessSnapshot(
'schema\tactivity-processes\t1\nsample\t104\t100\nprocess\t42\talice\tR\t2000\t900\t100\tnew process\n'
)
assertEqual(
activity.nextProcesses(secondProcesses, reusedPid, 1000 * 1024)[0].cpu,
0,
'activity does not carry CPU history across PID reuse'
)
const processes = [
{ pid: 3, name: 'firefox', user: 'alice', cpu: 4, memory: 10, elapsed: 50 },
{ pid: 1, name: 'quickshell', user: 'alice', cpu: 8, memory: 2, elapsed: 100 },
{ pid: 2, name: 'postgres', user: 'postgres', cpu: 1, memory: 20, elapsed: 200 }
]
assertDeepEqual(
activity.filterAndSortProcesses(processes, '', 'cpu', false).map(process => process.pid),
[1, 3, 2],
'activity sorts processes by CPU'
)
assertDeepEqual(
activity.filterAndSortProcesses(processes, 'post', 'memory', false).map(process => process.pid),
[2],
'activity filters processes by name and user'
)
assertEqual(
activity.processIdentityKey({ pid: 42, startTicks: 1234 }),
'42:1234',
'activity ties process selection to PID and sampled start time'
)
assertEqual(
activity.processActionBlockReason(
{ pid: 42, startTicks: 1234, state: 'S', user: 'alice', name: 'firefox' },
'alice'
),
'',
'activity allows an actionable same-user app'
)
assertEqual(
activity.processActionBlockReason(
{ pid: 42, startTicks: 1234, state: 'S', user: 'alice', name: 'quickshell' },
'alice'
),
'Desktop session is protected',
'activity gives immediate feedback for a protected session process'
)
assertEqual(
activity.processActionBlockReason(
{ pid: 42, startTicks: 1234, state: 'S', user: 'root', name: 'worker' },
'alice'
),
'Only your own processes can be ended',
'activity gives immediate feedback for a foreign process'
)
assertEqual(activity.formatBytes(1536, '/s'), '1.50 KiB/s', 'activity formats byte rates')
assertEqual(activity.formatDuration(42), '42s', 'activity formats short process runtimes')
assertEqual(activity.formatDuration(90061), '1d 1h', 'activity formats long durations')
assertEqual(activity.samplingProfile('Efficient').resources, 3000, 'activity offers a low-overhead sampling profile')
assertEqual(activity.samplingProfile('Balanced').processes, 3000, 'activity keeps the balanced process cadence')
assertEqual(activity.samplingProfile('Fast').resources, 750, 'activity offers a responsive sampling profile')
assertEqual(activity.samplingProfile('unknown').name, 'Balanced', 'activity safely defaults unknown sampling profiles')
assertEqual(activity.formatTemperature(20, 'Celsius'), '20°C', 'activity formats Celsius temperatures')
assertEqual(activity.formatTemperature(20, 'Fahrenheit'), '68°F', 'activity converts temperatures to Fahrenheit')
function topo(id, cls, domain, cluster, core) {
return { id, cls, domain, cluster, core, maxKhz: 0 }
}
function usages(count) {
return Array.from({ length: count }, (_, i) => ({ name: 'cpu' + i, usage: i + 1 }))
}
const panther = []
for (let i = 0; i < 4; i++) panther.push(topo(i, 'performance', '0-11', String(i), i))
for (let i = 4; i < 8; i++) panther.push(topo(i, 'efficiency', '0-11', '4-7', i))
for (let i = 8; i < 12; i++) panther.push(topo(i, 'efficiency', '0-11', '8-11', i))
for (let i = 12; i < 16; i++) panther.push(topo(i, 'lowpower', '12-15', '12-15', i))
const pantherDie = activity.coreLayout(usages(16), panther)
assertEqual(pantherDie.mode, 'die', 'activity uses a specialized die for P+E+LP-E')
assertEqual(pantherDie.domains.length, 2, 'activity separates Panther Lake cache domains')
assertDeepEqual(pantherDie.domains[0].rows.map(row => row.kind + ':' + row.cells.length),
['performance:4', 'efficiency:4', 'efficiency:4'],
'activity places 4 P-cores above two E-core rows in the shared-cache square')
assertEqual(pantherDie.domains[1].rows[0].kind, 'lowpower', 'activity keeps LP-E cores in their own square')
assertEqual(pantherDie.domains[1].rows[0].cells.length, 4, 'activity shows four LP-E cores')
const pantherPaint = activity.flattenCoreLayout(pantherDie, {
gap: 2, domainGap: 4, pad: 2, performance: 9, efficiency: 6, same: 7
})
assertEqual(pantherPaint.frames.length, 2, 'activity paints a frame per cache domain')
assertEqual(pantherPaint.frames[0].h, pantherPaint.frames[1].h,
'activity keeps both cache squares the same height')
assertEqual(pantherPaint.frames[1].w < pantherPaint.frames[0].w, true,
'activity rotates the LP-E cache into a narrower column')
assertEqual(pantherPaint.cells.length, 16, 'activity paints every hybrid core as a positioned cell')
assertEqual(pantherPaint.cells[0].size, 9, 'activity draws P-cores larger than E-cores')
assertEqual(pantherPaint.cells[4].size, 6, 'activity draws E-cores at the cluster size')
assertEqual(pantherPaint.cells[4].kind, 'efficiency', 'activity starts the E-core rows after the P-cores')
assertEqual(pantherPaint.cells[8].kind, 'efficiency', 'activity keeps a second E-core row in the shared-cache square')
assertEqual(pantherPaint.cells[12].kind, 'lowpower', 'activity paints LP-E cores after the shared-cache square')
assertEqual(pantherPaint.cells[12].x === pantherPaint.cells[15].x, true,
'activity stacks LP-E cores in a column')
assertEqual(pantherPaint.cells[15].y > pantherPaint.cells[12].y, true,
'activity spreads the LP-E column down the tall cache')
assertEqual(Math.abs(pantherPaint.cells[4].x - pantherPaint.cells[0].x) < 1, true,
'activity does not place the leftmost E-core outside the P-core row')
assertEqual(
Math.abs((pantherPaint.cells[7].x + pantherPaint.cells[7].size)
- (pantherPaint.cells[3].x + pantherPaint.cells[3].size)) < 1,
true,
'activity does not place the rightmost E-core outside the P-core row'
)
assertEqual(pantherPaint.cells[5].x > pantherPaint.cells[4].x, true,
'activity spreads E-cores between the P-core edges')
const lpFrame = pantherPaint.frames[1]
const lpMidX = pantherPaint.cells[12].x + pantherPaint.cells[12].size / 2
assertEqual(Math.abs(lpMidX - (lpFrame.x + lpFrame.w / 2)) < 1, true,
'activity centers LP-E cores horizontally in the small cache')
const lpTop = pantherPaint.cells[12].y - lpFrame.y
const lpBottom = (lpFrame.y + lpFrame.h) - (pantherPaint.cells[15].y + pantherPaint.cells[15].size)
assertEqual(Math.abs(lpTop - lpBottom) <= 1, true,
'activity centers the LP-E column vertically in the small cache')
const paintedAgain = activity.paintCoreLayout(
usages(16).map(core => ({ name: core.name, usage: core.usage + 5 })),
panther,
{ gap: 2, domainGap: 4, pad: 2, performance: 9, efficiency: 6, same: 7 },
'live'
)
const paintedFirst = activity.paintCoreLayout(
usages(16),
panther,
{ gap: 2, domainGap: 4, pad: 2, performance: 9, efficiency: 6, same: 7 },
'live'
)
assertEqual(paintedFirst.cells[0].x, paintedAgain.cells[0].x,
'activity reuses die geometry when only usage changes')
assertEqual(paintedFirst.frames, paintedAgain.frames,
'activity keeps cache-domain frames stable across usage samples')
assertEqual(paintedAgain.cells[3].usage > paintedFirst.cells[3].usage, true,
'activity still refreshes per-core usage on a cached die')
const meteor = []
for (let i = 0; i < 6; i++) meteor.push(topo(i, 'performance', '0-13', String(i), i))
for (let i = 6; i < 14; i++) meteor.push(topo(i, 'efficiency', '0-13', i < 10 ? '6-9' : '10-13', i))
for (let i = 14; i < 16; i++) meteor.push(topo(i, 'lowpower', '14-15', '14-15', i))
const meteorDie = activity.coreLayout(usages(16), meteor)
assertEqual(meteorDie.domains[0].rows[0].cells.length, 6, 'activity keeps six Meteor Lake P-cores in one row')
assertEqual(meteorDie.domains[1].rows[0].cells.length, 2, 'activity isolates Meteor Lake LP-E cores')
const meteorPaint = activity.flattenCoreLayout(meteorDie, {
gap: 2, domainGap: 4, pad: 2, performance: 9, efficiency: 6, same: 7
})
const meteorE = meteorPaint.cells.filter(cell => cell.kind === 'efficiency')
assertEqual(meteorE.length, 8, 'activity paints eight Meteor Lake E-cores')
assertEqual(meteorE.every(cell => cell.y === meteorE[0].y), true,
'activity keeps Meteor Lake E-cores on one row under six P-cores')
const alder = []
for (let i = 0; i < 16; i += 2) alder.push(topo(i, 'performance', '0-23', String(i), i), topo(i + 1, 'performance', '0-23', String(i), i))
for (let i = 16; i < 20; i++) alder.push(topo(i, 'efficiency', '0-23', '16-19', i))
for (let i = 20; i < 24; i++) alder.push(topo(i, 'efficiency', '0-23', '20-23', i))
const alderDie = activity.coreLayout(usages(24), alder)
assertEqual(alderDie.mode, 'die', 'activity specializes Alder Lake P+E')
assertEqual(alderDie.domains.length, 1, 'activity keeps a single Alder Lake cache domain together')
assertEqual(alderDie.domains[0].rows[0].cells.length, 8, 'activity collapses SMT siblings into one P-core cell')
assertEqual(alderDie.domains[0].rows[0].cells[0].logicals.length, 2, 'activity tracks both threads inside a P-core cell')
const alderPaint = activity.flattenCoreLayout(alderDie, {
gap: 2, domainGap: 4, pad: 2, performance: 9, efficiency: 6, same: 7
})
const alderE = alderPaint.cells.filter(cell => cell.kind === 'efficiency')
assertEqual(alderE.length, 8, 'activity paints eight Alder Lake E-cores')
assertEqual(alderE.every(cell => cell.y === alderE[0].y), true,
'activity keeps Alder Lake E-cores on one row under eight P-cores')
const raptor = []
for (let i = 0; i < 6; i++) raptor.push(topo(i, 'performance', '0-13', String(i), i))
for (let i = 6; i < 14; i++) raptor.push(topo(i, 'efficiency', '0-13', i < 10 ? '6-9' : '10-13', i))
const raptorPaint = activity.flattenCoreLayout(activity.coreLayout(usages(14), raptor), {
gap: 2, domainGap: 4, pad: 2, performance: 9, efficiency: 6, same: 7
})
const raptorE = raptorPaint.cells.filter(cell => cell.kind === 'efficiency')
assertEqual(raptorE.length, 8, 'activity paints eight Raptor Lake E-cores')
assertEqual(raptorE.every(cell => cell.y === raptorE[0].y), true,
'activity keeps Raptor Lake E-cores on one row under six P-cores')
const lunar = []
for (let i = 0; i < 4; i++) lunar.push(topo(i, 'performance', '0-3', String(i), i))
for (let i = 4; i < 8; i++) lunar.push(topo(i, 'lowpower', '4-7', '4-7', i))
const lunarDie = activity.coreLayout(usages(8), lunar)
assertEqual(lunarDie.domains.length, 2, 'activity splits Lunar Lake P-cores from LP-E')
assertEqual(lunarDie.domains[0].rows[0].kind, 'performance', 'activity shows Lunar Lake P-cores as the primary square')
const lunarPaint = activity.flattenCoreLayout(lunarDie, {
gap: 2, domainGap: 4, pad: 2, performance: 9, efficiency: 6, same: 7
})
assertEqual(lunarPaint.frames[1].y > lunarPaint.frames[0].y, true,
'activity stacks Lunar Lake cache squares instead of placing them in a row')
assertEqual(lunarPaint.frames[1].x >= lunarPaint.frames[0].x, true,
'activity centers the narrower Lunar Lake LP-E square under the P-cores')
const lunarP = lunarPaint.cells.filter(cell => cell.kind === 'performance')
const lunarLp = lunarPaint.cells.filter(cell => cell.kind === 'lowpower')
assertEqual(lunarP.every(cell => cell.y === lunarP[0].y), true,
'activity keeps Lunar Lake P-cores on one row')
assertEqual(lunarLp.every(cell => cell.y === lunarLp[0].y), true,
'activity keeps Lunar Lake LP-E cores on one row')
assertEqual(lunarLp[0].y > lunarP[0].y, true,
'activity places Lunar Lake LP-E under the P-cores')
assertEqual(lunarPaint.width < lunarPaint.frames[0].w + lunarPaint.frames[1].w,
true, 'activity stacks Lunar Lake caches to take less width')
const strix = []
for (let i = 0; i < 4; i++) strix.push(topo(i, 'performance', '0-3', '0-3', i))
for (let i = 4; i < 12; i++) strix.push(topo(i, 'efficiency', '4-11', '4-11', i))
const strixDie = activity.coreLayout(usages(12), strix)
assertEqual(strixDie.domains.length, 2, 'activity splits Strix Point Zen 5 and Zen 5c CCX squares')
assertEqual(strixDie.domains[0].rows[0].cells.length, 4, 'activity shows four Zen 5 cores in the larger square')
assertEqual(strixDie.domains[1].rows.length, 2, 'activity keeps Zen 5c cores as two rows in their CCX')
assertEqual(strixDie.domains[1].rows[0].cells.length, 4, 'activity shows four Zen 5c cores per row')
const strixPaint = activity.flattenCoreLayout(strixDie, {
gap: 2, domainGap: 4, pad: 2, performance: 9, efficiency: 6, same: 7
})
assertEqual(strixPaint.frames[1].y > strixPaint.frames[0].y, true,
'activity stacks Strix Point CCX squares instead of placing them in a row')
const strixP = strixPaint.cells.filter(cell => cell.kind === 'performance')
const strixE = strixPaint.cells.filter(cell => cell.kind === 'efficiency')
assertEqual(strixE[0].y > strixP[0].y, true,
'activity places Strix Point Zen 5c under the Zen 5 cores')
assertEqual(strixPaint.width < strixPaint.frames[0].w + strixPaint.frames[1].w,
true, 'activity stacks Strix Point caches to take less width')
const arm = []
for (let i = 0; i < 1; i++) arm.push(topo(i, 'performance', '0-7', '0', i))
for (let i = 1; i < 4; i++) arm.push(topo(i, 'efficiency', '0-7', '1-3', i))
for (let i = 4; i < 8; i++) arm.push(topo(i, 'lowpower', '0-7', '4-7', i))
const armDie = activity.coreLayout(usages(8), arm)
assertEqual(armDie.domains.length, 1, 'activity keeps a shared-DSU ARM cluster in one square')
assertDeepEqual(armDie.domains[0].rows.map(row => row.kind + ':' + row.cells.length),
['performance:1', 'efficiency:3', 'lowpower:4'],
'activity stacks ARM big.LITTLE tiers by class')
const armPaint = activity.flattenCoreLayout(armDie, {
gap: 2, domainGap: 4, pad: 2, performance: 9, efficiency: 6, same: 7
})
const armP = armPaint.cells.filter(cell => cell.kind === 'performance')
const armE = armPaint.cells.filter(cell => cell.kind === 'efficiency')
const armLp = armPaint.cells.filter(cell => cell.kind === 'lowpower')
assertEqual(armP.length, 1, 'activity paints one ARM big core')
assertEqual(armE.length, 3, 'activity paints three ARM middle cores')
assertEqual(armLp.length, 4, 'activity paints four ARM little cores')
assertEqual(armE.every(cell => cell.y === armE[0].y), true, 'activity keeps ARM middle cores on one row')
assertEqual(armLp.every(cell => cell.y === armLp[0].y), true, 'activity keeps ARM little cores on one row')
assertEqual(armE[0].y > armP[0].y, true, 'activity stacks ARM tiers with the big core on top')
assertEqual(armLp[0].y > armE[0].y, true, 'activity stacks ARM little cores under the middle row')
const armMid = armPaint.frames[0].x + armPaint.frames[0].w / 2
assertEqual(Math.abs(armP[0].x + armP[0].size / 2 - armMid) < 1, true,
'activity centers the ARM big core over the shared DSU')
assertEqual(Math.abs((armLp[0].x + armLp[3].x + armLp[3].size) / 2 - armMid) < 1, true,
'activity centers the ARM little-core row in the shared DSU')
const uniform = Array.from({ length: 16 }, (_, i) => topo(i, 'performance', '0-15', String(i), i))
assertEqual(activity.coreLayout(usages(16), uniform).mode, 'uniform',
'activity keeps a homogeneous desktop CPU on the regular grid')
assertEqual(activity.coreLayout(usages(8), []).mode, 'uniform',
'activity falls back to the regular grid without topology')
const catalog = activity.previewCoreCatalog({
gap: 2, domainGap: 4, pad: 2, performance: 9, efficiency: 6, same: 7
})
assertEqual(catalog.length >= 8, true, 'activity offers a review catalog of CPU layouts')
assertDeepEqual(catalog.map(entry => entry.id), [
'panther-16', 'panther-8', 'meteor-16', 'alder-24', 'raptor-14',
'strix-12', 'arm-8', 'oryon-12', 'desktop-16'
], 'activity previews the researched hybrid and homogeneous layouts')
assertEqual(catalog[0].paint.frames.length, 2, 'activity catalog paints the Panther Lake die')
assertEqual(catalog[7].mode, 'uniform', 'activity catalog keeps Snapdragon X Elite on the regular grid')
const parsedTopo = activity.parseSnapshot([
'schema\tactivity-resources\t1',
'sample\t1',
'cpu-topo\t0\tperformance\t0-11\t0\t0\t4800000',
'cpu-topo\t4\tefficiency\t0-11\t4-7\t4\t3700000',
'cpu-topo\t12\tlowpower\t12-15\t12-15\t12\t3300000',
''
].join('\n'))
assertEqual(parsedTopo.topology.length, 3, 'activity parses cpu-topo lines into the resource snapshot')
assertEqual(parsedTopo.topology[2].cls, 'lowpower', 'activity preserves LP-E classification')
const manifest = requireFromRoot('manifest.json')
const settingKeys = manifest.barWidget.schema.map(entry => entry.key)
assertDeepEqual(
settingKeys,
['samplingSpeed', 'historySamples', 'temperatureUnit', 'showFrequencies', 'openExpanded', 'processPowerEnabled'],
'activity publishes the same focused preferences offered by its settings menu'
)
JS
snapshot=$("$ROOT/activity-stats" --activity-resources)
grep -Eq '^schema[[:space:]]+activity-resources[[:space:]]+1$' <<<"$snapshot" || fail "activity collector emits its schema"
grep -Eq '^sample[[:space:]][0-9]+([.][0-9]+)?$' <<<"$snapshot" || fail "activity collector emits a monotonic sample"
grep -Eq '^cpu[[:space:]]+cpu[[:space:]]' <<<"$snapshot" || fail "activity collector emits CPU counters"
grep -Eq '^memory[[:space:]]' <<<"$snapshot" || fail "activity collector emits memory counters"
grep -Eq '^network[[:space:]]' <<<"$snapshot" || fail "activity collector emits network counters"
grep -Eq '^disk[[:space:]]' <<<"$snapshot" || fail "activity collector emits disk counters"
pass "activity collector emits the resource snapshot contract"
write_cpu_topo() {
local sys="$1" id="$2" core="$3" cluster="$4" l2="$5" l3="$6" khz="$7"
local cpu="$sys/devices/system/cpu/cpu$id"
mkdir -p "$cpu/topology" "$cpu/cache/index2" "$cpu/cpufreq"
printf '%s\n' "$core" >"$cpu/topology/core_id"
printf '%s\n' "$cluster" >"$cpu/topology/cluster_cpus_list"
printf '2\n' >"$cpu/cache/index2/level"
printf 'Unified\n' >"$cpu/cache/index2/type"
printf '%s\n' "$l2" >"$cpu/cache/index2/shared_cpu_list"
if [[ -n $l3 ]]; then
mkdir -p "$cpu/cache/index3"
printf '3\n' >"$cpu/cache/index3/level"
printf 'Unified\n' >"$cpu/cache/index3/type"
printf '%s\n' "$l3" >"$cpu/cache/index3/shared_cpu_list"
fi
printf '%s\n' "$khz" >"$cpu/cpufreq/cpuinfo_max_freq"
}
topo_root=$(mktemp -d)
mkdir -p "$topo_root/sys/devices/cpu_core" "$topo_root/sys/devices/cpu_atom"
printf '0-3\n' >"$topo_root/sys/devices/cpu_core/cpus"
printf '4-15\n' >"$topo_root/sys/devices/cpu_atom/cpus"
for id in 0 1 2 3; do
write_cpu_topo "$topo_root/sys" "$id" "$id" "$id" "$id" "0-11" 4800000
done
for id in 4 5 6 7; do
write_cpu_topo "$topo_root/sys" "$id" "$id" "4-7" "4-7" "0-11" 3700000
done
for id in 8 9 10 11; do
write_cpu_topo "$topo_root/sys" "$id" "$id" "8-11" "8-11" "0-11" 3700000
done
for id in 12 13 14 15; do
write_cpu_topo "$topo_root/sys" "$id" "$id" "12-15" "12-15" "" 3300000
done
topo_snapshot=$(
OMARCHY_SYSTEM_STATS_SYS_PATH="$topo_root/sys" \
"$ROOT/activity-stats" --activity-resources
)
rm -rf -- "$topo_root"
[[ $(grep -c $'^cpu-topo\t' <<<"$topo_snapshot") -eq 16 ]] ||
fail "activity collector does not emit a topology row for every logical CPU"
[[ $(grep -c $'^cpu-topo\t[0-9]\tperformance\t0-11\t' <<<"$topo_snapshot") -eq 4 ]] ||
fail "activity collector does not classify Intel P-cores"
[[ $(grep -c $'\tefficiency\t0-11\t' <<<"$topo_snapshot") -eq 8 ]] ||
fail "activity collector does not classify Intel E-cores that share the P-core L3"
[[ $(grep -c $'\tlowpower\t12-15\t' <<<"$topo_snapshot") -eq 4 ]] ||
fail "activity collector does not isolate LP-E cores without that L3"
pass "activity collector classifies hybrid CPU cache domains"
gpu_snapshot=$("$ROOT/activity-stats" --activity-gpus)
grep -Eq '^schema[[:space:]]+activity-gpus[[:space:]]+1$' <<<"$gpu_snapshot" ||
fail "activity GPU collector emits its schema"
grep -Eq '^sample[[:space:]][0-9]+([.][0-9]+)?$' <<<"$gpu_snapshot" ||
fail "activity GPU collector emits a monotonic sample"
pass "activity collector emits the independent GPU snapshot contract"
process_snapshot=$("$ROOT/activity-stats" --activity-processes)
grep -Eq '^schema[[:space:]]+activity-processes[[:space:]]+1$' <<<"$process_snapshot" || fail "activity process collector emits its schema"
grep -Eq '^sample[[:space:]][0-9]+([.][0-9]+)?[[:space:]][0-9]+[[:space:]][0-9]+$' <<<"$process_snapshot" ||
fail "activity process collector emits its sample and CPU clocks"
grep -Eq '^process[[:space:]]' <<<"$process_snapshot" || fail "activity process collector emits process rows"
pass "activity collector emits the process snapshot contract"
[[ -x $ROOT/activity-sampler ]] || fail "native activity sampler is executable"
[[ $("$ROOT/activity-sampler" --version) == "activity-sampler 2.1.1" ]] ||
fail "native activity sampler reports the release protocol version"
[[ ! -e $ROOT/activity-process-stats ]] ||
fail "legacy process helper remains beside the unified native sampler"
pass "activity ships one native sampler instead of a process-helper chain"
reader_snapshot=$(
printf 'resources\nprocesses\nprocesses\nthermals\ngpus\nstorage\n' |
"$ROOT/activity-stats" --activity-reader
)
[[ $(grep -c '^snapshot-end' <<<"$reader_snapshot") -eq 6 ]] ||
fail "activity reader does not frame every requested snapshot"
grep -Fq $'snapshot-end\tresources' <<<"$reader_snapshot" ||
fail "activity reader does not frame resource snapshots"
grep -Fq $'snapshot-end\tprocesses' <<<"$reader_snapshot" ||
fail "activity reader does not frame process snapshots"
[[ $(grep -c $'^snapshot-end\tprocesses$' <<<"$reader_snapshot") -eq 2 ]] ||
fail "activity reader does not serve repeated process snapshots"
grep -Fq $'snapshot-end\tthermals' <<<"$reader_snapshot" ||
fail "activity reader does not frame thermal snapshots"
grep -Fq $'snapshot-end\tgpus' <<<"$reader_snapshot" ||
fail "activity reader does not frame GPU snapshots"
grep -Fq $'snapshot-end\tstorage' <<<"$reader_snapshot" ||
fail "activity reader does not frame storage snapshots"
pass "activity reader serves independently requested snapshots in one native process"
coproc SELF_READER { exec "$ROOT/activity-stats" --activity-reader; }
self_reader_pid=$SELF_READER_PID
self_reader_input=${SELF_READER[1]}
self_reader_output=${SELF_READER[0]}
self_reader_snapshot=""
printf 'processes\n' >&"$self_reader_input"
while IFS= read -r -u "$self_reader_output" line; do
[[ $line == $'snapshot-end\tprocesses' ]] && break
self_reader_snapshot+="$line"$'\n'
done
process_children=$(<"/proc/$self_reader_pid/task/$self_reader_pid/children")
[[ -z $process_children ]] ||
fail "native activity reader spawned a child collector"
printf 'processes\n' >&"$self_reader_input"
while IFS= read -r -u "$self_reader_output" line; do
[[ $line == $'snapshot-end\tprocesses' ]] && break
done
process_children=$(<"/proc/$self_reader_pid/task/$self_reader_pid/children")
[[ -z $process_children ]] ||
fail "native activity reader spawned a child between process snapshots"
exec {self_reader_input}>&-
wait "$self_reader_pid"
pass "activity reader keeps all unprivileged sampling in one native process"
if grep -Fq $'process\t'"$self_reader_pid"$'\t' <<<"$self_reader_snapshot"; then
fail "activity reader reports its own sampling process as workload"
fi
pass "activity reader excludes its own sampling process"
fixture_root=$(mktemp -d)
trap 'rm -rf -- "$fixture_root"' EXIT
mkdir -p "$fixture_root/proc/101" "$fixture_root/proc/102" "$fixture_root/proc/103"
printf '200.00 100.00\n' >"$fixture_root/proc/uptime"
printf '%s\n' \
'101 (worker task) R 1 1 1 0 -1 0 0 0 0 0 12 8 0 0 20 0 3 0 1000 4096000 25' \
>"$fixture_root/proc/101/stat"
printf 'Name:\tworker task\nUid:\t1000\t1000\t1000\t1000\nVmRSS:\t120 kB\n' \
>"$fixture_root/proc/101/status"
printf '%s\n' \
'103 (kernel worker) I 2 0 0 0 0 2097152 0 0 0 0 0 0 0 0 20 0 1 0 1000 0 0' \
>"$fixture_root/proc/103/stat"
printf 'alice:x:1000:1000:Alice:/home/alice:/bin/bash\n' >"$fixture_root/passwd"
fixture_process_snapshot=$(
OMARCHY_SYSTEM_STATS_PROC_PATH="$fixture_root/proc" \
OMARCHY_SYSTEM_STATS_PASSWD_PATH="$fixture_root/passwd" \
OMARCHY_SYSTEM_STATS_CLOCK_TICKS=100 \
OMARCHY_SYSTEM_STATS_PAGE_SIZE=4096 \
"$ROOT/activity-stats" --activity-processes
)
grep -Fq $'process\t101\talice\tR\t1000\t20\t100\tworker task' \
<<<"$fixture_process_snapshot" || fail "activity process collector reads a stable fixture process"
[[ $(grep -c '^process' <<<"$fixture_process_snapshot") -eq 1 ]] ||
fail "activity process collector did not skip a vanished fixture PID"
pass "activity process collector skips PIDs that vanish during a snapshot"
if grep -Fq $'process\t103\t' <<<"$fixture_process_snapshot"; then
fail "activity process collector included a kernel thread"
fi
pass "activity process collector omits non-actionable kernel threads"
grep -Fq 'centerOnBar: false' "$ROOT/Panel.qml" ||
fail "activity panel keeps compact and expanded layouts on the same bar anchor"
pass "activity panel expansion preserves its bar anchor"
grep -Fq 'implicitWidth: button.implicitWidth' "$ROOT/Panel.qml" ||
fail "activity bar widget does not expose its icon width"
grep -Fq 'implicitHeight: button.implicitHeight' "$ROOT/Panel.qml" ||
fail "activity bar widget does not expose its icon height"
pass "activity bar widget exposes its icon geometry"
grep -Fq 'id: headerActions' "$ROOT/Panel.qml" ||
fail "activity header actions do not share a header row"
grep -A3 -F 'id: headerActions' "$ROOT/Panel.qml" |
grep -Fq 'anchors.verticalCenter: parent.verticalCenter' ||
fail "activity header controls are not vertically centered together"
pass "activity header actions stay aligned"
panel_file="$ROOT/Panel.qml"
controller_file="$ROOT/ActivityController.qml"
action_controller_file="$ROOT/ProcessActionController.qml"
if grep -Eq 'label: "LOAD"|detail: .*"LOAD ' "$panel_file"; then
fail "activity panel exposes the Linux load average"
fi
if grep -Fq 'LOGICAL CPUS' "$panel_file"; then
fail "activity panel still advertises logical CPU count as card filler"
fi
grep -Fq 'function cpuCardDetail()' "$panel_file" &&
grep -Fq 'Model.formatTemperature(cpuTemperature.value, temperatureUnitSetting)' "$panel_file" ||
fail "activity CPU card does not own the CPU temperature"
if grep -Fq 'component TemperatureBadge' "$panel_file"; then
fail "activity temperature still lives in the header"
fi
pass "activity keeps CPU temperature on the CPU card"
grep -Fq 'label: root.storageCardLabel()' "$panel_file" ||
fail "activity expanded details do not show disk storage"
grep -Fq 'label: root.gpuHeadingText()' "$panel_file" &&
grep -Fq 'gpuUsageText(adapters[0])' "$panel_file" ||
fail "activity expanded details do not show GPU adapters"
grep -Fq 'return "Shared"' "$panel_file" ||
fail "activity panel does not distinguish integrated shared GPU memory"
grep -Fq 'return "VRAM"' "$panel_file" ||
fail "activity panel does not label dedicated GPU memory"
grep -Fq 'function storageCardValue()' "$panel_file" &&
grep -Fq 'function storageCardDetail()' "$panel_file" &&
grep -Fq '" free"' "$panel_file" ||
fail "activity storage card does not show used storage and free space"
grep -Fq 'function cycleDisk(' "$panel_file" &&
grep -Fq 'function cycleStorage(' "$panel_file" &&
grep -Fq 'interactive: root.canCycleDisks' "$panel_file" &&
grep -Fq 'interactive: root.canCycleStorage' "$panel_file" &&
grep -Fq 'raw === "D" ? -1 : 1' "$panel_file" &&
grep -Fq 'raw === "V" ? -1 : 1' "$panel_file" &&
grep -Fq 'd/v cycle drives' "$panel_file" ||
fail "activity does not page Disk and Storage with click and d/v"
grep -Fq 'resetDriveSelection()' "$panel_file" ||
fail "activity does not reset drive selection when the panel closes"
if grep -Fq 'POWER & THERMALS' "$panel_file"; then
fail "activity expanded details still show power and thermals"
fi
if grep -Fq 'label: "RAM TOTAL"' "$panel_file" || grep -Fq 'label: "CORES"' "$panel_file"; then
fail "activity GPU card is still padded with unrelated system facts"
fi
pass "activity shows GPU and storage without filler metrics"
grep -Fq 'ActivityController {' "$panel_file" &&
grep -Fq 'active: root.opened' "$panel_file" &&
grep -Fq 'expanded: root.expanded' "$panel_file" ||
fail "activity panel does not delegate sampling lifecycle to its controller"
grep -Fq 'readonly property var snapshot: _snapshot' "$controller_file" &&
grep -Fq 'readonly property var processes: _processes' "$controller_file" ||
fail "activity controller does not expose read-only sampled state"
if grep -Eq '^[[:space:]]*Process[[:space:]]*[{]' "$panel_file"; then
fail "activity visual panel still owns a helper process"
fi
grep -Fq 'command: [root.statsHelperPath, "--activity-reader"]' "$controller_file" ||
fail "activity panel does not reuse a single unprivileged stats reader"
grep -Fq 'String(Qt.resolvedUrl("activity-sampler"))' "$controller_file" ||
fail "activity panel does not launch the native sampler directly"
grep -Fq '? [powerHelperPath]' "$controller_file" ||
fail "activity panel does not use the persistent guarded CPU-energy reader"
grep -Fq 'if (!active || !expanded || !processPowerEnabled' "$controller_file" &&
grep -Fq '|| processPowerUnavailable || _processPowerSamplePending) return' "$controller_file" ||
fail "activity panel samples process power outside the advanced view"
grep -Fq 'onExpandedChanged:' "$controller_file" &&
grep -Fq 'resetProcessPowerMeasurement()' "$controller_file" ||
fail "activity panel carries an energy baseline across collapsed time"
grep -Fq 'root.markProcessPowerUnavailable()' "$controller_file" ||
fail "activity panel does not clear stale watts after an energy read failure"
grep -Fq 'refreshProcessCycle()' "$controller_file" ||
fail "activity panel does not coordinate energy and process refreshes"
grep -Fq 'id: deadlineScheduler' "$controller_file" &&
grep -Fq 'function collectDueSnapshots()' "$controller_file" &&
grep -Fq '_resourceDue = nextDeadline(_resourceDue, refreshInterval, now)' "$controller_file" &&
grep -Fq '_processDue = nextDeadline(_processDue, processRefreshInterval, now)' "$controller_file" &&
grep -Fq '_thermalDue = nextDeadline(_thermalDue, thermalRefreshInterval, now)' "$controller_file" &&
grep -Fq '_gpuDue = nextDeadline(_gpuDue, gpuRefreshInterval, now)' "$controller_file" &&
grep -Fq '_storageDue = nextDeadline(_storageDue, storageRefreshInterval, now)' "$controller_file" ||
fail "activity panel does not coalesce its independent deadlines"
if grep -Fq 'repeat: true' "$controller_file"; then
fail "activity panel still wakes separate recurring timers"
fi
grep -Fq 'id: statsReaderWatchdog' "$controller_file" &&
grep -Fq 'id: processPowerWatchdog' "$controller_file" ||
fail "activity readers can remain stuck indefinitely"
grep -Fq 'powerStateSwitchingRoot: "switching-root"' "$controller_file" &&
grep -Fq 'readonly property string processPowerState: _processPowerState' "$controller_file" ||
fail "activity power reader lifecycle is not represented by an explicit state"
if grep -Eq 'processPowerRestartWithSudo|processPowerUseSudo|processPowerReaderReady[[:space:]]*:' "$controller_file"; then
fail "activity power reader still uses overlapping lifecycle booleans"
fi
if grep -Eq 'activity-package-power|refreshPackagePower|packagePowerProc' "$controller_file"; then
fail "activity panel still polls package power"
fi
grep -Fq 'if (expanded) sendStatsRequest("gpus")' "$controller_file" &&
grep -Fq '_gpuDue = 0' "$controller_file" &&
grep -Fq 'resetGpuMeasurement()' "$controller_file" ||
fail "activity panel samples GPU details while the advanced view is collapsed"
grep -Fq '_metrics = Model.baselineMetrics(next, metrics)' "$controller_file" &&
grep -Fq 'function resetSessionSampling()' "$controller_file" &&
grep -Fq 'if (_gpus.length === 0)' "$controller_file" ||
fail "activity panel does not retain valid display data while taking fresh baselines"
if grep -Fq ' _snapshot = Model.emptySnapshot()' "$controller_file" ||
grep -Fq ' _processes = []' "$controller_file" ||
grep -Fq ' _gpus = []' "$controller_file"; then
fail "activity panel clears stale display data when a sampling session changes"
fi
pass "activity controller coalesces deadlines and refreshes retained values without blanking"
grep -Fq 'label: "~W"' "$panel_file" ||
fail "activity process table does not label estimated CPU watts"
grep -Fq 'measuredPackagePowerText(processPower.watts)' "$panel_file" ||
fail "activity CPU summary does not expose measured total CPU-package power"
grep -Fq 'return "~" + watts.toFixed' "$panel_file" ||
fail "activity process watts are not visibly marked as estimates"
grep -Fq 'root.setSort("power")' "$panel_file" ||
fail "activity process power cannot be sorted"
grep -Fq 'if (nextKey === "power" && (!powerEstimatesEnabled || !processPower.available)) return' "$panel_file" ||
fail "activity process power sorting remains active without a measurement"
grep -Fq 'showProcessUserColumn' "$panel_file" &&
grep -Fq 'showProcessTimeColumn' "$panel_file" ||
fail "activity process table does not adapt its optional columns on narrow screens"
grep -Fq 'model: root.sortedProcesses' "$panel_file" ||
fail "activity process table silently caps the virtualized process list"
grep -Fq 'Model.formatBytes(snapshot.memory.cached)' "$panel_file" ||
fail "activity panel does not expose reclaimable memory cache"
[[ $(grep -Fc 'label: root.memoryCardLabel()' "$panel_file") -eq 2 ]] ||
fail "activity panel does not place RAM speed in both card headings"
[[ $(grep -Fc 'label: root.cpuCardLabel()' "$panel_file") -eq 2 ]] ||
fail "activity panel does not place CPU frequency in both card headings"
grep -Fq 'function memoryBreakdownText()' "$panel_file" &&
grep -Fq 'if (usedUnit && usedUnit === cacheUnit)' "$panel_file" &&
grep -Fq '" · cache " + cacheText' "$panel_file" ||
fail "activity RAM card does not show used memory and cache together"
grep -Fq 'snapshot.memorySpeedMTs, "MT/s"' "$panel_file" &&
grep -Fq 'snapshot.cpuFrequencyMHz, "MHz"' "$panel_file" &&
grep -Fq 'var frequency = gpuFrequencyText(adapters[0])' "$panel_file" &&
grep -Fq 'if (frequency === 0) return "IDLE"' "$panel_file" ||
fail "activity card headings do not distinguish DDR transfer rate from CPU and GPU clocks"
[[ $(grep -Fc 'badge: root.swapBadgeText()' "$panel_file") -eq 2 ]] &&
grep -Fq 'return "Swap " + percentText(metrics.swap)' "$panel_file" ||
fail "activity RAM cards do not expose swap usage"
if grep -Fq 'label: "DISK"' "$panel_file"; then
fail "activity disk card still shouts its heading"
fi
if grep -Fq 'component CoreLine' "$panel_file"; then
fail "activity still draws a labeled bar for every logical core"
fi
grep -Fq 'component CoreHeatGrid' "$panel_file" &&
grep -Fq 'paint: root.corePaintCompact' "$panel_file" &&
grep -Fq 'paint: root.corePaintExpanded' "$panel_file" &&
grep -Fq 'corePaintSizesExpanded' "$panel_file" &&
grep -Fq 'id: coreDie' "$panel_file" ||
fail "activity does not draw per-core load as a die on the CPU card"
if grep -Fq 'text: "Cores"' "$panel_file"; then
fail "activity still treats cores as a separate strip under the cards"
fi
grep -Fq 'component ProcessSortCell' "$panel_file" &&
grep -Fq 'sortKey: "cpu"' "$panel_file" &&
grep -Fq 'sortKey: "runtime"' "$panel_file" ||
fail "activity process column headers are not the sort controls"
if grep -Fq 'text: "More details"' "$panel_file"; then
fail "activity compact view still duplicates the expand control"
fi
grep -Fq 'property bool hintsVisible: false' "$panel_file" &&
grep -Fq 'function shortcutHintText()' "$panel_file" ||
fail "activity keyboard hints are not opt-in"
grep -Fq 'contentHeight: panel.fittedContentHeight(' "$panel_file" &&
grep -Fq 'contentLoader.item.implicitHeight' "$panel_file" ||
fail "activity panel does not size the window to its content"
if grep -Fq 'processFooterReserve' "$panel_file"; then
fail "activity still shrinks the process list to make room for keyboard hints"
fi
if grep -Fq 'Style.space(720)' "$panel_file" || grep -Fq 'Style.space(560))' "$panel_file"; then
fail "activity still caps the window below the height of keyboard hints"
fi
grep -Fq 'height: Style.space(300)' "$panel_file" ||
fail "activity expanded process list is not a stable height while hints open"
if grep -Fq 'Preview CPU layouts' "$panel_file" || grep -Fq 'layoutPreviewContent' "$panel_file"; then
fail "activity still ships the CPU layout preview in the production panel"
fi
if grep -Fq 'label: "GPU"' "$panel_file"; then
fail "activity compact view still shows a GPU row"
fi
grep -Fq 'id: settingsContent' "$panel_file" &&
grep -Fq 'label: "Update speed"' "$panel_file" &&
grep -Fq 'label: "Graph history"' "$panel_file" &&
grep -Fq 'label: "Temperature"' "$panel_file" &&
grep -Fq 'function persistSettings(values)' "$panel_file" &&
grep -Fq 'root.bar.shell.updateEntryInline(root.moduleName, entry)' "$panel_file" ||
fail "activity panel does not expose persistent personalization controls"
grep -Fq 'processPowerEnabled: root.powerEstimatesEnabled' "$panel_file" &&
grep -Fq 'onProcessPowerEnabledChanged:' "$controller_file" &&
grep -Fq 'visible: !compact && root.powerEstimatesEnabled' "$panel_file" ||
fail "activity power preference does not stop sampling and simplify the process table"
if grep -Fq 'text: "POWER"' "$panel_file"; then
fail "activity presents estimated per-process power as an exact measurement"
fi
pass "activity clearly labels and sorts estimated per-process CPU power"
grep -Fq 'if (cursorActive && selectedProcessIndex === index) return' "$panel_file" ||
fail "activity process rows remap pointer coordinates after already being selected"
grep -Fq 'hoverEnabled: !compact' "$panel_file" ||
fail "activity compact process rows subscribe to unused hover movement"
if grep -Fq 'animateCursor: true' "$panel_file"; then
fail "activity process row selection starts multi-frame hover animations"
fi
pass "activity process rows avoid input-rate pointer and animation work"
grep -Fq 'ConfirmDialog {' "$panel_file" ||
fail "activity process actions are not confirmed"
grep -Fq 'processConfirm.selectedIndex = 0' "$panel_file" ||
fail "activity process confirmation does not default to Cancel"
grep -Fq 'if (!root.settingsOpen && !processActions.confirmationOpen && root.cursorActive)' "$panel_file" ||
fail "activity process shortcut can act without an explicit selection"
grep -Fq 'ProcessActionController {' "$panel_file" &&
grep -Fq 'active: root.opened' "$panel_file" &&
grep -Fq 'enabled: !root.settingsOpen' "$panel_file" ||
fail "activity panel does not delegate guarded app actions to their controller"
grep -Fq 'function requestCloseProcess(process, index)' "$panel_file" &&
grep -Fq 'onClicked: root.requestCloseProcess(parent.processData, compact ? -1 : parent.rowIndex)' "$panel_file" ||
fail "activity process rows do not confirm close on left click"
if grep -Fq 'id: endProcessButton' "$panel_file" || grep -Fq 'id: endProcessHost' "$panel_file"; then
fail "activity still shows a separate End app button"
fi
grep -Fq 'Model.processIdentityKey' "$panel_file" ||
fail "activity process selection is not tied to its sampled start time"
grep -Fq 'signalHelperPath,' "$action_controller_file" ||
fail "activity panel does not use the guarded process signal helper"
grep -Fq 'action: "APP_TERM"' "$action_controller_file" ||
fail "activity panel does not resolve worker processes to their parent app"
grep -Fq 'confirmText: "Close"' "$panel_file" ||
fail "activity panel does not confirm closing the selected process"
grep -Fq 'Do you want to close ' "$panel_file" ||
fail "activity panel does not ask a simple close confirmation"
grep -Fq '_status = reason' "$action_controller_file" ||
fail "activity panel does not explain why a process cannot be closed"
grep -Fq '"hyprland"' "$ROOT/Model.js" ||
fail "activity panel does not protect the desktop compositor"
grep -Fq '"systemd-executo"' "$ROOT/Model.js" ||
fail "activity panel does not protect the kernel-truncated systemd executor name"
grep -Fq 'disposition === "graceful"' "$action_controller_file" &&
grep -Fq 'disposition === "escalated"' "$action_controller_file" ||
fail "activity app action status does not distinguish normal and forced closure"
if grep -Eq 'bash[[:space:]]+-c|pkexec' "$panel_file" "$action_controller_file"; then
fail "activity process actions invoke a shell or privilege prompt"
fi
pass "activity app actions use guarded identity and safe confirmation"