...

Source file src/golang.org/x/tools/cmd/deadcode/deadcode_test.go

Documentation: golang.org/x/tools/cmd/deadcode

     1  // Copyright 2023 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  //go:build go1.20
     6  
     7  package main_test
     8  
     9  import (
    10  	"bytes"
    11  	"fmt"
    12  	"os"
    13  	"os/exec"
    14  	"path/filepath"
    15  	"runtime"
    16  	"strconv"
    17  	"strings"
    18  	"testing"
    19  
    20  	"golang.org/x/tools/internal/testenv"
    21  	"golang.org/x/tools/txtar"
    22  )
    23  
    24  // Test runs the deadcode command on each scenario
    25  // described by a testdata/*.txtar file.
    26  func Test(t *testing.T) {
    27  	testenv.NeedsTool(t, "go")
    28  	if runtime.GOOS == "android" {
    29  		t.Skipf("the dependencies are not available on android")
    30  	}
    31  
    32  	exe := buildDeadcode(t)
    33  
    34  	matches, err := filepath.Glob("testdata/*.txtar")
    35  	if err != nil {
    36  		t.Fatal(err)
    37  	}
    38  	for _, filename := range matches {
    39  		filename := filename
    40  		t.Run(filename, func(t *testing.T) {
    41  			t.Parallel()
    42  
    43  			ar, err := txtar.ParseFile(filename)
    44  			if err != nil {
    45  				t.Fatal(err)
    46  			}
    47  
    48  			// Write the archive files to the temp directory.
    49  			tmpdir := t.TempDir()
    50  			for _, f := range ar.Files {
    51  				filename := filepath.Join(tmpdir, f.Name)
    52  				if err := os.MkdirAll(filepath.Dir(filename), 0777); err != nil {
    53  					t.Fatal(err)
    54  				}
    55  				if err := os.WriteFile(filename, f.Data, 0666); err != nil {
    56  					t.Fatal(err)
    57  				}
    58  			}
    59  
    60  			// Parse archive comment as directives of these forms:
    61  			//
    62  			//  [!]deadcode args...	command-line arguments
    63  			//  [!]want arg		expected/unwanted string in output (or stderr)
    64  			//
    65  			// Args may be Go-quoted strings.
    66  			type testcase struct {
    67  				linenum int
    68  				args    []string
    69  				wantErr bool
    70  				want    map[string]bool // string -> sense
    71  			}
    72  			var cases []*testcase
    73  			var current *testcase
    74  			for i, line := range strings.Split(string(ar.Comment), "\n") {
    75  				line = strings.TrimSpace(line)
    76  				if line == "" || line[0] == '#' {
    77  					continue // skip blanks and comments
    78  				}
    79  
    80  				words, err := words(line)
    81  				if err != nil {
    82  					t.Fatalf("cannot break line into words: %v (%s)", err, line)
    83  				}
    84  				switch kind := words[0]; kind {
    85  				case "deadcode", "!deadcode":
    86  					current = &testcase{
    87  						linenum: i + 1,
    88  						want:    make(map[string]bool),
    89  						args:    words[1:],
    90  						wantErr: kind[0] == '!',
    91  					}
    92  					cases = append(cases, current)
    93  				case "want", "!want":
    94  					if current == nil {
    95  						t.Fatalf("'want' directive must be after 'deadcode'")
    96  					}
    97  					if len(words) != 2 {
    98  						t.Fatalf("'want' directive needs argument <<%s>>", line)
    99  					}
   100  					current.want[words[1]] = kind[0] != '!'
   101  				default:
   102  					t.Fatalf("%s: invalid directive %q", filename, kind)
   103  				}
   104  			}
   105  
   106  			for _, tc := range cases {
   107  				t.Run(fmt.Sprintf("L%d", tc.linenum), func(t *testing.T) {
   108  					// Run the command.
   109  					cmd := exec.Command(exe, tc.args...)
   110  					cmd.Stdout = new(bytes.Buffer)
   111  					cmd.Stderr = new(bytes.Buffer)
   112  					cmd.Dir = tmpdir
   113  					cmd.Env = append(os.Environ(), "GOPROXY=", "GO111MODULE=on")
   114  					var got string
   115  					if err := cmd.Run(); err != nil {
   116  						if !tc.wantErr {
   117  							t.Fatalf("deadcode failed: %v (stderr=%s)", err, cmd.Stderr)
   118  						}
   119  						got = fmt.Sprint(cmd.Stderr)
   120  					} else {
   121  						if tc.wantErr {
   122  							t.Fatalf("deadcode succeeded unexpectedly (stdout=%s)", cmd.Stdout)
   123  						}
   124  						got = fmt.Sprint(cmd.Stdout)
   125  					}
   126  
   127  					// Check each want directive.
   128  					for str, sense := range tc.want {
   129  						ok := true
   130  						if strings.Contains(got, str) != sense {
   131  							if sense {
   132  								t.Errorf("missing %q", str)
   133  							} else {
   134  								t.Errorf("unwanted %q", str)
   135  							}
   136  							ok = false
   137  						}
   138  						if !ok {
   139  							t.Errorf("got: <<%s>>", got)
   140  						}
   141  					}
   142  				})
   143  			}
   144  		})
   145  	}
   146  }
   147  
   148  // buildDeadcode builds the deadcode executable.
   149  // It returns its path, and a cleanup function.
   150  func buildDeadcode(t *testing.T) string {
   151  	bin := filepath.Join(t.TempDir(), "deadcode")
   152  	if runtime.GOOS == "windows" {
   153  		bin += ".exe"
   154  	}
   155  	cmd := exec.Command("go", "build", "-o", bin)
   156  	if out, err := cmd.CombinedOutput(); err != nil {
   157  		t.Fatalf("Building deadcode: %v\n%s", err, out)
   158  	}
   159  	return bin
   160  }
   161  
   162  // words breaks a string into words, respecting
   163  // Go string quotations around words with spaces.
   164  func words(s string) ([]string, error) {
   165  	var words []string
   166  	for s != "" {
   167  		s = strings.TrimSpace(s)
   168  		var word string
   169  		if s[0] == '"' || s[0] == '`' {
   170  			prefix, err := strconv.QuotedPrefix(s)
   171  			if err != nil {
   172  				return nil, err
   173  			}
   174  			s = s[len(prefix):]
   175  			word, _ = strconv.Unquote(prefix)
   176  		} else {
   177  			prefix, rest, _ := strings.Cut(s, " ")
   178  			s = rest
   179  			word = prefix
   180  		}
   181  		words = append(words, word)
   182  	}
   183  	return words, nil
   184  }
   185  

View as plain text