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