github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/refactor/importgraph/graph_test.go (about) 1 // Copyright 2015 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 // Incomplete std lib sources on Android. 6 7 //go:build !android 8 // +build !android 9 10 package importgraph_test 11 12 import ( 13 "fmt" 14 "go/build" 15 "os" 16 "sort" 17 "strings" 18 "testing" 19 20 "golang.org/x/tools/go/packages/packagestest" 21 "golang.org/x/tools/refactor/importgraph" 22 23 _ "crypto/hmac" // just for test, below 24 ) 25 26 const this = "golang.org/x/tools/refactor/importgraph" 27 28 func TestBuild(t *testing.T) { 29 exported := packagestest.Export(t, packagestest.GOPATH, []packagestest.Module{ 30 {Name: "golang.org/x/tools/refactor/importgraph", Files: packagestest.MustCopyFileTree(".")}}) 31 defer exported.Cleanup() 32 33 var gopath string 34 for _, env := range exported.Config.Env { 35 eq := strings.Index(env, "=") 36 if eq == 0 { 37 // We sometimes see keys with a single leading "=" in the environment on Windows. 38 // TODO(#49886): What is the correct way to parse them in general? 39 eq = strings.Index(env[1:], "=") + 1 40 } 41 if eq < 0 { 42 t.Fatalf("invalid variable in exported.Config.Env: %q", env) 43 } 44 k := env[:eq] 45 v := env[eq+1:] 46 if k == "GOPATH" { 47 gopath = v 48 } 49 50 if os.Getenv(k) == v { 51 continue 52 } 53 defer func(prev string, prevOK bool) { 54 if !prevOK { 55 if err := os.Unsetenv(k); err != nil { 56 t.Fatal(err) 57 } 58 } else { 59 if err := os.Setenv(k, prev); err != nil { 60 t.Fatal(err) 61 } 62 } 63 }(os.LookupEnv(k)) 64 65 if err := os.Setenv(k, v); err != nil { 66 t.Fatal(err) 67 } 68 t.Logf("%s=%s", k, v) 69 } 70 if gopath == "" { 71 t.Fatal("Failed to fish GOPATH out of env: ", exported.Config.Env) 72 } 73 74 var buildContext = build.Default 75 buildContext.GOPATH = gopath 76 buildContext.Dir = exported.Config.Dir 77 78 forward, reverse, errs := importgraph.Build(&buildContext) 79 for path, err := range errs { 80 t.Errorf("%s: %s", path, err) 81 } 82 if t.Failed() { 83 return 84 } 85 86 // Log the complete graph before the errors, so that the errors are near the 87 // end of the log (where we expect them to be). 88 nodePrinted := map[string]bool{} 89 printNode := func(direction string, from string) { 90 key := fmt.Sprintf("%s[%q]", direction, from) 91 if nodePrinted[key] { 92 return 93 } 94 nodePrinted[key] = true 95 96 var g importgraph.Graph 97 switch direction { 98 case "forward": 99 g = forward 100 case "reverse": 101 g = reverse 102 default: 103 t.Helper() 104 t.Fatalf("bad direction: %q", direction) 105 } 106 107 t.Log(key) 108 var pkgs []string 109 for pkg := range g[from] { 110 pkgs = append(pkgs, pkg) 111 } 112 sort.Strings(pkgs) 113 for _, pkg := range pkgs { 114 t.Logf("\t%s", pkg) 115 } 116 } 117 118 if testing.Verbose() { 119 printNode("forward", this) 120 printNode("reverse", this) 121 } 122 123 // Test direct edges. 124 // We throw in crypto/hmac to prove that external test files 125 // (such as this one) are inspected. 126 for _, p := range []string{"go/build", "testing", "crypto/hmac"} { 127 if !forward[this][p] { 128 printNode("forward", this) 129 t.Errorf("forward[%q][%q] not found", this, p) 130 } 131 if !reverse[p][this] { 132 printNode("reverse", p) 133 t.Errorf("reverse[%q][%q] not found", p, this) 134 } 135 } 136 137 // Test non-existent direct edges 138 for _, p := range []string{"errors", "reflect"} { 139 if forward[this][p] { 140 printNode("forward", this) 141 t.Errorf("unexpected: forward[%q][%q] found", this, p) 142 } 143 if reverse[p][this] { 144 printNode("reverse", p) 145 t.Errorf("unexpected: reverse[%q][%q] found", p, this) 146 } 147 } 148 149 // Test Search is reflexive. 150 if !forward.Search(this)[this] { 151 printNode("forward", this) 152 t.Errorf("irreflexive: forward.Search(importgraph)[importgraph] not found") 153 } 154 if !reverse.Search(this)[this] { 155 printNode("reverse", this) 156 t.Errorf("irrefexive: reverse.Search(importgraph)[importgraph] not found") 157 } 158 159 // Test Search is transitive. (There is no direct edge to these packages.) 160 for _, p := range []string{"errors", "reflect", "unsafe"} { 161 if !forward.Search(this)[p] { 162 printNode("forward", this) 163 t.Errorf("intransitive: forward.Search(importgraph)[%s] not found", p) 164 } 165 if !reverse.Search(p)[this] { 166 printNode("reverse", p) 167 t.Errorf("intransitive: reverse.Search(%s)[importgraph] not found", p) 168 } 169 } 170 171 // Test strongly-connected components. Because A's external 172 // test package can depend on B, and vice versa, most of the 173 // standard libraries are mutually dependent when their external 174 // tests are considered. 175 // 176 // For any nodes x, y in the same SCC, y appears in the results 177 // of both forward and reverse searches starting from x 178 if !forward.Search("fmt")["io"] || 179 !forward.Search("io")["fmt"] || 180 !reverse.Search("fmt")["io"] || 181 !reverse.Search("io")["fmt"] { 182 printNode("forward", "fmt") 183 printNode("forward", "io") 184 printNode("reverse", "fmt") 185 printNode("reverse", "io") 186 t.Errorf("fmt and io are not mutually reachable despite being in the same SCC") 187 } 188 }