129 lines
8.4 KiB
Python
129 lines
8.4 KiB
Python
#!/usr/bin/env python3
|
|
"""0.33.6 plot size/scale checks in simulated Chromium, not a running RustProxy."""
|
|
from pathlib import Path
|
|
import re
|
|
from playwright.sync_api import sync_playwright
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
HTML = (ROOT / 'src/admin/web/index.html').read_text(encoding='utf-8')
|
|
CSS = (ROOT / 'src/admin/web/style.css').read_text(encoding='utf-8')
|
|
SCALES = (ROOT / 'src/admin/web/app/chart-scales.js').read_text(encoding='utf-8')
|
|
RATES = (ROOT / 'src/admin/web/app/rate-sampling.js').read_text(encoding='utf-8')
|
|
WIDE = (ROOT / 'src/admin/web/app/overview-wide-metrics.js').read_text(encoding='utf-8')
|
|
|
|
WIDE_IDS = ('wide_protocol_rates', 'wide_cpu', 'wide_memory', 'wide_cache_activity',
|
|
'wide_connections', 'wide_backend_health', 'wide_latency',
|
|
'wide_transform_activity', 'wide_compression_activity',
|
|
'www_requests', 'www_latency', 'www_failures', 'www_traffic', 'www_status_trend')
|
|
|
|
with sync_playwright() as api:
|
|
browser = api.chromium.launch(headless=True, executable_path='/usr/bin/chromium', args=['--no-sandbox'])
|
|
for width in (320, 375, 768, 1440, 1920):
|
|
page = browser.new_page(viewport={'width': width, 'height': 920})
|
|
errors = []
|
|
page.on('pageerror', lambda error: errors.append(str(error)))
|
|
page.set_content(HTML.replace('/assets/app___RUSTPROXY_ASSET_ID__.js', '')
|
|
.replace('/assets/style___RUSTPROXY_ASSET_ID__.css', ''))
|
|
page.add_style_tag(content=CSS)
|
|
page.evaluate('''() => {
|
|
document.documentElement.classList.remove('app-loading','app-startup-failed');
|
|
document.getElementById('workspace').hidden = false;
|
|
document.getElementById('panel-overview').hidden = false;
|
|
document.getElementById('dashboard-overview-grid').classList.add('is-aligned');
|
|
for (const card of document.querySelectorAll('#dashboard-overview-grid>[data-dashboard-widget]'))
|
|
card.hidden = !['wide_protocol_rates','wide_cpu','wide_memory','wide_cache_activity',
|
|
'wide_connections','wide_backend_health','wide_latency',
|
|
'wide_transform_activity','wide_compression_activity',
|
|
'www_requests','www_latency','www_failures','www_traffic','www_status_trend'].includes(card.dataset.dashboardWidget);
|
|
window.currentPanel='overview';
|
|
window.$ = id => document.getElementById(id);
|
|
window.fmt = (n, digits=0) => Number(n).toFixed(digits);
|
|
window.fmtBytes = n => `${n} B`;
|
|
window.rustProxyOverviewSamples = {getLimit:()=>60,onChange:()=>{}};
|
|
window.rustProxyRefresh = {current:()=>({overview_seconds:3})};
|
|
window.__clock = 1000;
|
|
Object.defineProperty(performance,'now',{configurable:true,value:()=>window.__clock});
|
|
}''')
|
|
for script in (RATES, SCALES, WIDE):
|
|
page.add_script_tag(content=script)
|
|
for density in ('normal','compact','ultra'):
|
|
measured = page.evaluate('''density => {
|
|
document.documentElement.dataset.dashboardDensity=density;
|
|
const results={};
|
|
for (const card of document.querySelectorAll('#dashboard-overview-grid > .overview-wide-chart')) {
|
|
const svg=card.querySelector('svg.overview-wide-svg');
|
|
if (!svg) continue;
|
|
const r=svg.getBoundingClientRect();
|
|
results[card.dataset.dashboardWidget]={height:r.height, width:r.width,
|
|
grid:svg.querySelector('.chart-grid').getAttribute('d'),
|
|
contentWidth:Math.hypot(svg.getScreenCTM().a,svg.getScreenCTM().b)*600};
|
|
}
|
|
document.getElementById('panel-transform').hidden=false;
|
|
document.getElementById('panel-cache').hidden=false;
|
|
for(const id of ['transform-live-chart','cache-live-chart'])document.getElementById(id).open=true;
|
|
results.transformDetails=document.getElementById('transform-live-activity-chart').getBoundingClientRect().height;
|
|
results.cacheDetails=document.getElementById('cache-live-activity-chart').getBoundingClientRect().height;
|
|
return results;
|
|
}''', density)
|
|
assert abs(measured['transformDetails'] - 180) < 1, (width,density,measured)
|
|
assert abs(measured['cacheDetails'] - 180) < 1, (width,density,measured)
|
|
for name in WIDE_IDS:
|
|
data=measured[name]
|
|
assert abs(data['height']-125)<1, (width,density,name,data)
|
|
assert data['width']>50 and abs(data['width']-data['contentWidth'])<2, (width,density,name,data)
|
|
assert data['grid']=='M0 47H600 M0 77H600 M0 107H600', (width,density,name,data)
|
|
result = page.evaluate('''() => {
|
|
const c=window.rustProxyChartScale;
|
|
const first=c.peak('probe:rate','rate',[[.2,.7]]);
|
|
const count=c.peak('probe:count','count',[[0,1]]);
|
|
const latency=c.peak('probe:ms','ms',[[.1,1.5]]);
|
|
const memory=c.peak('probe:bytes','bytes',[[1000]]);
|
|
const high=c.peak('probe:rate','rate',[[101]]);
|
|
const held=c.peak('probe:rate','rate',[[.3]]);
|
|
c.reset('probe:');
|
|
const reset=c.peak('probe:rate','rate',[[.3]]);
|
|
const percent=c.peak('probe:cpu','percent',[[.1,500]]);
|
|
const blank=c.peak('probe:blank','rate',[[null,NaN,Infinity]]);
|
|
const before={revision:10,live_system:{cpu_percent:0.7,process_cpu_percent:0.2},
|
|
metrics:{compression_gzip:50,compression_br:100,compression_zstd:10,compression_deflate:5},
|
|
transform_metrics:{applied:2,cache_hits:10,skipped:1,errors:0}};
|
|
window.rustProxyWideOverview.render(before);
|
|
window.__clock=4000;
|
|
before.metrics.compression_gzip+=3;before.metrics.compression_br+=6;
|
|
before.metrics.compression_zstd+=0;before.metrics.compression_deflate+=0;
|
|
before.transform_metrics.applied+=6;before.transform_metrics.cache_hits+=3;
|
|
window.rustProxyWideOverview.render(before);
|
|
const get = (id,key) => document.getElementById(`overview-wide-${id}-${key}-line`).getAttribute('d');
|
|
const compressionLow=get('wide_compression_activity','gzip');
|
|
const transformLow=get('wide_transform_activity','applied');
|
|
const cpu=get('wide_cpu','system');
|
|
const lowLabel=document.getElementById('overview-wide-wide_compression_activity-gzip-value').textContent;
|
|
window.__clock=7000;
|
|
before.metrics.compression_gzip+=603; // now 201/s => ceiling expands to 500/s
|
|
before.transform_metrics.applied+=603;
|
|
window.rustProxyWideOverview.render(before);
|
|
const highLabel=document.getElementById('overview-wide-wide_compression_activity-gzip-value').textContent;
|
|
const compressionHigh=get('wide_compression_activity','gzip');
|
|
window.__clock=10000;
|
|
before.metrics.compression_gzip+=3;
|
|
before.transform_metrics.applied+=3;
|
|
window.rustProxyWideOverview.render(before);
|
|
const afterSpike=get('wide_compression_activity','gzip');
|
|
return {first,count,latency,memory,high,held,reset,percent,blank,
|
|
lowLabel,highLabel,compressionLow,transformLow,cpu,compressionHigh,afterSpike};
|
|
}''')
|
|
assert result['first']==100 and result['count']==10 and result['latency']==100, (width,result)
|
|
assert result['memory']>=64*1024*1024, (width,result)
|
|
assert (result['high'],result['held'],result['reset'],result['percent'],result['blank']) == (200,200,100,100,100), (width,result)
|
|
assert result['lowLabel']=='1.0/s' and result['highLabel']=='201.0/s', (width,result)
|
|
def yvalues(line): return [int(n) for n in re.findall(r'[ML]\d+ (\d+)',line)]
|
|
assert yvalues(result['compressionLow'])[-1]>=135, (width,result)
|
|
assert yvalues(result['transformLow'])[-1]>=130, (width,result)
|
|
assert yvalues(result['cpu'])==[136,136], (width,result)
|
|
assert yvalues(result['compressionHigh'])[-1]<100, (width,result)
|
|
assert yvalues(result['afterSpike'])[-1]>=135, (width,result)
|
|
assert not errors, (width,errors)
|
|
print('PASS all 14 overview-wide plot sizes/grids + full Cache/Transform charts + stable scales:', width)
|
|
page.close()
|
|
browser.close()
|