github.com/traefik/yaegi@v0.15.1/interp/interp_consistent_test.go (about) 1 package interp_test 2 3 import ( 4 "go/build" 5 "io" 6 "os" 7 "os/exec" 8 "path/filepath" 9 "strings" 10 "testing" 11 12 "github.com/traefik/yaegi/interp" 13 "github.com/traefik/yaegi/stdlib" 14 "github.com/traefik/yaegi/stdlib/unsafe" 15 ) 16 17 func TestInterpConsistencyBuild(t *testing.T) { 18 if testing.Short() { 19 t.Skip("short mode") 20 } 21 dir := filepath.Join("..", "_test", "tmp") 22 if _, err := os.Stat(dir); os.IsNotExist(err) { 23 if err := os.Mkdir(dir, 0o700); err != nil { 24 t.Fatal(err) 25 } 26 } 27 28 baseDir := filepath.Join("..", "_test") 29 files, err := os.ReadDir(baseDir) 30 if err != nil { 31 t.Fatal(err) 32 } 33 34 for _, file := range files { 35 if filepath.Ext(file.Name()) != ".go" || 36 file.Name() == "assign11.go" || // expect error 37 file.Name() == "assign12.go" || // expect error 38 file.Name() == "assign15.go" || // expect error 39 file.Name() == "bad0.go" || // expect error 40 file.Name() == "break0.go" || // expect error 41 file.Name() == "cont3.go" || // expect error 42 file.Name() == "const9.go" || // expect error 43 file.Name() == "export1.go" || // non-main package 44 file.Name() == "export0.go" || // non-main package 45 file.Name() == "for7.go" || // expect error 46 file.Name() == "fun21.go" || // expect error 47 file.Name() == "fun22.go" || // expect error 48 file.Name() == "fun23.go" || // expect error 49 file.Name() == "fun24.go" || // expect error 50 file.Name() == "fun25.go" || // expect error 51 file.Name() == "gen7.go" || // expect error 52 file.Name() == "gen8.go" || // expect error 53 file.Name() == "gen9.go" || // expect error 54 file.Name() == "if2.go" || // expect error 55 file.Name() == "import6.go" || // expect error 56 file.Name() == "init1.go" || // expect error 57 file.Name() == "io0.go" || // use random number 58 file.Name() == "issue-1093.go" || // expect error 59 file.Name() == "issue-1276.go" || // expect error 60 file.Name() == "issue-1330.go" || // expect error 61 file.Name() == "op1.go" || // expect error 62 file.Name() == "op7.go" || // expect error 63 file.Name() == "op9.go" || // expect error 64 file.Name() == "bltn0.go" || // expect error 65 file.Name() == "method16.go" || // private struct field 66 file.Name() == "method39.go" || // expect error 67 file.Name() == "switch8.go" || // expect error 68 file.Name() == "switch9.go" || // expect error 69 file.Name() == "switch13.go" || // expect error 70 file.Name() == "switch19.go" || // expect error 71 file.Name() == "time0.go" || // display time (similar to random number) 72 file.Name() == "factor.go" || // bench 73 file.Name() == "fib.go" || // bench 74 75 file.Name() == "type5.go" || // used to illustrate a limitation with no workaround, related to the fact that the reflect package does not allow the creation of named types 76 file.Name() == "type6.go" || // used to illustrate a limitation with no workaround, related to the fact that the reflect package does not allow the creation of named types 77 78 file.Name() == "redeclaration0.go" || // expect error 79 file.Name() == "redeclaration1.go" || // expect error 80 file.Name() == "redeclaration2.go" || // expect error 81 file.Name() == "redeclaration3.go" || // expect error 82 file.Name() == "redeclaration4.go" || // expect error 83 file.Name() == "redeclaration5.go" || // expect error 84 file.Name() == "redeclaration-global0.go" || // expect error 85 file.Name() == "redeclaration-global1.go" || // expect error 86 file.Name() == "redeclaration-global2.go" || // expect error 87 file.Name() == "redeclaration-global3.go" || // expect error 88 file.Name() == "redeclaration-global4.go" || // expect error 89 file.Name() == "redeclaration-global5.go" || // expect error 90 file.Name() == "redeclaration-global6.go" || // expect error 91 file.Name() == "redeclaration-global7.go" || // expect error 92 file.Name() == "pkgname0.go" || // has deps 93 file.Name() == "pkgname1.go" || // expect error 94 file.Name() == "pkgname2.go" || // has deps 95 file.Name() == "ipp_as_key.go" || // has deps 96 file.Name() == "restricted0.go" || // expect error 97 file.Name() == "restricted1.go" || // expect error 98 file.Name() == "restricted2.go" || // expect error 99 file.Name() == "restricted3.go" || // expect error 100 file.Name() == "server6.go" || // syntax parsing 101 file.Name() == "server5.go" || // syntax parsing 102 file.Name() == "server4.go" || // syntax parsing 103 file.Name() == "server3.go" || // syntax parsing 104 file.Name() == "server2.go" || // syntax parsing 105 file.Name() == "server1a.go" || // syntax parsing 106 file.Name() == "server1.go" || // syntax parsing 107 file.Name() == "server0.go" || // syntax parsing 108 file.Name() == "server.go" || // syntax parsing 109 file.Name() == "range9.go" || // expect error 110 file.Name() == "unsafe6.go" || // needs go.mod to be 1.17 111 file.Name() == "unsafe7.go" || // needs go.mod to be 1.17 112 file.Name() == "type24.go" || // expect error 113 file.Name() == "type27.go" || // expect error 114 file.Name() == "type28.go" || // expect error 115 file.Name() == "type29.go" || // expect error 116 file.Name() == "type30.go" || // expect error 117 file.Name() == "type31.go" || // expect error 118 file.Name() == "type32.go" || // expect error 119 file.Name() == "type33.go" { // expect error 120 continue 121 } 122 123 file := file 124 t.Run(file.Name(), func(t *testing.T) { 125 filePath := filepath.Join(baseDir, file.Name()) 126 127 // catch stdout 128 backupStdout := os.Stdout 129 defer func() { 130 os.Stdout = backupStdout 131 }() 132 r, w, _ := os.Pipe() 133 os.Stdout = w 134 135 i := interp.New(interp.Options{GoPath: build.Default.GOPATH}) 136 if err := i.Use(stdlib.Symbols); err != nil { 137 t.Fatal(err) 138 } 139 if err := i.Use(interp.Symbols); err != nil { 140 t.Fatal(err) 141 } 142 if err := i.Use(unsafe.Symbols); err != nil { 143 t.Fatal(err) 144 } 145 146 _, err = i.EvalPath(filePath) 147 if err != nil { 148 t.Fatal(err) 149 } 150 151 // read stdout 152 if err = w.Close(); err != nil { 153 t.Fatal(err) 154 } 155 outInterp, err := io.ReadAll(r) 156 if err != nil { 157 t.Fatal(err) 158 } 159 160 // restore Stdout 161 os.Stdout = backupStdout 162 163 bin := filepath.Join(dir, strings.TrimSuffix(file.Name(), ".go")) 164 165 cmdBuild := exec.Command("go", "build", "-tags=dummy", "-o", bin, filePath) 166 outBuild, err := cmdBuild.CombinedOutput() 167 if err != nil { 168 t.Log(string(outBuild)) 169 t.Fatal(err) 170 } 171 172 cmd := exec.Command(bin) 173 outRun, err := cmd.CombinedOutput() 174 if err != nil { 175 t.Log(string(outRun)) 176 t.Fatal(err) 177 } 178 179 if string(outInterp) != string(outRun) { 180 t.Errorf("\nGot: %q,\n want: %q", string(outInterp), string(outRun)) 181 } 182 }) 183 } 184 } 185 186 func TestInterpErrorConsistency(t *testing.T) { 187 testCases := []struct { 188 fileName string 189 expectedInterp string 190 expectedExec string 191 }{ 192 { 193 fileName: "assign11.go", 194 expectedInterp: "6:2: assignment mismatch: 3 variables but fmt.Println returns 2 values", 195 expectedExec: "6:12: assignment mismatch: 3 variables but fmt.Println returns 2 values", 196 }, 197 { 198 fileName: "assign12.go", 199 expectedInterp: "6:2: assignment mismatch: 3 variables but fmt.Println returns 2 values", 200 expectedExec: "6:13: assignment mismatch: 3 variables but fmt.Println returns 2 values", 201 }, 202 { 203 fileName: "bad0.go", 204 expectedInterp: "1:1: expected 'package', found println", 205 expectedExec: "1:1: expected 'package', found println", 206 }, 207 { 208 fileName: "break0.go", 209 expectedInterp: "15:5: invalid break label OuterLoop", 210 expectedExec: "15:11: invalid break label OuterLoop", 211 }, 212 { 213 fileName: "cont3.go", 214 expectedInterp: "15:5: invalid continue label OuterLoop", 215 expectedExec: "15:14: invalid continue label OuterLoop", 216 }, 217 { 218 fileName: "const9.go", 219 expectedInterp: "5:2: constant definition loop", 220 expectedExec: "5:2: initialization", 221 }, 222 { 223 fileName: "if2.go", 224 expectedInterp: "7:5: non-bool used as if condition", 225 expectedExec: "7:5: non-boolean condition in if statement", 226 }, 227 { 228 fileName: "for7.go", 229 expectedInterp: "4:14: non-bool used as for condition", 230 expectedExec: "4:14: non-boolean condition in for statement", 231 }, 232 { 233 fileName: "fun21.go", 234 expectedInterp: "4:2: not enough arguments to return", 235 expectedExec: "4:2: not enough return values", 236 }, 237 { 238 fileName: "fun22.go", 239 expectedInterp: "6:2: not enough arguments in call to time.Date", 240 expectedExec: "6:2: not enough arguments in call to time.Date", 241 }, 242 { 243 fileName: "fun23.go", 244 expectedInterp: "3:17: too many arguments to return", 245 expectedExec: "3:24: too many return values", 246 }, 247 { 248 fileName: "issue-1093.go", 249 expectedInterp: "9:6: cannot use type untyped string as type int in assignment", 250 expectedExec: `9:6: cannot use "a" + b() (value of type string)`, 251 }, 252 { 253 fileName: "op1.go", 254 expectedInterp: "5:2: invalid operation: mismatched types int and untyped float", 255 expectedExec: "5:7: 1.3 (untyped float constant) truncated to int", 256 }, 257 { 258 fileName: "bltn0.go", 259 expectedInterp: "4:7: use of builtin println not in function call", 260 expectedExec: "4:7: println (built-in) must be called", 261 }, 262 { 263 fileName: "import6.go", 264 expectedInterp: "import cycle not allowed", 265 expectedExec: "import cycle not allowed", 266 }, 267 { 268 fileName: "switch8.go", 269 expectedInterp: "5:2: fallthrough statement out of place", 270 expectedExec: "5:2: fallthrough statement out of place", 271 }, 272 { 273 fileName: "switch9.go", 274 expectedInterp: "9:3: cannot fallthrough in type switch", 275 expectedExec: "fallthrough", 276 }, 277 { 278 fileName: "switch13.go", 279 expectedInterp: "9:2: i is not a type", 280 expectedExec: "9:7: i (variable of type interface{}) is not a type", 281 }, 282 { 283 fileName: "switch19.go", 284 expectedInterp: "37:2: duplicate case Bir in type switch", 285 expectedExec: "37:7: duplicate case Bir in type switch", 286 }, 287 } 288 289 for _, test := range testCases { 290 t.Run(test.fileName, func(t *testing.T) { 291 if len(test.expectedInterp) == 0 && len(test.expectedExec) == 0 { 292 t.Fatal("at least expectedInterp must be define") 293 } 294 295 filePath := filepath.Join("..", "_test", test.fileName) 296 297 i := interp.New(interp.Options{GoPath: build.Default.GOPATH}) 298 if err := i.Use(stdlib.Symbols); err != nil { 299 t.Fatal(err) 300 } 301 302 _, errEval := i.EvalPath(filePath) 303 if errEval == nil { 304 t.Fatal("An error is expected but got none.") 305 } 306 307 if !strings.Contains(errEval.Error(), test.expectedInterp) { 308 t.Errorf("got %q, want: %q", errEval.Error(), test.expectedInterp) 309 } 310 311 cmd := exec.Command("go", "run", filePath) 312 outRun, errExec := cmd.CombinedOutput() 313 if errExec == nil { 314 t.Log(string(outRun)) 315 t.Fatal("An error is expected but got none.") 316 } 317 318 if len(test.expectedExec) == 0 && !strings.Contains(string(outRun), test.expectedInterp) { 319 t.Errorf("got %q, want: %q", string(outRun), test.expectedInterp) 320 } else if !strings.Contains(string(outRun), test.expectedExec) { 321 t.Errorf("got %q, want: %q", string(outRun), test.expectedExec) 322 } 323 }) 324 } 325 }