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