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