@@ -28,7 +28,9 @@ import (
28
28
"time"
29
29
30
30
"github.com/google/go-cmp/cmp"
31
+ "golang.org/x/sync/errgroup"
31
32
"golang.org/x/tools/go/packages"
33
+ "golang.org/x/tools/internal/gocommand"
32
34
"golang.org/x/tools/internal/packagesinternal"
33
35
"golang.org/x/tools/internal/packagestest"
34
36
"golang.org/x/tools/internal/testenv"
@@ -3400,3 +3402,57 @@ func writeTree(t *testing.T, archive string) string {
3400
3402
}
3401
3403
return root
3402
3404
}
3405
+
3406
+ // This is not a test of go/packages at all: it's a test of whether it
3407
+ // is possible to delete the directory used by go list once it has
3408
+ // finished. It is intended to evaluate the hypothesis (to explain
3409
+ // issue #71544) that the go command, on Windows, occasionally fails
3410
+ // to release all its handles to the temporary directory even when it
3411
+ // should have finished. If this test ever fails, the go command has a bug.
3412
+ func TestRmdirAfterGoList (t * testing.T ) {
3413
+ testenv .NeedsExec (t )
3414
+
3415
+ dir := t .TempDir ()
3416
+ if err := os .Mkdir (filepath .Join (dir , "p" ), 0777 ); err != nil {
3417
+ t .Fatalf ("mkdir p: %v" , err )
3418
+ }
3419
+
3420
+ // Create a go.mod file and 100 trivial Go files for the go command to read.
3421
+ if err := os .WriteFile (filepath .Join (dir , "go.mod" ), []byte ("module example.com" ), 0666 ); err != nil {
3422
+ t .Fatal (err )
3423
+ }
3424
+ for i := range 100 {
3425
+ filename := filepath .Join (dir , fmt .Sprintf ("p/%d.go" , i ))
3426
+ if err := os .WriteFile (filename , []byte ("package p" ), 0666 ); err != nil {
3427
+ t .Fatal (err )
3428
+ }
3429
+ }
3430
+
3431
+ runner := gocommand.Runner {}
3432
+
3433
+ g , ctx := errgroup .WithContext (context .Background ())
3434
+ for range 10 {
3435
+ g .Go (func () error {
3436
+ stdout , stderr , friendlyErr , err := runner .RunRaw (ctx , gocommand.Invocation {
3437
+ Verb : "list" ,
3438
+ Args : []string {"-json" , "example.com/p" },
3439
+ WorkingDir : dir ,
3440
+ })
3441
+ if ctx .Err () != nil {
3442
+ return nil // don't report error if canceled
3443
+ }
3444
+ if err != nil || friendlyErr != nil {
3445
+ t .Fatalf ("go list failed: %v, %v (stdout=%s stderr=%s)" ,
3446
+ err , friendlyErr , stdout , stderr )
3447
+ }
3448
+ // Return an error so that concurrent invocations are canceled.
3449
+ return fmt .Errorf ("oops" )
3450
+ })
3451
+ }
3452
+ g .Wait () // ignore expected error
3453
+
3454
+ // This is the critical operation.
3455
+ if err := os .RemoveAll (dir ); err != nil {
3456
+ t .Fatalf ("failed to remove temp dir: %v" , err )
3457
+ }
3458
+ }
0 commit comments