@@ -134,18 +134,33 @@ function journeyInputs([ceilingsPath, journey, evidencePath]) {
134134 return { all, ceilingsPath, journey, evidence, counts : evidence . workCounts ?? { } } ;
135135}
136136
137- function runCheck ( args ) {
138- const { all, journey, evidence, counts } = journeyInputs ( args ) ;
139- // The base branch's copy of this file; absent while the journey is new there.
140- const basePath = args [ 3 ] ;
141- const base = basePath && existsSync ( basePath ) ? readJson ( basePath ) [ journey ] ?. counts : undefined ;
142- const { passed, rows } = checkCeilings ( all [ journey ] . counts , counts , base ) ;
143- console . log ( `[perf-ratchet] ${ journey } : ${ passed ? "PASS" : "FAIL" } ` ) ;
144- for ( const row of rows ) console . log ( `[perf-ratchet] ${ formatRow ( row ) } ` ) ;
137+ const isObject = ( value ) => typeof value === "object" && value !== null ;
138+
139+ /**
140+ * The base branch's ceilings for `journey`, and the base journeys this file no longer has. A
141+ * base file in a shape this script does not write throws: an unreadable base must not pass.
142+ */
143+ export function readBase ( base , all , journey ) {
144+ for ( const [ name , entry ] of Object . entries ( base ) ) {
145+ if ( ! isObject ( entry ?. counts ) ) throw new Error ( `base ceilings: "${ name } " has no counts object` ) ;
146+ }
147+ return {
148+ counts : base [ journey ] ?. counts ,
149+ removedJourneys : Object . keys ( base ) . filter ( ( name ) => ! Object . hasOwn ( all , name ) ) ,
150+ } ;
151+ }
152+
153+ /** Absent while the base branch has no ceilings file yet (and on runs without a base). */
154+ function loadBase ( path , all , journey ) {
155+ if ( ! path || ! existsSync ( path ) ) return { counts : undefined , removedJourneys : [ ] } ;
156+ return readBase ( readJson ( path ) , all , journey ) ;
157+ }
158+
159+ function printNotes ( recorded , evidence , rows ) {
145160 const measuredOn = browserMajor ( evidence ) ;
146- if ( measuredOn !== all [ journey ] . browser ) {
161+ if ( measuredOn !== recorded . browser ) {
147162 console . log (
148- `[perf-ratchet] note: measured on Chrome ${ measuredOn } , ceilings recorded on Chrome ${ all [ journey ] . browser } ` ,
163+ `[perf-ratchet] note: measured on Chrome ${ measuredOn } , ceilings recorded on Chrome ${ recorded . browser } ` ,
149164 ) ;
150165 }
151166 if ( rows . some ( ( row ) => row . status === "rose" ) ) {
@@ -154,7 +169,22 @@ function runCheck(args) {
154169 "exactly: run the journey a few times and compare with perf-ratchet.mjs correlate." ,
155170 ) ;
156171 }
157- return passed ? 0 : 1 ;
172+ }
173+
174+ function runCheck ( args ) {
175+ const { all, journey, evidence, counts } = journeyInputs ( args ) ;
176+ const base = loadBase ( args [ 3 ] , all , journey ) ;
177+ const { passed, rows } = checkCeilings ( all [ journey ] . counts , counts , base . counts ) ;
178+ const ok = passed && base . removedJourneys . length === 0 ;
179+ console . log ( `[perf-ratchet] ${ journey } : ${ ok ? "PASS" : "FAIL" } ` ) ;
180+ for ( const row of rows ) console . log ( `[perf-ratchet] ${ formatRow ( row ) } ` ) ;
181+ for ( const name of base . removedJourneys ) {
182+ console . log (
183+ `[perf-ratchet] FAIL journey ${ name } removed against the base branch; ceilings only go down` ,
184+ ) ;
185+ }
186+ printNotes ( all [ journey ] , evidence , rows ) ;
187+ return ok ? 0 : 1 ;
158188}
159189
160190function runLower ( args ) {
0 commit comments