@@ -13,11 +13,11 @@ import (
13
13
"os/exec"
14
14
"path/filepath"
15
15
"strings"
16
+ "sync/atomic"
16
17
17
18
"github.com/rogpeppe/go-internal/goproxytest"
18
19
"github.com/rogpeppe/go-internal/gotooltest"
19
20
"github.com/rogpeppe/go-internal/testscript"
20
- "github.com/rogpeppe/go-internal/txtar"
21
21
)
22
22
23
23
const (
@@ -71,16 +71,6 @@ func mainerr() (retErr error) {
71
71
return err
72
72
}
73
73
74
- td , err := os .MkdirTemp ("" , "testscript" )
75
- if err != nil {
76
- return fmt .Errorf ("unable to create temp dir: %v" , err )
77
- }
78
- if * fWork {
79
- fmt .Fprintf (os .Stderr , "temporary work directory: %v\n " , td )
80
- } else {
81
- defer os .RemoveAll (td )
82
- }
83
-
84
74
files := fs .Args ()
85
75
if len (files ) == 0 {
86
76
files = []string {"-" }
@@ -99,228 +89,95 @@ func mainerr() (retErr error) {
99
89
if onlyReadFromStdin && * fUpdate {
100
90
return fmt .Errorf ("cannot use -u when reading from stdin" )
101
91
}
102
-
103
- tr := testRunner {
104
- update : * fUpdate ,
105
- continueOnError : * fContinue ,
106
- verbose : * fVerbose ,
107
- env : envVars .vals ,
108
- testWork : * fWork ,
109
- }
110
-
111
- dirNames := make (map [string ]int )
112
- for _ , filename := range files {
113
- // TODO make running files concurrent by default? If we do, note we'll need to do
114
- // something smarter with the runner stdout and stderr below
115
-
116
- // Derive a name for the directory from the basename of file, making
117
- // uniq by adding a numeric suffix in the case we otherwise end
118
- // up with multiple files with the same basename
119
- dirName := filepath .Base (filename )
120
- count := dirNames [dirName ]
121
- dirNames [dirName ] = count + 1
122
- if count != 0 {
123
- dirName = fmt .Sprintf ("%s%d" , dirName , count )
92
+ var stdinTempFile string
93
+ for i , f := range files {
94
+ if f != "-" {
95
+ continue
124
96
}
125
-
126
- runDir := filepath .Join (td , dirName )
127
- if err := os .Mkdir (runDir , 0o777 ); err != nil {
128
- return fmt .Errorf ("failed to create a run directory within %v for %v: %v" , td , renderFilename (filename ), err )
97
+ if stdinTempFile != "" {
98
+ return fmt .Errorf ("cannot read stdin twice" )
129
99
}
130
- if err := tr .run (runDir , filename ); err != nil {
131
- return err
100
+ data , err := io .ReadAll (os .Stdin )
101
+ if err != nil {
102
+ return fmt .Errorf ("error reading stdin: %v" , err )
132
103
}
133
- }
134
-
135
- return nil
136
- }
137
-
138
- type testRunner struct {
139
- // update denotes that the source testscript archive filename should be
140
- // updated in the case of any cmp failures.
141
- update bool
142
-
143
- // continueOnError indicates that T.FailNow should not panic, allowing the
144
- // test script to continue running. Note that T is still marked as failed.
145
- continueOnError bool
146
-
147
- // verbose indicates the running of the script should be noisy.
148
- verbose bool
149
-
150
- // env is the environment that should be set on top of the base
151
- // testscript-defined minimal environment.
152
- env []string
153
-
154
- // testWork indicates whether or not temporary working directory trees
155
- // should be left behind. Corresponds exactly to the
156
- // testscript.Params.TestWork field.
157
- testWork bool
158
- }
159
-
160
- // run runs the testscript archive located at the path filename, within the
161
- // working directory runDir. filename could be "-" in the case of stdin
162
- func (tr * testRunner ) run (runDir , filename string ) error {
163
- var ar * txtar.Archive
164
- var err error
165
-
166
- mods := filepath .Join (runDir , goModProxyDir )
167
-
168
- if err := os .MkdirAll (mods , 0o777 ); err != nil {
169
- return fmt .Errorf ("failed to create goModProxy dir: %v" , err )
170
- }
171
-
172
- if filename == "-" {
173
- byts , err := io .ReadAll (os .Stdin )
104
+ f , err := os .CreateTemp ("" , "stdin*.txtar" )
174
105
if err != nil {
175
- return fmt . Errorf ( "failed to read from stdin: %v" , err )
106
+ return err
176
107
}
177
- ar = txtar .Parse (byts )
178
- } else {
179
- ar , err = txtar .ParseFile (filename )
180
- }
181
-
182
- if err != nil {
183
- return fmt .Errorf ("failed to txtar parse %v: %v" , renderFilename (filename ), err )
184
- }
185
-
186
- var script , gomodProxy txtar.Archive
187
- script .Comment = ar .Comment
188
-
189
- for _ , f := range ar .Files {
190
- fp := filepath .Clean (filepath .FromSlash (f .Name ))
191
- parts := strings .Split (fp , string (os .PathSeparator ))
192
-
193
- if len (parts ) > 1 && parts [0 ] == goModProxyDir {
194
- gomodProxy .Files = append (gomodProxy .Files , f )
195
- } else {
196
- script .Files = append (script .Files , f )
108
+ if _ , err := f .Write (data ); err != nil {
109
+ return err
197
110
}
198
- }
199
-
200
- if txtar .Write (& gomodProxy , runDir ); err != nil {
201
- return fmt .Errorf ("failed to write .gomodproxy files: %v" , err )
202
- }
203
-
204
- scriptFile := filepath .Join (runDir , "script.txtar" )
205
-
206
- if err := os .WriteFile (scriptFile , txtar .Format (& script ), 0o666 ); err != nil {
207
- return fmt .Errorf ("failed to write script for %v: %v" , renderFilename (filename ), err )
111
+ if err := f .Close (); err != nil {
112
+ return err
113
+ }
114
+ stdinTempFile = f .Name ()
115
+ files [i ] = stdinTempFile
116
+ defer os .Remove (stdinTempFile )
208
117
}
209
118
210
119
p := testscript.Params {
211
- Dir : runDir ,
212
- UpdateScripts : tr .update ,
213
- ContinueOnError : tr .continueOnError ,
120
+ Setup : func (* testscript.Env ) error { return nil },
121
+ Files : files ,
122
+ UpdateScripts : * fUpdate ,
123
+ ContinueOnError : * fContinue ,
124
+ TestWork : * fWork ,
214
125
}
215
126
216
127
if _ , err := exec .LookPath ("go" ); err == nil {
217
128
if err := gotooltest .Setup (& p ); err != nil {
218
- return fmt .Errorf ("failed to setup go tool for %v run : %v" , renderFilename ( filename ) , err )
129
+ return fmt .Errorf ("failed to setup go tool: %v" , err )
219
130
}
220
131
}
221
-
222
- addSetup := func (f func (env * testscript.Env ) error ) {
223
- origSetup := p .Setup
224
- p .Setup = func (env * testscript.Env ) error {
225
- if origSetup != nil {
226
- if err := origSetup (env ); err != nil {
227
- return err
228
- }
229
- }
230
- return f (env )
132
+ origSetup := p .Setup
133
+ p .Setup = func (env * testscript.Env ) error {
134
+ if err := origSetup (env ); err != nil {
135
+ return err
231
136
}
232
- }
233
-
234
- if tr .testWork {
235
- addSetup (func (env * testscript.Env ) error {
236
- fmt .Fprintf (os .Stderr , "temporary work directory for %s: %s\n " , renderFilename (filename ), env .WorkDir )
237
- return nil
238
- })
239
- }
240
-
241
- if len (gomodProxy .Files ) > 0 {
242
- srv , err := goproxytest .NewServer (mods , "" )
243
- if err != nil {
244
- return fmt .Errorf ("cannot start proxy for %v: %v" , renderFilename (filename ), err )
137
+ if * fWork {
138
+ env .T ().Log ("temporary work directory: " , env .WorkDir )
245
139
}
246
- defer srv .Close ()
140
+ proxyDir := filepath .Join (env .WorkDir , goModProxyDir )
141
+ if info , err := os .Stat (proxyDir ); err == nil && info .IsDir () {
142
+ srv , err := goproxytest .NewServer (proxyDir , "" )
143
+ if err != nil {
144
+ return fmt .Errorf ("cannot start Go proxy: %v" , err )
145
+ }
146
+ env .Defer (srv .Close )
247
147
248
- addSetup (func (env * testscript.Env ) error {
249
148
// Add GOPROXY after calling the original setup
250
149
// so that it overrides any GOPROXY set there.
251
150
env .Vars = append (env .Vars ,
252
151
"GOPROXY=" + srv .URL ,
253
152
"GONOSUMDB=*" ,
254
153
)
255
- return nil
256
- })
257
- }
258
-
259
- if len (tr .env ) > 0 {
260
- addSetup (func (env * testscript.Env ) error {
261
- for _ , v := range tr .env {
262
- varName := v
263
- if i := strings .Index (v , "=" ); i >= 0 {
264
- varName = v [:i ]
265
- } else {
266
- v = fmt .Sprintf ("%s=%s" , v , os .Getenv (v ))
267
- }
268
- switch varName {
269
- case "" :
270
- return fmt .Errorf ("invalid variable name %q" , varName )
271
- case "WORK" :
272
- return fmt .Errorf ("cannot override WORK variable" )
273
- }
274
- env .Vars = append (env .Vars , v )
154
+ }
155
+ for _ , v := range envVars .vals {
156
+ varName , _ , ok := strings .Cut (v , "=" )
157
+ if ! ok {
158
+ v += "=" + os .Getenv (v )
275
159
}
276
- return nil
277
- })
160
+ switch varName {
161
+ case "" :
162
+ return fmt .Errorf ("invalid variable name %q" , varName )
163
+ case "WORK" :
164
+ return fmt .Errorf ("cannot override WORK variable" )
165
+ }
166
+ env .Vars = append (env .Vars , v )
167
+ }
168
+ return nil
278
169
}
279
170
280
171
r := & runT {
281
- verbose : tr .verbose ,
172
+ verbose : * fVerbose ,
173
+ stdinTempFile : stdinTempFile ,
282
174
}
283
-
284
- func () {
285
- defer func () {
286
- switch recover () {
287
- case nil , skipRun :
288
- case failedRun :
289
- err = failedRun
290
- default :
291
- panic (fmt .Errorf ("unexpected panic: %v [%T]" , err , err ))
292
- }
293
- }()
294
- testscript .RunT (r , p )
295
- }()
296
-
297
- if err != nil {
298
- return fmt .Errorf ("error running %v in %v\n " , renderFilename (filename ), runDir )
299
- }
300
-
301
- if tr .update && filename != "-" {
302
- // Parse the (potentially) updated scriptFile as an archive, then merge
303
- // with the original archive, retaining order. Then write the archive
304
- // back to the source file
305
- source , err := os .ReadFile (scriptFile )
306
- if err != nil {
307
- return fmt .Errorf ("failed to read from script file %v for -update: %v" , scriptFile , err )
308
- }
309
- updatedAr := txtar .Parse (source )
310
- updatedFiles := make (map [string ]txtar.File )
311
- for _ , f := range updatedAr .Files {
312
- updatedFiles [f .Name ] = f
313
- }
314
- for i , f := range ar .Files {
315
- if newF , ok := updatedFiles [f .Name ]; ok {
316
- ar .Files [i ] = newF
317
- }
318
- }
319
- if err := os .WriteFile (filename , txtar .Format (ar ), 0o666 ); err != nil {
320
- return fmt .Errorf ("failed to write script back to %v for -update: %v" , renderFilename (filename ), err )
321
- }
175
+ r .Run ("" , func (t testscript.T ) {
176
+ testscript .RunT (t , p )
177
+ })
178
+ if r .failed .Load () {
179
+ return failedRun
322
180
}
323
-
324
181
return nil
325
182
}
326
183
@@ -329,18 +186,11 @@ var (
329
186
skipRun = errors .New ("skip" )
330
187
)
331
188
332
- // renderFilename renders filename in error messages, taking into account
333
- // the filename could be the special "-" (stdin)
334
- func renderFilename (filename string ) string {
335
- if filename == "-" {
336
- return "<stdin>"
337
- }
338
- return filename
339
- }
340
-
341
189
// runT implements testscript.T and is used in the call to testscript.Run
342
190
type runT struct {
343
- verbose bool
191
+ verbose bool
192
+ stdinTempFile string
193
+ failed atomic.Bool
344
194
}
345
195
346
196
func (r * runT ) Skip (is ... interface {}) {
@@ -353,22 +203,36 @@ func (r *runT) Fatal(is ...interface{}) {
353
203
}
354
204
355
205
func (r * runT ) Parallel () {
356
- // No-op for now; we are currently only running a single script in a
357
- // testscript instance.
206
+ // TODO run tests in parallel.
358
207
}
359
208
360
209
func (r * runT ) Log (is ... interface {}) {
361
- fmt .Print (is ... )
210
+ msg := fmt .Sprint (is ... )
211
+ if r .stdinTempFile != "" {
212
+ msg = strings .ReplaceAll (msg , r .stdinTempFile , "<stdin>" )
213
+ }
214
+ if ! strings .HasSuffix (msg , "\n " ) {
215
+ msg += "\n "
216
+ }
217
+ fmt .Print (msg )
362
218
}
363
219
364
220
func (r * runT ) FailNow () {
365
221
panic (failedRun )
366
222
}
367
223
368
- func (r * runT ) Run (n string , f func (t testscript.T )) {
369
- // For now we we don't top/tail the run of a subtest. We are currently only
370
- // running a single script in a testscript instance, which means that we
371
- // will only have a single subtest.
224
+ func (r * runT ) Run (name string , f func (t testscript.T )) {
225
+ // TODO: perhaps log the test name when there's more
226
+ // than one test file?
227
+ defer func () {
228
+ switch err := recover (); err {
229
+ case nil , skipRun :
230
+ case failedRun :
231
+ r .failed .Store (true )
232
+ default :
233
+ panic (fmt .Errorf ("unexpected panic: %v [%T]" , err , err ))
234
+ }
235
+ }()
372
236
f (r )
373
237
}
374
238
0 commit comments