@@ -4,6 +4,14 @@ import { renderMetricView } from "./metric-ui.mjs";
44import { defaultParams , params } from "./shared/params.mjs" ;
55import { createDeveloperModeContainer } from "./developer-mode.mjs" ;
66
7+ const BENCHMARK_STATE = Object . freeze ( {
8+ IDLE : "IDLE" ,
9+ READY : "READY" ,
10+ RUNNING : "RUNNING" ,
11+ DONE : "DONE" ,
12+ ERROR : "ERROR" ,
13+ } ) ;
14+
715// FIXME(camillobruni): Add base class
816class MainBenchmarkClient {
917 developerMode = false ;
@@ -12,8 +20,7 @@ class MainBenchmarkClient {
1220 _measuredValuesList = [ ] ;
1321 _finishedTestCount = 0 ;
1422 _progressCompleted = null ;
15- _isRunning = false ;
16- _hasResults = false ;
23+ _state = BENCHMARK_STATE . IDLE ;
1724 _developerModeContainer = null ;
1825 _metrics = Object . create ( null ) ;
1926 _steppingPromise = null ;
@@ -31,21 +38,29 @@ class MainBenchmarkClient {
3138 } ) ;
3239 }
3340
34- start ( ) {
41+ isRunning ( ) {
42+ return this . _state === BENCHMARK_STATE . RUNNING ;
43+ }
44+
45+ hasFinished ( ) {
46+ return this . _state === BENCHMARK_STATE . DONE || this . _state === BENCHMARK_STATE . ERROR ;
47+ }
48+
49+ async start ( ) {
3550 if ( this . _isStepping ( ) )
3651 this . _clearStepping ( ) ;
37- else if ( this . _startBenchmark ( ) )
52+ else if ( await this . _startBenchmark ( ) )
3853 this . _showSection ( "#running" ) ;
3954 }
4055
41- step ( ) {
56+ async step ( ) {
4257 const currentSteppingResolver = this . _steppingResolver ;
4358 this . _steppingPromise = new Promise ( ( resolve ) => {
4459 this . _steppingResolver = resolve ;
4560 } ) ;
4661 currentSteppingResolver ?. ( ) ;
47- if ( ! this . _isRunning ) {
48- this . _startBenchmark ( ) ;
62+ if ( ! this . isRunning ( ) ) {
63+ await this . _startBenchmark ( ) ;
4964 this . _showSection ( "#running" ) ;
5065 }
5166 }
@@ -67,7 +82,7 @@ class MainBenchmarkClient {
6782 }
6883
6984 async _startBenchmark ( ) {
70- if ( this . _isRunning )
85+ if ( this . isRunning ( ) )
7186 return false ;
7287
7388 const { benchmarkConfigurator } = await this . _benchmarkConfiguratorPromise ;
@@ -99,11 +114,11 @@ class MainBenchmarkClient {
99114 }
100115
101116 this . _metrics = Object . create ( null ) ;
102- this . _isRunning = true ;
103117
104118 this . stepCount = params . iterationCount * totalSuitesCount ;
105119 this . _progressCompleted . max = this . stepCount ;
106120 this . suitesCount = enabledSuites . length ;
121+ this . _setBenchmarkState ( BENCHMARK_STATE . RUNNING ) ;
107122 const runner = new BenchmarkRunner ( benchmarkConfigurator . suites , this ) ;
108123 runner . runMultipleIterations ( params . iterationCount ) ;
109124 return true ;
@@ -113,13 +128,20 @@ class MainBenchmarkClient {
113128 return this . _metrics ;
114129 }
115130
131+ _assertNotAborted ( ) {
132+ if ( this . _state === BENCHMARK_STATE . ERROR )
133+ throw new Error ( "Benchmark aborted by another process." ) ;
134+ }
135+
116136 willAddTestFrame ( frame ) {
137+ this . _assertNotAborted ( ) ;
117138 frame . style . left = "50%" ;
118139 frame . style . top = "50%" ;
119140 frame . style . transform = "translate(-50%, -50%)" ;
120141 }
121142
122143 async willRunTest ( suite , test ) {
144+ this . _assertNotAborted ( ) ;
123145 document . getElementById ( "info-label" ) . textContent = suite . name ;
124146 document . getElementById ( "info-progress" ) . textContent = `${ this . _finishedTestCount } / ${ this . stepCount } ` ;
125147 if ( this . _steppingPromise )
@@ -143,10 +165,10 @@ class MainBenchmarkClient {
143165 }
144166
145167 didFinishLastIteration ( metrics ) {
146- console . assert ( this . _isRunning ) ;
147- this . _isRunning = false ;
148- this . _hasResults = true ;
168+ console . assert ( this . isRunning ( ) ) ;
169+
149170 this . _metrics = metrics ;
171+ this . _setBenchmarkState ( BENCHMARK_STATE . DONE ) ;
150172
151173 const scoreResults = this . _computeResults ( this . _measuredValuesList , "score" ) ;
152174 if ( scoreResults . isValid )
@@ -163,11 +185,11 @@ class MainBenchmarkClient {
163185 }
164186
165187 handleError ( error ) {
166- console . assert ( this . _isRunning ) ;
167- this . _isRunning = false ;
168- this . _hasResults = true ;
188+ if ( this . _state === BENCHMARK_STATE . ERROR )
189+ return ;
169190 this . _metrics = Object . create ( null ) ;
170- this . _populateInvalidScore ( ) ;
191+ this . _setBenchmarkState ( BENCHMARK_STATE . ERROR ) ;
192+ this . _populateErrorMessage ( error . message ) ;
171193 this . showResultsSummary ( ) ;
172194 throw error ;
173195 }
@@ -182,9 +204,17 @@ class MainBenchmarkClient {
182204 }
183205
184206 _populateInvalidScore ( ) {
207+ this . _populateErrorMessage ( undefined ) ;
208+ }
209+
210+ _populateErrorMessage ( errorText ) {
185211 document . getElementById ( "summary" ) . className = "invalid" ;
186212 document . getElementById ( "result-number" ) . textContent = "Error" ;
187213 document . getElementById ( "confidence-number" ) . textContent = "" ;
214+ if ( errorText === undefined )
215+ return ;
216+ const errorMessage = document . getElementById ( "invalid-score-text" ) ;
217+ errorMessage . textContent = errorText ;
188218 }
189219
190220 _computeResults ( measuredValuesList , displayUnit ) {
@@ -344,6 +374,7 @@ class MainBenchmarkClient {
344374 document . getElementById ( "copy-csv" ) . onclick = this . copyCSVResults . bind ( this ) ;
345375 document . querySelectorAll ( ".start-tests-button" ) . forEach ( ( button ) => {
346376 button . onclick = this . _startBenchmarkHandler . bind ( this ) ;
377+ button . disabled = true ;
347378 } ) ;
348379 }
349380
@@ -360,6 +391,24 @@ class MainBenchmarkClient {
360391
361392 if ( params . startAutomatically )
362393 this . start ( ) ;
394+ else
395+ this . _setBenchmarkState ( BENCHMARK_STATE . READY ) ;
396+ }
397+
398+ async _setBenchmarkState ( state ) {
399+ this . _state = state ;
400+ document . body . setAttribute ( "data-benchmark-state" , state ) ;
401+ const startButtons = document . querySelectorAll ( ".start-tests-button" ) ;
402+ if ( state !== BENCHMARK_STATE . RUNNING ) {
403+ startButtons . forEach ( ( btn ) => {
404+ btn . innerHTML = "<div>Start Test</div>" ;
405+ } ) ;
406+ if ( state === BENCHMARK_STATE . READY ) {
407+ startButtons . forEach ( ( btn ) => {
408+ btn . disabled = false ;
409+ } ) ;
410+ }
411+ }
363412 }
364413
365414 _hashChangeHandler ( ) {
@@ -382,7 +431,7 @@ class MainBenchmarkClient {
382431
383432 _logoClickHandler ( event ) {
384433 // Prevent any accidental UI changes during benchmark runs.
385- if ( ! this . _isRunning )
434+ if ( ! this . isRunning ( ) )
386435 this . _showSection ( "#home" ) ;
387436 event . preventDefault ( ) ;
388437 return false ;
@@ -431,10 +480,10 @@ class MainBenchmarkClient {
431480 }
432481
433482 _showSection ( hash ) {
434- if ( this . _isRunning ) {
483+ if ( this . isRunning ( ) ) {
435484 this . _setLocationHash ( "#running" ) ;
436485 return ;
437- } else if ( this . _hasResults ) {
486+ } else if ( this . hasFinished ( ) ) {
438487 if ( hash !== "#summary" && hash !== "#details" ) {
439488 this . _setLocationHash ( "#summary" ) ;
440489 return ;
@@ -483,6 +532,13 @@ function init() {
483532 rootStyle . setProperty ( "--viewport-height" , `${ params . viewport . height } px` ) ;
484533
485534 globalThis . benchmarkClient = new MainBenchmarkClient ( ) ;
535+ window . addEventListener ( "error" , ( event ) => {
536+ globalThis . benchmarkClient . handleError ( event . error || new Error ( event . message ) ) ;
537+ } ) ;
538+
539+ window . addEventListener ( "unhandledrejection" , ( event ) => {
540+ globalThis . benchmarkClient . handleError ( event . reason || new Error ( "Unhandled promise rejection" ) ) ;
541+ } ) ;
486542}
487543
488544if ( document . readyState === "loading" )
0 commit comments