Help us shape the future of Go! Take the 2026 Go Developer Survey

Text file src/cmd/go/testdata/script/list_modindex_dupactionid.txt

     1  # This is a regression test for #652435. It checks that we don't generate
     2  # multiple action entry ids for the same index file. That was happening
     3  # previously because we sometimes generated the action id with unclean
     4  # paths (and the rest of the time with clean paths) for the same package.
     5  # This test will use a go program ('check') to check the cache that there are
     6  # no two action entry files that point to the same object id.
     7  
     8  sleep 2s # Sleep so that the unpacked files are > 2 seconds old. The index won't be used if the modified times on the files are newer.
     9  go build -o check$GOEXE check.go
    10  cd mod
    11  env GOCACHE=$WORK/newcache # Run list command in a clean cache.
    12  go list all
    13  exec ../check$GOEXE
    14  
    15  -- mod/go.mod --
    16  module example.com/foo
    17  -- mod/foo.go --
    18  package foo
    19  -- check.go --
    20  package main
    21  
    22  import (
    23  	"errors"
    24  	"log"
    25  	"os"
    26  	"path/filepath"
    27  	"strings"
    28  )
    29  
    30  func main() {
    31  	cachedir := os.Getenv("GOCACHE")
    32  	if cachedir == "" {
    33  		log.Fatal("GOCACHE env var is empty; expected it to be set")
    34  	}
    35  
    36  	// Read the top level cache directory. The cache directory contains directories with
    37  	// each of the possible two hex digit prefixes (00-FF) of a cache entry's id.
    38  	// Those directories in turn contain files with the hex id followed by either
    39  	// "-a" for the action entries or "-d" for the object entries. We want to check
    40  	// if two action entries point to the same object entry so we'll iterate through
    41  	// all the "-a" files and see if any two of them refer to the same object id
    42  	// (corresponding to a "-d" file).
    43  	dirs, err := os.ReadDir(cachedir)
    44  	if err != nil {
    45  		log.Fatal(err)
    46  	}
    47  
    48  	seen := map[string]string{} // object id -> action id
    49  
    50  	for _, entry := range dirs {
    51  		if entry.IsDir() && len(entry.Name()) == 2 {
    52  			prefixdir := filepath.Join(cachedir, entry.Name())
    53  			entries, err := os.ReadDir(prefixdir)
    54  			if err != nil {
    55  				log.Fatal(err)
    56  			}
    57  
    58  			for _, entry := range entries {
    59  				if !strings.HasSuffix(entry.Name(), "-a") {
    60  					// not an action id entry
    61  					continue
    62  				}
    63  				actionEntryFile := filepath.Join(prefixdir, entry.Name())
    64  				objid, err := objectid(actionEntryFile)
    65  				if err != nil {
    66  					log.Fatal(err)
    67  				}
    68  				if other, ok := seen[objid]; ok {
    69  					log.Printf("found two action entry files (%s, %s) pointing to the same object id: %s", other, entry.Name(), objid)
    70  					os.Exit(1)
    71  				}
    72  				seen[objid] = entry.Name()
    73  			}
    74  		}
    75  	}
    76  }
    77  
    78  // objectid returns the object id that the given actionEntryFile points to.
    79  func objectid(actionEntryFile string) (string, error) {
    80  	// See cmd/go/internal/cache.(*DiskCache).get for the code that reads
    81  	// from the action entry files. The following is based on that function.
    82  	const (
    83  		HashSize  = 32
    84  		hexSize   = HashSize * 2
    85  		entrySize = 2 + 1 + hexSize + 1 + hexSize + 1 + 20 + 1 + 20 + 1
    86  	)
    87  
    88  	entry, err := os.ReadFile(actionEntryFile)
    89  	if err != nil {
    90  		return "", err
    91  	}
    92  	if len(entry) < entrySize {
    93  		return "", errors.New("entry file incomplete")
    94  	}
    95  	if entry[0] != 'v' || entry[1] != '1' || entry[2] != ' ' || entry[3+hexSize] != ' ' || entry[3+hexSize+1+hexSize] != ' ' || entry[3+hexSize+1+hexSize+1+20] != ' ' || entry[entrySize-1] != '\n' {
    96  		return "", errors.New("invalid header")
    97  	}
    98  	return string(entry[3+hexSize+1 : 3+hexSize+1+hexSize]), nil
    99  }
   100  

View as plain text