6
6
import * as assert from 'assert'
7
7
import { SpawnOptions } from 'child_process'
8
8
9
+ import { access } from '../../../../shared/filesystem'
10
+ import { readFileAsString } from '../../../../shared/filesystemUtilities'
9
11
import { TestLogger } from '../../../../shared/loggerUtils'
10
12
import {
11
13
makeRequiredSamCliProcessInvokeOptions ,
@@ -15,10 +17,7 @@ import {
15
17
import { ChildProcessResult } from '../../../../shared/utilities/childProcess'
16
18
17
19
export class TestSamCliProcessInvoker implements SamCliProcessInvoker {
18
- public constructor (
19
- private readonly onInvoke : ( spawnOptions : SpawnOptions , ...args : any [ ] ) => ChildProcessResult
20
- ) {
21
- }
20
+ public constructor ( private readonly onInvoke : ( spawnOptions : SpawnOptions , ...args : any [ ] ) => ChildProcessResult ) { }
22
21
23
22
public async invoke ( options ?: SamCliProcessInvokeOptions ) : Promise < ChildProcessResult > {
24
23
const invokeOptions = makeRequiredSamCliProcessInvokeOptions ( options )
@@ -28,7 +27,6 @@ export class TestSamCliProcessInvoker implements SamCliProcessInvoker {
28
27
}
29
28
30
29
export class BadExitCodeSamCliProcessInvoker extends TestSamCliProcessInvoker {
31
-
32
30
public exitCode : number
33
31
public error : Error
34
32
public stdout : string
@@ -38,7 +36,7 @@ export class BadExitCodeSamCliProcessInvoker extends TestSamCliProcessInvoker {
38
36
exitCode = - 1 ,
39
37
error = new Error ( 'Bad Result' ) ,
40
38
stdout = 'stdout message' ,
41
- stderr = 'stderr message' ,
39
+ stderr = 'stderr message'
42
40
} : {
43
41
exitCode ?: number
44
42
error ?: Error
@@ -60,7 +58,7 @@ export class BadExitCodeSamCliProcessInvoker extends TestSamCliProcessInvoker {
60
58
exitCode : this . exitCode ,
61
59
error : this . error ,
62
60
stdout : this . stdout ,
63
- stderr : this . stderr ,
61
+ stderr : this . stderr
64
62
}
65
63
66
64
return result
@@ -73,51 +71,94 @@ export class FakeChildProcessResult implements ChildProcessResult {
73
71
public stdout : string
74
72
public stderr : string
75
73
76
- public constructor (
77
- {
78
- exitCode = 0 ,
79
- stdout = '' ,
80
- stderr = '' ,
81
- ...params
82
- } : Partial < ChildProcessResult >
83
- ) {
74
+ public constructor ( { exitCode = 0 , stdout = '' , stderr = '' , ...params } : Partial < ChildProcessResult > ) {
84
75
this . exitCode = exitCode
85
76
this . error = params . error
86
77
this . stdout = stdout
87
78
this . stderr = stderr
88
79
}
89
80
}
90
81
91
- export function assertErrorContainsBadExitMessage (
92
- actualError : Error ,
93
- sourceErrorMessage : string
94
- ) {
82
+ export function assertErrorContainsBadExitMessage ( actualError : Error , sourceErrorMessage : string ) {
95
83
assert . strictEqual (
96
- actualError . message , `Error with child process: ${ sourceErrorMessage } ` ,
84
+ actualError . message ,
85
+ `Error with child process: ${ sourceErrorMessage } ` ,
97
86
'Unexpected error message'
98
87
)
99
88
}
100
89
101
90
export async function assertLogContainsBadExitInformation (
102
91
logger : TestLogger ,
103
92
errantChildProcessResult : ChildProcessResult ,
104
- expectedExitCode : number ,
93
+ expectedExitCode : number
105
94
) : Promise < void > {
106
- assert . ok (
107
- // tslint:disable-next-line:max-line-length
108
- await logger . logContainsText ( `Unexpected exitcode (${ errantChildProcessResult . exitCode } ), expecting (${ expectedExitCode } )` ) ,
109
- 'Log message missing for exit code'
110
- )
111
- assert . ok (
112
- await logger . logContainsText ( `Error: ${ errantChildProcessResult . error } ` ) ,
113
- 'Log message missing for error'
114
- )
115
- assert . ok (
116
- await logger . logContainsText ( `stderr: ${ errantChildProcessResult . stderr } ` ) ,
117
- 'Log message missing for stderr'
118
- )
119
- assert . ok (
120
- await logger . logContainsText ( `stdout: ${ errantChildProcessResult . stdout } ` ) ,
121
- 'Log message missing for stdout'
122
- )
95
+ const logPath = logger . logPath
96
+ const expectedTexts = [
97
+ {
98
+ text : `Unexpected exitcode (${ errantChildProcessResult . exitCode } ), expecting (${ expectedExitCode } )` ,
99
+ verifyMessage : 'Log message missing for exit code'
100
+ } ,
101
+ { text : `Error: ${ errantChildProcessResult . error } ` , verifyMessage : 'Log message missing for error' } ,
102
+ { text : `stderr: ${ errantChildProcessResult . stderr } ` , verifyMessage : 'Log message missing for stderr' } ,
103
+ { text : `stdout: ${ errantChildProcessResult . stdout } ` , verifyMessage : 'Log message missing for stdout' }
104
+ ]
105
+
106
+ // Give the log a chance to get created/flushed
107
+ await retryOnError ( {
108
+ description : 'Wait for log file to exist' ,
109
+ fn : async ( ) => await access ( logPath ) ,
110
+ retries : 20 ,
111
+ delayMilliseconds : 50 ,
112
+ failureMessage : `Could not find log file: ${ logPath } `
113
+ } )
114
+
115
+ await retryOnError ( {
116
+ description : 'Wait for expected text to appear in log' ,
117
+ fn : async ( ) => {
118
+ const logText = await readFileAsString ( logPath )
119
+ const verifyMessage = verifyLogText ( logText , expectedTexts )
120
+ if ( verifyMessage ) {
121
+ console . log ( verifyMessage )
122
+ throw new Error ( verifyMessage )
123
+ }
124
+ } ,
125
+ retries : 20 ,
126
+ delayMilliseconds : 50 ,
127
+ failureMessage : 'Did not find expected log contents'
128
+ } )
129
+ }
130
+
131
+ function verifyLogText ( text : string , expectedTexts : { text : string ; verifyMessage : string } [ ] ) : string | undefined {
132
+ for ( const entry of expectedTexts ) {
133
+ if ( ! text . includes ( entry . text ) ) {
134
+ return entry . verifyMessage
135
+ }
136
+ }
137
+
138
+ return undefined
139
+ }
140
+
141
+ async function retryOnError ( parameters : {
142
+ description : string
143
+ retries : number
144
+ delayMilliseconds : number
145
+ failureMessage : string
146
+ fn ( ) : Promise < void >
147
+ } ) : Promise < void > {
148
+ for ( let attempt = 0 ; attempt < parameters . retries ; attempt ++ ) {
149
+ try {
150
+ console . log ( `${ parameters . description } : Attempt ${ attempt + 1 } ` )
151
+ await parameters . fn ( )
152
+
153
+ return
154
+ } catch ( err ) {
155
+ if ( attempt + 1 >= parameters . retries ) {
156
+ assert . fail ( parameters . failureMessage )
157
+ }
158
+ }
159
+
160
+ await new Promise < any > ( resolve => setTimeout ( resolve , parameters . delayMilliseconds ) )
161
+ }
162
+
163
+ assert . fail ( parameters . failureMessage )
123
164
}
0 commit comments