66 * found in the LICENSE file at https://angular.io/license
77 */
88
9- import { existsSync } from 'node:fs' ;
109import fs from 'node:fs/promises' ;
1110import path from 'node:path' ;
1211import os from 'node:os' ;
@@ -31,14 +30,7 @@ import {
3130} from './process_utils.mjs' ;
3231import { ENVIRONMENT_TMP_PLACEHOLDER } from './constants.mjs' ;
3332import { debug } from './debug.mjs' ;
34- import chalk from 'chalk' ;
35-
36- // Convience access to chalk colors.
37- const { red, green} = chalk ;
38- /** The size discrepancy we allow in bytes. */
39- const THRESHOLD_BYTES = 5000 ;
40- /** The size discrepancy as a percentage. */
41- const THRESHOLD_PERCENT = 5 ;
33+ import { SizeTracker } from './size-tracking.mjs' ;
4234
4335/** Error class that is used when an integration command fails. */
4436class IntegrationTestCommandError extends Error { }
@@ -56,6 +48,7 @@ type EnvironmentConfig = Record<string, BazelExpandedValue>;
5648 */
5749export class TestRunner {
5850 private readonly environment : EnvironmentConfig ;
51+ private readonly sizeTracker : SizeTracker ;
5952
6053 constructor (
6154 private readonly isTestDebugMode : boolean ,
@@ -68,6 +61,7 @@ export class TestRunner {
6861 environment : EnvironmentConfig ,
6962 ) {
7063 this . environment = this . _assignDefaultEnvironmentVariables ( environment ) ;
64+ this . sizeTracker = new SizeTracker ( this . testPackage ) ;
7165 }
7266
7367 async run ( ) {
@@ -91,6 +85,7 @@ export class TestRunner {
9185
9286 try {
9387 await this . _runTestCommands ( testWorkingDir , testEnv ) ;
88+ await this . sizeTracker . run ( testWorkingDir , testEnv ) ;
9489 } finally {
9590 debug ( 'Finished running integration test commands.' ) ;
9691
@@ -279,11 +274,6 @@ export class TestRunner {
279274 console . info (
280275 `Successfully ran all commands in test directory: ${ path . normalize ( testWorkingDir ) } ` ,
281276 ) ;
282-
283- // If the integration test provides a size.json file we use it as a size tracking marker.
284- if ( existsSync ( path . join ( testWorkingDir , 'size.json' ) ) ) {
285- await this . _runSizeTracking ( testWorkingDir , commandEnv ) ;
286- }
287277 }
288278
289279 /**
@@ -313,98 +303,4 @@ export class TestRunner {
313303
314304 return { ...defaults , ...baseEnv } ;
315305 }
316-
317- /**
318- * Runs the size tracking scripting.
319- *
320- * Builds the integration test application and then checks if the size of the generated files varies too
321- * far from our known file sizes.
322- */
323- private async _runSizeTracking (
324- testWorkingDir : string ,
325- commandEnv : NodeJS . ProcessEnv ,
326- ) : Promise < void > {
327- const success = await runCommandInChildProcess ( 'yarn' , [ 'build' ] , testWorkingDir , commandEnv ) ;
328- if ( ! success ) {
329- throw Error ( 'Failed to build for size tracking.' ) ;
330- }
331-
332- const sizes : { [ key : string ] : SizeCheckResult } = { } ;
333-
334- const expectedSizes = JSON . parse (
335- await fs . readFile ( path . join ( testWorkingDir , 'size.json' ) , 'utf-8' ) ,
336- ) as { [ key : string ] : number } ;
337-
338- for ( let [ filename , expectedSize ] of Object . entries ( expectedSizes ) ) {
339- const generedFilePath = path . join ( testWorkingDir , 'dist' , filename ) ;
340- if ( ! existsSync ( generedFilePath ) ) {
341- sizes [ filename ] = {
342- actual : undefined ,
343- failing : false ,
344- expected : expectedSize ,
345- details : 'missing' ,
346- } ;
347- } else {
348- const { size : actualSize } = await fs . stat ( path . join ( testWorkingDir , 'dist' , filename ) ) ;
349- const absoluteSizeDiff = Math . abs ( actualSize - expectedSize ) ;
350- const percentSizeDiff = ( absoluteSizeDiff / expectedSize ) * 100 ;
351- const direction = actualSize === expectedSize ? '' : actualSize > expectedSize ? '+' : '-' ;
352- sizes [ filename ] = {
353- actual : actualSize ,
354- expected : expectedSize ,
355- failing : absoluteSizeDiff > THRESHOLD_BYTES || percentSizeDiff > THRESHOLD_PERCENT ,
356- details : {
357- raw : `${ direction } ${ absoluteSizeDiff . toFixed ( 0 ) } ` ,
358- percent : `${ direction } ${ percentSizeDiff . toFixed ( 3 ) } ` ,
359- } ,
360- } ;
361- }
362- }
363-
364- console . info ( Array ( 80 ) . fill ( '=' ) . join ( '' ) ) ;
365- console . info (
366- `${ Array ( 28 ) . fill ( '=' ) . join ( '' ) } SIZE TRACKING RESULTS ${ Array ( 29 ) . fill ( '=' ) . join ( '' ) } ` ,
367- ) ;
368- console . info ( Array ( 80 ) . fill ( '=' ) . join ( '' ) ) ;
369- let failed = false ;
370- for ( let [ filename , { actual, expected, failing, details} ] of Object . entries ( sizes ) ) {
371- failed = failed || failing ;
372- const bullet = failing ? green ( '✔' ) : red ( '✘' ) ;
373- console . info ( ` ${ bullet } ${ filename } ` ) ;
374- if ( details === 'missing' ) {
375- console . info (
376- ` File not found in generated integration test application, either ensure the file is created or remove it from the size tracking json file.` ,
377- ) ;
378- } else {
379- console . info (
380- ` Actual Size: ${ actual } | Expected Size: ${ expected } | ${ details . raw } bytes (${ details . raw } %)` ,
381- ) ;
382- }
383- }
384- console . info ( ) ;
385- if ( failed ) {
386- const sizeFileLocation = path . join ( this . testPackage , 'size.json' ) ;
387- console . info (
388- `If this is a desired change, please update the size limits in: ${ sizeFileLocation } ` ,
389- ) ;
390- process . exitCode = 1 ;
391- } else {
392- console . info (
393- `Payload size check passed. All diffs are less than ${ THRESHOLD_PERCENT } % or ${ THRESHOLD_BYTES } bytes.` ,
394- ) ;
395- }
396- console . info ( Array ( 80 ) . fill ( '=' ) . join ( '' ) ) ;
397- }
398- }
399-
400- interface SizeCheckResult {
401- expected : number ;
402- actual : number | undefined ;
403- failing : boolean ;
404- details :
405- | 'missing'
406- | {
407- raw : string ;
408- percent : string ;
409- } ;
410306}
0 commit comments