lab.nexedi.com/kirr/go123@v0.0.0-20240207185015-8299741fa871/tracing/cmd/gotrace/gotrace_test.go (about) 1 // Copyright (C) 2017-2021 Nexedi SA and Contributors. 2 // Kirill Smelkov <kirr@nexedi.com> 3 // 4 // This program is free software: you can Use, Study, Modify and Redistribute 5 // it under the terms of the GNU General Public License version 3, or (at your 6 // option) any later version, as published by the Free Software Foundation. 7 // 8 // You can also Link and Combine this program with other software covered by 9 // the terms of any of the Free Software licenses or any of the Open Source 10 // Initiative approved licenses and Convey the resulting work. Corresponding 11 // source of such a combination shall include the source code for all other 12 // software used. 13 // 14 // This program is distributed WITHOUT ANY WARRANTY; without even the implied 15 // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 16 // 17 // See COPYING file for full licensing terms. 18 // See https://www.nexedi.com/licensing for rationale and options. 19 20 package main 21 22 import ( 23 "bytes" 24 "fmt" 25 "go/build" 26 "io/ioutil" 27 "os" 28 "os/exec" 29 "path/filepath" 30 "runtime" 31 "strings" 32 "syscall" 33 "testing" 34 35 "lab.nexedi.com/kirr/go123/exc" 36 ) 37 38 type TreePrepareMode int 39 const ( 40 TreePrepareGolden TreePrepareMode = iota // prepare golden tree - how `gotrace gen` result should look like 41 TreePrepareWork // prepare work tree - inital state for `gotrace gen` to run 42 ) 43 44 // prepareTestTree copies files from src to dst recursively processing *.ok and *.rm depending on mode. 45 // 46 // dst should not initially exist 47 func prepareTestTree(src, dst string, mode TreePrepareMode) error { 48 err := os.MkdirAll(dst, 0777) 49 if err != nil { 50 return err 51 } 52 53 return filepath.Walk(src, func(srcpath string, info os.FileInfo, err error) error { 54 if srcpath == src /* skip root */ || err != nil { 55 return err 56 } 57 58 dstpath := dst + strings.TrimPrefix(srcpath, src) 59 if info.IsDir() { 60 err := os.Mkdir(dstpath, 0777) 61 return err 62 } 63 64 // NOTE since files are walked in lexical order <f>.ok or 65 // <f>.rm is always guaranteed to go after <f>. 66 67 var isOk, isRm bool 68 if strings.HasSuffix(srcpath, ".ok") { 69 isOk = true 70 dstpath = strings.TrimSuffix(dstpath, ".ok") 71 } 72 if strings.HasSuffix(srcpath, ".rm") { 73 isRm = true 74 dstpath = strings.TrimSuffix(dstpath, ".rm") 75 } 76 77 data, err := ioutil.ReadFile(srcpath) 78 if err != nil { 79 return err 80 } 81 82 switch mode { 83 case TreePrepareGolden: 84 // ok files are written as is 85 86 // no removed files 87 if isRm { 88 return nil 89 } 90 91 case TreePrepareWork: 92 // no ok files initially 93 if isOk { 94 return nil 95 } 96 // files to remove - prepopulate with magic 97 if isRm { 98 data = []byte(magic) 99 } 100 } 101 102 err = ioutil.WriteFile(dstpath, data, info.Mode()) 103 return err 104 }) 105 } 106 107 func xprepareTree(src, dst string, mode TreePrepareMode) { 108 err := prepareTestTree(src, dst, mode) 109 exc.Raiseif(err) 110 } 111 112 // diffR compares two directories recursively 113 func diffR(patha, pathb string) (diff string, err error) { 114 cmd := exec.Command("diff", "-urN", patha, pathb) 115 out, err := cmd.Output() 116 if e, ok := err.(*exec.ExitError); ok { 117 if e.Sys().(syscall.WaitStatus).ExitStatus() == 1 { 118 err = nil // diff signals with 1 just a difference - problem exit code is 2 119 } else { 120 err = fmt.Errorf("diff %s %s:\n%s", patha, pathb, e.Stderr) 121 } 122 } 123 124 return string(out), err 125 } 126 127 // haveReleaseTag returns whether current compiler has specified tag in its default build environment. 128 func haveReleaseTag(tag string) bool { 129 for _, rtag := range build.Default.ReleaseTags { 130 if tag == rtag { 131 return true 132 } 133 } 134 return false 135 } 136 137 func TestGoTrace(t *testing.T) { 138 tmp, err := ioutil.TempDir("", "t-gotrace") 139 if err != nil { 140 t.Fatal(err) 141 } 142 defer os.RemoveAll(tmp) 143 144 good := tmp + "/good" 145 work := tmp + "/work" 146 147 xprepareTree("testdata", good, TreePrepareGolden) 148 xprepareTree("testdata", work, TreePrepareWork) 149 150 // test build context with GOPATH set to work tree 151 var tBuildCtx = &build.Context{ 152 GOARCH: "amd64", 153 GOOS: "linux", 154 GOROOT: runtime.GOROOT(), 155 GOPATH: work, 156 CgoEnabled: true, 157 Compiler: runtime.Compiler, 158 } 159 160 // XXX autodetect (go list ?) 161 testv := []string{"a/pkg1", "b/pkg2", "c/pkg3", "d/pkg4"} 162 // cgo parsing works starting with Go 1.10 163 if haveReleaseTag("go1.10") { 164 testv = append(testv, "a/pkg1_cgo") 165 } 166 167 for _, tpkg := range testv { 168 // verify `gotrace gen` 169 err = tracegen(tpkg, tBuildCtx, "" /* = local imorts disabled */) 170 if err != nil { 171 t.Errorf("%v: %v", tpkg, err) 172 } 173 174 diff, err := diffR(good+"/src/"+tpkg, work+"/src/"+tpkg) 175 if err != nil { 176 t.Fatalf("%v: %v", tpkg, err) 177 } 178 179 if diff != "" { 180 t.Errorf("%v: gold & work differ:\n%s", tpkg, diff) 181 } 182 183 // verify `gotrace list` 184 var tlistBuf bytes.Buffer 185 err = tracelist(&tlistBuf, tpkg, tBuildCtx, "" /* = local imports disabled */) 186 if err != nil { 187 t.Fatalf("%v: %v", tpkg, err) 188 } 189 190 tlistOk, err := ioutil.ReadFile(work + "/src/" + tpkg + "/tracelist.txt") 191 if err != nil { 192 t.Fatalf("%v: %v", tpkg, err) 193 } 194 195 tlist := tlistBuf.Bytes() 196 if !bytes.Equal(tlist, tlistOk) { 197 t.Errorf("%v: tracelist differ:\nhave:\n%s\nwant:\n%s", tpkg, tlist, tlistOk) 198 } 199 } 200 }