github.com/julianthome/gore@v0.0.0-20231109011145-b3a6bbe6fe55/session_test.go (about) 1 package gore 2 3 import ( 4 "os" 5 "regexp" 6 "strings" 7 "testing" 8 9 "github.com/stretchr/testify/assert" 10 "github.com/stretchr/testify/require" 11 ) 12 13 func init() { 14 printerPkgs = printerPkgs[1:] 15 } 16 17 func newTempDir(t *testing.T) string { 18 dir, err := os.MkdirTemp("", "gore-test-") 19 if err != nil { 20 t.Fatal(err) 21 } 22 t.Cleanup(func() { os.RemoveAll(dir) }) 23 24 wd, err := os.Getwd() 25 if err != nil { 26 t.Fatal(err) 27 } 28 t.Cleanup(func() { os.Chdir(wd) }) 29 30 err = os.Chdir(dir) 31 if err != nil { 32 t.Fatal(err) 33 } 34 35 return dir 36 } 37 38 func TestSessionEval_import(t *testing.T) { 39 var stdout, stderr strings.Builder 40 s, err := NewSession(&stdout, &stderr) 41 t.Cleanup(func() { s.Clear() }) 42 require.NoError(t, err) 43 44 codes := []string{ 45 ":import encoding/json", 46 "b, err := json.Marshal(nil)", 47 "string(b)", 48 } 49 50 for _, code := range codes { 51 err := s.Eval(code) 52 require.NoError(t, err) 53 } 54 55 assert.Equal(t, `[]byte{0x6e, 0x75, 0x6c, 0x6c} 56 <nil> 57 "null" 58 `, stdout.String()) 59 assert.Equal(t, "", stderr.String()) 60 } 61 62 func TestSessionEval_QuickFix_evaluated_but_not_used(t *testing.T) { 63 var stdout, stderr strings.Builder 64 s, err := NewSession(&stdout, &stderr) 65 t.Cleanup(func() { s.Clear() }) 66 require.NoError(t, err) 67 68 codes := []string{ 69 `[]byte("")`, 70 `make([]int, 0)`, 71 `1+1`, 72 `func() {}`, 73 `(4 & (1 << 1))`, 74 `1`, 75 } 76 77 for _, code := range codes { 78 err := s.Eval(code) 79 require.NoError(t, err) 80 } 81 82 r := regexp.MustCompile(`0x[0-9a-f]+`) 83 assert.Equal(t, `[]byte{} 84 []int{} 85 2 86 (func())(...) 87 0 88 1 89 `, r.ReplaceAllString(stdout.String(), "...")) 90 assert.Equal(t, "", stderr.String()) 91 } 92 93 func TestSessionEval_QuickFix_used_as_value(t *testing.T) { 94 var stdout, stderr strings.Builder 95 s, err := NewSession(&stdout, &stderr) 96 t.Cleanup(func() { s.Clear() }) 97 require.NoError(t, err) 98 99 codes := []string{ 100 `:import log`, 101 `a := 1`, 102 `log.SetPrefix("")`, 103 } 104 105 for _, code := range codes { 106 err := s.Eval(code) 107 require.NoError(t, err) 108 } 109 110 assert.Equal(t, "1\n", stdout.String()) 111 assert.Equal(t, "", stderr.String()) 112 } 113 114 func TestSessionEval_QuickFix_no_new_variables(t *testing.T) { 115 var stdout, stderr strings.Builder 116 s, err := NewSession(&stdout, &stderr) 117 t.Cleanup(func() { s.Clear() }) 118 require.NoError(t, err) 119 120 codes := []string{ 121 `var a, b int`, 122 `a := 2`, 123 `b := a * 2`, 124 `a := 3`, 125 `c := a * b`, 126 `c := b * c`, 127 `b := c * a`, 128 `a * b * c`, 129 } 130 131 for _, code := range codes { 132 err := s.Eval(code) 133 require.NoError(t, err) 134 } 135 136 assert.Equal(t, `2 137 4 138 3 139 12 140 48 141 144 142 20736 143 `, stdout.String()) 144 assert.Equal(t, "", stderr.String()) 145 } 146 147 func TestSessionEval_AutoImport(t *testing.T) { 148 var stdout, stderr strings.Builder 149 s, err := NewSession(&stdout, &stderr) 150 t.Cleanup(func() { s.Clear() }) 151 require.NoError(t, err) 152 s.autoImport = true 153 154 codes := []string{ 155 `filepath.Join("a", "b")`, 156 } 157 158 for _, code := range codes { 159 err := s.Eval(code) 160 require.NoError(t, err) 161 } 162 163 assert.Equal(t, "\"a/b\"\n", stdout.String()) 164 assert.Equal(t, "", stderr.String()) 165 } 166 167 func TestSession_IncludePackage(t *testing.T) { 168 var stdout, stderr strings.Builder 169 s, err := NewSession(&stdout, &stderr) 170 t.Cleanup(func() { s.Clear() }) 171 require.NoError(t, err) 172 173 err = s.includePackage("github.com/x-motemen/gore/gocode") 174 require.NoError(t, err) 175 176 err = s.Eval("Completer{}") 177 require.NoError(t, err) 178 } 179 180 func TestSessionEval_Copy(t *testing.T) { 181 var stdout, stderr strings.Builder 182 s, err := NewSession(&stdout, &stderr) 183 t.Cleanup(func() { s.Clear() }) 184 require.NoError(t, err) 185 186 codes := []string{ 187 `a := []string{"hello", "world"}`, 188 `b := []string{"goodbye", "world"}`, 189 `copy(a, b)`, 190 `a[0]`, 191 } 192 193 for _, code := range codes { 194 err := s.Eval(code) 195 require.NoError(t, err) 196 } 197 198 assert.Equal(t, `[]string{"hello", "world"} 199 []string{"goodbye", "world"} 200 2 201 "goodbye" 202 `, stdout.String()) 203 assert.Equal(t, "", stderr.String()) 204 } 205 206 func TestSessionEval_Const(t *testing.T) { 207 var stdout, stderr strings.Builder 208 s, err := NewSession(&stdout, &stderr) 209 t.Cleanup(func() { s.Clear() }) 210 require.NoError(t, err) 211 212 codes := []string{ 213 `const ( a = iota; b )`, 214 `a`, 215 `b`, 216 } 217 218 for _, code := range codes { 219 err := s.Eval(code) 220 require.NoError(t, err) 221 } 222 223 assert.Equal(t, "0\n1\n0\n1\n", stdout.String()) 224 assert.Equal(t, "", stderr.String()) 225 } 226 227 func TestSessionEval_Declarations(t *testing.T) { 228 var stdout, stderr strings.Builder 229 s, err := NewSession(&stdout, &stderr) 230 t.Cleanup(func() { s.Clear() }) 231 require.NoError(t, err) 232 233 codes := []string{ 234 `var a, b int = 10, 20`, 235 `var (x int = 10; y string = "hello"; z uint64; w error; v func(string)int)`, 236 } 237 238 for _, code := range codes { 239 err := s.Eval(code) 240 require.NoError(t, err) 241 } 242 243 assert.Equal(t, `10 244 20 245 10 246 "hello" 247 0x0 248 <nil> 249 (func(string) int)(nil) 250 `, stdout.String()) 251 assert.Equal(t, "", stderr.String()) 252 } 253 254 func TestSessionEval_NotUsed(t *testing.T) { 255 var stdout, stderr strings.Builder 256 s, err := NewSession(&stdout, &stderr) 257 t.Cleanup(func() { s.Clear() }) 258 require.NoError(t, err) 259 260 codes := []string{ 261 `f := func() []int { return []int{1, 2, 3} }`, 262 `len(f())`, 263 `3`, 264 `len(f()) + len(f())`, 265 `var x int`, 266 `g := func() int { x++; return 128 }`, 267 `g() + g()`, 268 `g() * g()`, 269 `x`, 270 } 271 272 for _, code := range codes { 273 _ = s.Eval(code) 274 } 275 276 r := regexp.MustCompile(`0x[0-9a-f]+`) 277 assert.Equal(t, `(func() []int)(...) 278 3 279 3 280 6 281 (func() int)(...) 282 256 283 16384 284 4 285 `, r.ReplaceAllString(stdout.String(), "...")) 286 assert.Equal(t, "", stderr.String()) 287 } 288 289 func TestSessionEval_MultipleValues(t *testing.T) { 290 var stdout, stderr strings.Builder 291 s, err := NewSession(&stdout, &stderr) 292 t.Cleanup(func() { s.Clear() }) 293 require.NoError(t, err) 294 295 codes := []string{ 296 `var err error`, 297 `:import fmt`, 298 `fmt.Print()`, 299 `fmt.Print()`, 300 `:import io`, 301 `_, err = func() (int, error) { return 0, io.EOF }()`, 302 `err.Error()`, 303 `var x int`, 304 `x, err = 10, fmt.Errorf("test")`, 305 `x`, 306 `err.Error()`, 307 } 308 309 for _, code := range codes { 310 _ = s.Eval(code) 311 } 312 313 assert.Equal(t, `0 314 <nil> 315 0 316 <nil> 317 &errors.errorString{s:"EOF"} 318 "EOF" 319 10 320 &errors.errorString{s:"test"} 321 10 322 "test" 323 `, stdout.String()) 324 assert.Equal(t, "", stderr.String()) 325 } 326 327 func TestSessionEval_Struct(t *testing.T) { 328 var stdout, stderr strings.Builder 329 s, err := NewSession(&stdout, &stderr) 330 t.Cleanup(func() { s.Clear() }) 331 require.NoError(t, err) 332 333 codes := []string{ 334 `type X struct { v int }`, 335 `func (x *X) add(v int) { x.v += v }`, 336 `var x X`, 337 `x`, 338 `x.add(1)`, 339 `x`, 340 `x.add(2)`, 341 `x`, 342 `type Y X; type Z Y;`, 343 `func (z *Z) sub(v int) { z.v -= v }`, 344 `var z Z`, 345 `z.sub(3)`, 346 `z`, 347 } 348 349 for _, code := range codes { 350 _ = s.Eval(code) 351 } 352 353 assert.Contains(t, stdout.String(), `main.X{v:0} 354 main.X{v:1} 355 main.X{v:3} 356 main.Z{v:-3}`) 357 assert.Equal(t, ``, stderr.String()) 358 } 359 360 func TestSessionEval_Func(t *testing.T) { 361 var stdout, stderr strings.Builder 362 s, err := NewSession(&stdout, &stderr) 363 t.Cleanup(func() { s.Clear() }) 364 require.NoError(t, err) 365 366 codes := []string{ 367 `func f() int { return 100 }`, 368 `func g() string { return "hello, world" }`, 369 `func h() int { s := ""; return s }`, 370 `f() + len(g())`, 371 `func f() int { return 200 }`, 372 `f() * len(g())`, 373 `func f() string { i := 100; return i }`, 374 `f() | len(g())`, 375 } 376 377 for _, code := range codes { 378 _ = s.Eval(code) 379 } 380 381 assert.Equal(t, "112\n2400\n204\n", stdout.String()) 382 assert.Regexp(t, `cannot use s \((?:variable of )?type string\) as (?:type int|int value) in return (?:argument|statement) 383 invalid operation: f\(\) \+ len\(g\(\)\) \(mismatched types string and int\) 384 invalid operation: f\(\) \* len\(g\(\)\) \(mismatched types string and int\) 385 cannot use i \((?:variable of )?type int\) as (?:type string|string value) in return (?:argument|statement) 386 `, stderr.String()) 387 } 388 389 func TestSessionEval_TokenError(t *testing.T) { 390 var stdout, stderr strings.Builder 391 s, err := NewSession(&stdout, &stderr) 392 t.Cleanup(func() { s.Clear() }) 393 require.NoError(t, err) 394 395 codes := []string{ 396 `foo\`, 397 `ba # r`, 398 `$ + 3`, 399 "`foo", 400 "`foo\nbar`", 401 } 402 403 for _, code := range codes { 404 _ = s.Eval(code) 405 } 406 407 assert.Equal(t, "\"foo\\nbar\"\n", stdout.String()) 408 assert.Equal(t, `invalid token: "\\" 409 invalid token: "#" 410 invalid token: "$" 411 `, stderr.String()) 412 } 413 414 func TestSessionEval_CompileError(t *testing.T) { 415 var stdout, stderr strings.Builder 416 s, err := NewSession(&stdout, &stderr) 417 t.Cleanup(func() { s.Clear() }) 418 require.NoError(t, err) 419 420 codes := []string{ 421 `foo`, 422 `func f() int { return 100 }`, 423 `func g() string { return "hello" }`, 424 `len(f())`, 425 `len(g())`, 426 `f() + g()`, 427 `f() + len(g())`, 428 } 429 430 for _, code := range codes { 431 _ = s.Eval(code) 432 } 433 434 assert.Equal(t, "5\n105\n", stdout.String()) 435 assert.Regexp(t, `undefined: foo 436 invalid argument:? f\(\) \((?:value of )?type int\) for len 437 invalid operation: f\(\) \+ g\(\) \(mismatched types int and string\) 438 `, stderr.String()) 439 } 440 441 func TestSession_ExtraFiles(t *testing.T) { 442 var stdout, stderr strings.Builder 443 _ = newTempDir(t) 444 require.NoError(t, os.WriteFile("test.go", []byte(`package test 445 446 // V is a value 447 var V = 42 448 `), 0o644)) 449 s, err := NewSession(&stdout, &stderr) 450 t.Cleanup(func() { s.Clear() }) 451 require.NoError(t, err) 452 453 s.includeFiles([]string{"test.go"}) 454 codes := []string{ 455 `V`, 456 `:type V`, 457 `:doc V`, 458 } 459 460 for _, code := range codes { 461 _ = s.Eval(code) 462 } 463 464 assert.Contains(t, stdout.String(), `42 465 int 466 package builtin`) 467 assert.Equal(t, ``, stderr.String()) 468 }