Source file src/cmd/compile/internal/ssa/generate_test.go

     1  // Copyright 2025 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package ssa
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"internal/testenv"
    11  	"os"
    12  	"path/filepath"
    13  	"testing"
    14  )
    15  
    16  const expectedHeader = "// Code generated from _gen/" // this is the common part
    17  
    18  // TestGeneratedFilesUpToDate regenerates all the rewrite and rewrite-related
    19  // files defined in _gen into a temporary directory,
    20  // checks that they match what appears in the source tree,
    21  // verifies that they start with the prefix of a generated header,
    22  // and checks that the only source files with that header were actually generated.
    23  func TestGeneratedFilesUpToDate(t *testing.T) {
    24  	testenv.MustHaveGoRun(t)
    25  	wd, err := os.Getwd()
    26  	if err != nil {
    27  		t.Fatalf("Failed to get current working directory: %v", err)
    28  	}
    29  	genDir := filepath.Join(wd, "_gen")
    30  	if _, err := os.Stat(genDir); os.IsNotExist(err) {
    31  		t.Fatalf("_gen directory not found")
    32  	}
    33  
    34  	tmpdir := t.TempDir()
    35  
    36  	// Accumulate a list of all existing files that look generated.
    37  	// It's an error if this set does not match the set that are
    38  	// generated into tmpdir.
    39  	genFiles := make(map[string]bool)
    40  	genPrefix := []byte(expectedHeader)
    41  	ssaFiles, err := filepath.Glob(filepath.Join(wd, "*.go"))
    42  	if err != nil {
    43  		t.Fatalf("could not glob for .go files in ssa directory: %v", err)
    44  	}
    45  	for _, f := range ssaFiles {
    46  		contents, err := os.ReadFile(f)
    47  		if err != nil {
    48  			t.Fatalf("could not read source file from ssa directory: %v", err)
    49  		}
    50  		// verify that the generated file has the expected header
    51  		// (this should cause other failures later, but if this is
    52  		// the problem, diagnose it here to shorten the treasure hunt.)
    53  		if bytes.HasPrefix(contents, genPrefix) {
    54  			genFiles[filepath.Base(f)] = true
    55  		}
    56  	}
    57  
    58  	goFiles, err := filepath.Glob(filepath.Join(genDir, "*.go"))
    59  	if err != nil {
    60  		t.Fatalf("could not glob for .go files in _gen: %v", err)
    61  	}
    62  	if len(goFiles) == 0 {
    63  		t.Fatal("no .go files found in _gen")
    64  	}
    65  
    66  	// Construct the command line for "go run".
    67  	// Explicitly list the files, just to make it
    68  	// clear what is included (if the test is logging).
    69  	args := []string{"run", "-C", genDir}
    70  	for _, f := range goFiles {
    71  		args = append(args, filepath.Base(f))
    72  	}
    73  	args = append(args, "-outdir", tmpdir)
    74  
    75  	logArgs := fmt.Sprintf("%v", args)
    76  	logArgs = logArgs[1 : len(logArgs)-2] // strip '[' and ']'
    77  	t.Logf("%s %v", testenv.GoToolPath(t), logArgs)
    78  	output, err := testenv.Command(t, testenv.GoToolPath(t), args...).CombinedOutput()
    79  
    80  	if err != nil {
    81  		t.Fatalf("go run in _gen failed: %v\n%s", err, output)
    82  	}
    83  
    84  	// Compare generated files with existing files in the parent directory.
    85  	files, err := os.ReadDir(tmpdir)
    86  	if err != nil {
    87  		t.Fatalf("could not read tmpdir %s: %v", tmpdir, err)
    88  	}
    89  
    90  	for _, file := range files {
    91  		if file.IsDir() {
    92  			continue
    93  		}
    94  		filename := file.Name()
    95  
    96  		// filename must be in the generated set,
    97  		if !genFiles[filename] {
    98  			t.Errorf("%s does not start with the expected header '%s' (if the header was changed the test needs to be updated)",
    99  				filename, expectedHeader)
   100  		}
   101  		genFiles[filename] = false // remove from set
   102  
   103  		generatedPath := filepath.Join(tmpdir, filename)
   104  		originalPath := filepath.Join(wd, filename)
   105  
   106  		generatedData, err := os.ReadFile(generatedPath)
   107  		if err != nil {
   108  			t.Errorf("could not read generated file %s: %v", generatedPath, err)
   109  			continue
   110  		}
   111  
   112  		// there should be a corresponding file in the ssa directory,
   113  		originalData, err := os.ReadFile(originalPath)
   114  		if err != nil {
   115  			if os.IsNotExist(err) {
   116  				t.Errorf("generated file %s was created, but does not exist in the ssa directory. It may need to be added to the repository.", filename)
   117  			} else {
   118  				t.Errorf("could not read original file %s: %v", originalPath, err)
   119  			}
   120  			continue
   121  		}
   122  
   123  		// and the contents of that file should match.
   124  		if !bytes.Equal(originalData, generatedData) {
   125  			t.Errorf("%s is out of date. Please run 'go generate'.", filename)
   126  		}
   127  	}
   128  
   129  	// the generated set should be empty now.
   130  	for file, notGenerated := range genFiles {
   131  		if notGenerated {
   132  			t.Errorf("%s has the header of a generated file but was not generated", file)
   133  		}
   134  	}
   135  }
   136  

View as plain text