...

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

Documentation: cmd/go/testdata/script

     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'
     9sleep 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.
    10go build -o check$GOEXE check.go
    11cd mod
    12env GOCACHE=$WORK/newcache # Run list command in a clean cache.
    13go list all
    14exec ../check$GOEXE
    15
    16-- mod/go.mod --
    17module example.com/foo
    18-- mod/foo.go --
    19package foo
    20-- check.go --
    21package main
    22
    23import (
    24	"errors"
    25	"log"
    26	"os"
    27	"path/filepath"
    28	"strings"
    29)
    30
    31func 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.
    80func 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}

View as plain text