@@ -13,11 +13,11 @@ import (
1313 "os/exec"
1414 "path/filepath"
1515 "strings"
16+ "sync/atomic"
1617
1718 "github.com/rogpeppe/go-internal/goproxytest"
1819 "github.com/rogpeppe/go-internal/gotooltest"
1920 "github.com/rogpeppe/go-internal/testscript"
20- "github.com/rogpeppe/go-internal/txtar"
2121)
2222
2323const (
@@ -71,16 +71,6 @@ func mainerr() (retErr error) {
7171 return err
7272 }
7373
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-
8474 files := fs .Args ()
8575 if len (files ) == 0 {
8676 files = []string {"-" }
@@ -99,228 +89,95 @@ func mainerr() (retErr error) {
9989 if onlyReadFromStdin && * fUpdate {
10090 return fmt .Errorf ("cannot use -u when reading from stdin" )
10191 }
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
12496 }
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" )
12999 }
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 )
132103 }
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" )
174105 if err != nil {
175- return fmt . Errorf ( "failed to read from stdin: %v" , err )
106+ return err
176107 }
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
197110 }
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 )
208117 }
209118
210119 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 ,
214125 }
215126
216127 if _ , err := exec .LookPath ("go" ); err == nil {
217128 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 )
219130 }
220131 }
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
231136 }
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 )
245139 }
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 )
247147
248- addSetup (func (env * testscript.Env ) error {
249148 // Add GOPROXY after calling the original setup
250149 // so that it overrides any GOPROXY set there.
251150 env .Vars = append (env .Vars ,
252151 "GOPROXY=" + srv .URL ,
253152 "GONOSUMDB=*" ,
254153 )
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 )
275159 }
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
278169 }
279170
280171 r := & runT {
281- verbose : tr .verbose ,
172+ verbose : * fVerbose ,
173+ stdinTempFile : stdinTempFile ,
282174 }
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
322180 }
323-
324181 return nil
325182}
326183
@@ -329,18 +186,11 @@ var (
329186 skipRun = errors .New ("skip" )
330187)
331188
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-
341189// runT implements testscript.T and is used in the call to testscript.Run
342190type runT struct {
343- verbose bool
191+ verbose bool
192+ stdinTempFile string
193+ failed atomic.Bool
344194}
345195
346196func (r * runT ) Skip (is ... interface {}) {
@@ -353,22 +203,36 @@ func (r *runT) Fatal(is ...interface{}) {
353203}
354204
355205func (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.
358207}
359208
360209func (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 )
362218}
363219
364220func (r * runT ) FailNow () {
365221 panic (failedRun )
366222}
367223
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+ }()
372236 f (r )
373237}
374238
0 commit comments