github.com/yunabe/lgo@v0.0.0-20190709125917-42c42d410fdf/converter/converter_test.go (about) 1 package converter 2 3 import ( 4 "flag" 5 "fmt" 6 "go/ast" 7 "go/importer" 8 "go/types" 9 "io/ioutil" 10 "os" 11 "os/exec" 12 "reflect" 13 "sort" 14 "strings" 15 "testing" 16 17 // Rebuild core library before this test if it's modified. 18 _ "github.com/yunabe/lgo/core" 19 ) 20 21 func calcDiff(s1, s2 string) (data []byte, err error) { 22 f1, err := ioutil.TempFile("", "converter_test") 23 if err != nil { 24 return 25 } 26 defer os.Remove(f1.Name()) 27 defer f1.Close() 28 29 f2, err := ioutil.TempFile("", "converter_test") 30 if err != nil { 31 return 32 } 33 defer os.Remove(f2.Name()) 34 defer f2.Close() 35 36 f1.WriteString(s1) 37 f2.WriteString(s2) 38 39 data, err = exec.Command("diff", "-u", f1.Name(), f2.Name()).CombinedOutput() 40 if len(data) > 0 { 41 // diff exits with a non-zero status when the files don't match. 42 // Ignore that failure as long as we get output. 43 err = nil 44 } 45 return 46 } 47 48 var update = flag.Bool("update", false, "update .golden files") 49 50 func checkGolden(t *testing.T, got string, golden string) { 51 b, err := ioutil.ReadFile(golden) 52 if err != nil && !*update { 53 t.Error(err) 54 return 55 } 56 expected := string(b) 57 if err == nil && got == expected { 58 return 59 } 60 if *update { 61 if err := ioutil.WriteFile(golden, []byte(got), 0666); err != nil { 62 t.Error(err) 63 } 64 return 65 } 66 d, err := calcDiff(expected, got) 67 if err != nil { 68 t.Errorf("Failed to calculate diff: %v", err) 69 return 70 } 71 t.Errorf("%s", d) 72 } 73 74 func TestUniqueSortedNames(t *testing.T) { 75 names := uniqueSortedNames([]*ast.Ident{ 76 {Name: "c"}, {Name: "a"}, {Name: "c"}, {Name: "z"}, {Name: "b"}, 77 }) 78 exp := []string{"a", "b", "c", "z"} 79 if !reflect.DeepEqual(exp, names) { 80 t.Errorf("Expected %v but got %v", exp, names) 81 } 82 } 83 84 func TestConvert_simple(t *testing.T) { 85 result := Convert(` 86 import ( 87 "fmt" 88 ) 89 import renamedio "io" 90 91 func fact(n int64) int64 { 92 if n > 0 { 93 return n * fact(n - 1) 94 } 95 return 1 96 } 97 98 type myStruct struct { 99 value int 100 } 101 102 func (m *myStruct) hello(name string) string { 103 return fmt.Sprintf("Hello %s!", name) 104 } 105 106 var sv myStruct 107 sp := &myStruct{} 108 msg0 := sv.hello("World0") 109 msg1 := sp.hello("World1") 110 111 const ( 112 ca = "hello" 113 cb = "piyo" 114 ) 115 116 func returnInterface() interface{method(int)float32} { 117 panic("not implemented") 118 } 119 120 inter := returnInterface() 121 122 f := fact(10) 123 var pi, pi2 float32 = 3.14, 6.28 124 125 var reader renamedio.Reader 126 `, &Config{}) 127 if result.Err != nil { 128 t.Error(result.Err) 129 return 130 } 131 checkGolden(t, result.Src, "testdata/simple.golden") 132 133 wantDeps := []string{"fmt", "io"} 134 if !reflect.DeepEqual(result.FinalDeps, wantDeps) { 135 t.Errorf("result.FinalDeps = %v; want %v", result.FinalDeps, wantDeps) 136 } 137 } 138 139 func TestConvert_novar(t *testing.T) { 140 result := Convert(` 141 func f(n int64) int64 { 142 return n * n 143 } 144 `, &Config{}) 145 if result.Err != nil { 146 t.Error(result.Err) 147 return 148 } 149 checkGolden(t, result.Src, "testdata/novar.golden") 150 } 151 152 func TestConvert_errorUndeclared(t *testing.T) { 153 // Variables must be declared explicitly. 154 result := Convert(` 155 var y = 10 156 x = y * y 157 `, &Config{}) 158 if result.Err == nil || !strings.Contains(result.Err.Error(), "undeclared name: x") { 159 t.Errorf("Unexpected error: %v", result.Err) 160 return 161 } 162 // Although, it's valid at the file scope in Go, it's invalid in lgo. 163 result = Convert(` 164 var x = y * y 165 var y = 10 166 `, &Config{}) 167 if result.Err == nil || !strings.Contains(result.Err.Error(), "undeclared name: y") { 168 t.Errorf("Unexpected error: %v", result.Err) 169 return 170 } 171 } 172 173 func TestConvert_withOld(t *testing.T) { 174 im := lgoImporter 175 bufio, err := im.Import("bufio") 176 if err != nil { 177 t.Error(err) 178 } 179 // Variables must be declared explicitly. 180 result := Convert(` 181 import pkg1 "io/ioutil" 182 183 var r = NewReader(nil) 184 c := pkg1.NopCloser(r) 185 `, &Config{ 186 Olds: []types.Object{bufio.Scope().Lookup("NewReader")}, 187 }) 188 if err != nil { 189 t.Error(err) 190 return 191 } 192 checkGolden(t, result.Src, "testdata/withold.golden") 193 } 194 195 func TestConvert_withOldPkgDup(t *testing.T) { 196 // This test demonstrates how old values are renamed if the package where an old value is defined is also imported in source code. 197 // This situation would not happen in the real world because old values must be defined in lgo-packages which should not be imported 198 // by import statements. 199 // 200 // In this test, use another importer other than defaultImporter here because 201 // we need to deferentiate Object from old values and the same Object 202 // referred in source code (NewReader and bufio.NewReader). 203 im := importer.Default() 204 205 bufio, err := im.Import("bufio") 206 if err != nil { 207 t.Error(err) 208 } 209 // Variables must be declared explicitly. 210 result := Convert(` 211 import "bufio" 212 213 var r0 = NewReader(nil) 214 var r1 = bufio.NewReader(nil) 215 `, &Config{ 216 Olds: []types.Object{bufio.Scope().Lookup("NewReader")}, 217 }) 218 if err != nil { 219 t.Error(err) 220 return 221 } 222 checkGolden(t, result.Src, "testdata/withold_pkgdup.golden") 223 } 224 225 func TestConvert_twoLgo(t *testing.T) { 226 result := Convert(` 227 func f(n int) int { 228 return n * n 229 } 230 type st struct { 231 value int 232 } 233 func (s *st) getValue() float32 { 234 return float32(s.value) 235 } 236 237 func getUnnamedStruct() struct{x int} { 238 return struct{x int}{10} 239 } 240 `, &Config{LgoPkgPath: "lgo/pkg0"}) 241 if result.Err != nil { 242 t.Error(result.Err) 243 return 244 } 245 pkg0 := result.Pkg 246 f := pkg0.Scope().Lookup("f") 247 st := pkg0.Scope().Lookup("st") 248 gu := pkg0.Scope().Lookup("getUnnamedStruct") 249 result = Convert(` 250 a := f(3) 251 s := st{ 252 value: 20, 253 } 254 b := s.value 255 c := s.getValue() 256 d := interface{getValue() float32}(&s) 257 f := d.getValue() 258 259 g := getUnnamedStruct() 260 var h struct{x int} = g 261 `, &Config{ 262 Olds: []types.Object{f, st, gu}, 263 }) 264 if result.Err != nil { 265 t.Error(result.Err) 266 return 267 } 268 checkGolden(t, result.Src, "testdata/twolgo.golden") 269 } 270 271 func TestConvert_twoLgo2(t *testing.T) { 272 result := Convert(` 273 x := 10 274 y := 20 275 `, &Config{LgoPkgPath: "lgo/pkg0"}) 276 if result.Err != nil { 277 t.Error(result.Err) 278 return 279 } 280 pkg0 := result.Pkg 281 // x in RHS of the first line refers to the old x. 282 result = Convert(` 283 x := x * x 284 func f() int { 285 return x + y 286 } 287 `, &Config{ 288 Olds: []types.Object{ 289 pkg0.Scope().Lookup("x"), 290 pkg0.Scope().Lookup("y"), 291 }, 292 }) 293 if result.Err != nil { 294 t.Error(result.Err) 295 return 296 } 297 checkGolden(t, result.Src, "testdata/twolgo2.golden") 298 299 result = Convert(` 300 func f() int { 301 return x + y 302 } 303 `, &Config{ 304 Olds: []types.Object{ 305 pkg0.Scope().Lookup("x"), 306 pkg0.Scope().Lookup("y"), 307 }, 308 }) 309 if result.Err != nil { 310 t.Error(result.Err) 311 return 312 } 313 checkGolden(t, result.Src, "testdata/twolgo3.golden") 314 } 315 316 func TestConvert_rename(t *testing.T) { 317 result := Convert(` 318 func f(n int) int { 319 return n * n 320 } 321 func Fn(n int) int { 322 b := func() int {return 10} 323 return b() 324 } 325 type st struct { 326 value int 327 } 328 func (s *st) getValue() float32 { 329 return float32(s.value) 330 } 331 332 type myInter interface { 333 Method0() 334 method() 335 } 336 337 func getInter() interface{method()} { 338 var i myInter 339 return i 340 } 341 342 v := f(3) 343 getInter().method() 344 myInter(nil).Method0() 345 s := st{ 346 value: 34, 347 } 348 `, &Config{ 349 DefPrefix: "Def_", 350 RefPrefix: "Ref_", 351 }) 352 if result.Err != nil { 353 t.Error(result.Err) 354 return 355 } 356 checkGolden(t, result.Src, "testdata/rename.golden") 357 } 358 359 func TestConvert_renameRefOtherPkgs(t *testing.T) { 360 result := Convert(` 361 func f(n int) int { 362 return n * n 363 } 364 type st struct { 365 value int 366 } 367 func (s *st) getValue() float32 { 368 return float32(s.value) 369 } 370 `, &Config{LgoPkgPath: "lgo/pkg0"}) 371 if result.Err != nil { 372 t.Error(result.Err) 373 return 374 } 375 pkg0 := result.Pkg 376 f := pkg0.Scope().Lookup("f") 377 st := pkg0.Scope().Lookup("st") 378 result = Convert(` 379 a := f(3) 380 s := st{ 381 value: 20, 382 } 383 var i interface{} = &s 384 i.(*st).getValue() 385 // Renaming to access unexported names in other packages is broken. 386 func myFunc() { 387 a := f(3) 388 s := st{ 389 value: a, 390 } 391 var i interface{} = &s 392 i.(*st).getValue() 393 } 394 `, &Config{ 395 DefPrefix: "Def_", 396 RefPrefix: "Ref_", 397 Olds: []types.Object{f, st}, 398 }) 399 if result.Err != nil { 400 t.Error(result.Err) 401 return 402 } 403 checkGolden(t, result.Src, "testdata/rename_other_pkgs.golden") 404 } 405 406 func TestConvert_unusedImport(t *testing.T) { 407 // Unused imports are renamed to "_" in the conversion. 408 // But the names are kept in result.Imports. 409 result := Convert(` 410 import ( 411 "fmt" 412 logger "log" 413 _ "image/png" 414 "io/ioutil" 415 ) 416 417 f, _ := ioutil.TempFile("a", "b") 418 `, &Config{LgoPkgPath: "lgo/pkg0"}) 419 if result.Err != nil { 420 t.Error(result.Err) 421 return 422 } 423 // fmt and logger are stripped. "os" is imported implicitly. 424 checkGolden(t, result.Src, "testdata/passimport0.golden") 425 426 var names []string 427 for _, im := range result.Imports { 428 names = append(names, im.Name()) 429 } 430 // Although "os" is imported implicitly, it's not exported to result.Imports. 431 expNames := []string{"fmt", "ioutil", "logger"} 432 if !reflect.DeepEqual(names, expNames) { 433 t.Errorf("Expected %#v but got %#v", expNames, names) 434 } 435 436 result = Convert(` 437 fmt.Println("Hello fmt!") 438 logger.Println("Hello log!") 439 `, &Config{ 440 OldImports: result.Imports, 441 }) 442 if result.Err != nil { 443 t.Error(result.Err) 444 return 445 } 446 checkGolden(t, result.Src, "testdata/passimport1.golden") 447 } 448 449 func TestConvert_lastExpr(t *testing.T) { 450 result := Convert(` 451 x := 10 452 x * x 453 `, &Config{LgoPkgPath: "lgo/pkg0"}) 454 if result.Err != nil { 455 t.Error(result.Err) 456 return 457 } 458 checkGolden(t, result.Src, "testdata/last_expr0.golden") 459 460 result = Convert(` 461 func f() {} 462 f() 463 `, &Config{LgoPkgPath: "lgo/pkg0"}) 464 if result.Err != nil { 465 t.Error(result.Err) 466 return 467 } 468 checkGolden(t, result.Src, "testdata/last_expr1.golden") 469 470 result = Convert(` 471 func f() (int, float32) { 472 return 10, 2.1 473 } 474 f() 475 `, &Config{LgoPkgPath: "lgo/pkg0"}) 476 if result.Err != nil { 477 t.Error(result.Err) 478 return 479 } 480 checkGolden(t, result.Src, "testdata/last_expr2.golden") 481 482 result = Convert(` 483 func f() int { 484 return 123 485 } 486 f() 487 `, &Config{LgoPkgPath: "lgo/pkg0"}) 488 if result.Err != nil { 489 t.Error(result.Err) 490 return 491 } 492 checkGolden(t, result.Src, "testdata/last_expr3.golden") 493 } 494 495 func TestConvert_emptyResult(t *testing.T) { 496 result := Convert("// Comment", &Config{LgoPkgPath: "lgo/pkg0"}) 497 if result.Err != nil { 498 t.Error(result.Err) 499 return 500 } 501 if result.Src != "" { 502 t.Errorf("Expected empty but got %q", result.Src) 503 } 504 } 505 506 func TestConvert_importOnly(t *testing.T) { 507 result := Convert(` 508 import ( 509 "fmt" 510 "os" 511 ) 512 `, &Config{LgoPkgPath: "lgo/pkg0", AutoExitCode: true}) 513 if result.Err != nil { 514 t.Error(result.Err) 515 return 516 } 517 checkGolden(t, result.Src, "testdata/import_only.golden") 518 var imports []string 519 for _, im := range result.Imports { 520 imports = append(imports, im.Name()) 521 } 522 sort.Strings(imports) 523 exp := []string{"fmt", "os"} 524 if !reflect.DeepEqual(imports, exp) { 525 t.Errorf("Expected %#v but got %#v", exp, imports) 526 } 527 } 528 529 func TestConvert_lgoctxBuiltin(t *testing.T) { 530 result := Convert(` 531 func waitCancel() { 532 <-_ctx.Done() 533 } 534 for { 535 select { 536 case <-_ctx.Done(): 537 break 538 } 539 } 540 `, &Config{LgoPkgPath: "lgo/pkg0"}) 541 if result.Err != nil { 542 t.Error(result.Err) 543 return 544 } 545 checkGolden(t, result.Src, "testdata/lgoctx_builtin.golden") 546 547 wantDeps := []string{"github.com/yunabe/lgo/core"} 548 if !reflect.DeepEqual(result.FinalDeps, wantDeps) { 549 t.Errorf("result.FinalDeps = %v; want %v", result.FinalDeps, wantDeps) 550 } 551 } 552 553 func TestConvert_autoExitCode(t *testing.T) { 554 result := Convert(` 555 func light(x int) int { 556 y := x * x 557 return y 558 } 559 560 func fcall(x int) int { 561 x = light(x) 562 x += 10 563 x = light(x) 564 x -= 10 565 return light(x) 566 } 567 568 func ifstmt() { 569 x := light(2) 570 if x > 10 { 571 } 572 573 x = light(3) 574 x += light(4) 575 if y := light(10); x - y < 0 { 576 } 577 578 x = light(4) 579 if x < light(10) { 580 } 581 } 582 583 func forstmt() { 584 x := light(1) 585 for i := 0; i < 10; i++ { 586 x += i 587 } 588 y := light(0) 589 for i := light(y);; { 590 y += i 591 } 592 } 593 594 func switchstmt() int { 595 x := light(2) 596 switch x { 597 case x * x: 598 x = 10 599 } 600 601 x = light(3) 602 switch x { 603 case light(4): 604 x = 10 605 } 606 607 // Inject exits into switch bodies. 608 switch x := light(10); x { 609 case 10: 610 light(x) 611 light(x + 1) 612 default: 613 light(x + 2) 614 light(x + 3) 615 } 616 617 // https://github.com/yunabe/lgo/issues/19 618 switch { 619 case x > 0: 620 light(x + 10) 621 } 622 return x 623 } 624 625 func deferstmt() int { 626 x := light(2) 627 defer light(light(4)) 628 y := light(3) 629 defer light(5) 630 z := light(10) 631 defer func() { 632 z += light(20) 633 for i := 0; i < x; i++ { 634 z += light(30) 635 } 636 f := func() { 637 z += 1 638 } 639 f() 640 f() 641 }() 642 return x * y + z 643 } 644 645 for i := 0; i < 100; i++ { 646 } 647 for {} 648 649 func chanFunc() (ret int) { 650 c := make(chan int) 651 select { 652 case i := <-c: 653 ret = i 654 } 655 select { 656 case <-c: 657 ret = ret * ret 658 default: 659 } 660 c <- 10 661 return 662 } 663 `, &Config{LgoPkgPath: "lgo/pkg0", AutoExitCode: true}) 664 if result.Err != nil { 665 t.Error(result.Err) 666 return 667 } 668 checkGolden(t, result.Src, "testdata/autoexit.golden") 669 } 670 671 func TestConvert_autoExitCodeVarOnly(t *testing.T) { 672 result := Convert(`var x int`, &Config{LgoPkgPath: "lgo/pkg0", AutoExitCode: true}) 673 if result.Err != nil { 674 t.Error(result.Err) 675 return 676 } 677 checkGolden(t, result.Src, "testdata/autoexit_varonly.golden") 678 } 679 680 func TestConvert_autoExitRecvOp(t *testing.T) { 681 result := Convert(` 682 type person struct { 683 name string 684 }`, &Config{LgoPkgPath: "lgo/pkg0"}) 685 if result.Err != nil { 686 t.Error(result.Err) 687 return 688 } 689 person := result.Pkg.Scope().Lookup("person") 690 result = Convert(` 691 func getCh() chan person { 692 return make(chan person) 693 }`, &Config{ 694 Olds: []types.Object{person}, 695 LgoPkgPath: "lgo/pkg1", 696 }) 697 if result.Err != nil { 698 t.Error(result.Err) 699 return 700 } 701 getCh := result.Pkg.Scope().Lookup("getCh") 702 result = Convert(` 703 func f(ch chan int) int { 704 return <-ch 705 } 706 func g(ch <-chan int) int { 707 return <-ch 708 } 709 func h(ch <-chan int) int { 710 if i, ok := <-ch; ok { 711 return i 712 } 713 return -1 714 } 715 type Person struct { 716 name string 717 Age int 718 } 719 func i() { 720 ch := make(chan Person) 721 <-ch 722 } 723 func j() { 724 <-getCh() 725 } 726 func k() { 727 ch := make(chan struct{ 728 name string 729 age int 730 }) 731 <-ch 732 } 733 `, &Config{ 734 Olds: []types.Object{getCh, person}, 735 LgoPkgPath: "lgo/pkg2", 736 AutoExitCode: true, 737 }) 738 if result.Err != nil { 739 t.Error(result.Err) 740 return 741 } 742 checkGolden(t, result.Src, "testdata/autoexit_recvop.golden") 743 } 744 745 func TestConvert_registerVars(t *testing.T) { 746 result := Convert(` 747 a := 10 748 b := 3.4 749 var c string 750 func f(n int) int { return n * n } 751 `, &Config{LgoPkgPath: "lgo/pkg0", RegisterVars: true}) 752 if result.Err != nil { 753 t.Error(result.Err) 754 return 755 } 756 checkGolden(t, result.Src, "testdata/register_vars.golden") 757 } 758 759 func TestConvert_registerVarsVarOnly(t *testing.T) { 760 // lgo_init should not be removed because it invokes LgoRegisterVars. 761 result := Convert("var c string", &Config{LgoPkgPath: "lgo/pkg0", RegisterVars: true}) 762 if result.Err != nil { 763 t.Error(result.Err) 764 return 765 } 766 checkGolden(t, result.Src, "testdata/register_vars_var_only.golden") 767 } 768 769 func TestConvert_wrapGoStmt(t *testing.T) { 770 tests := []struct { 771 name string 772 code string 773 }{ 774 { 775 name: "bind", code: ` 776 import "fmt" 777 for i := 0; i < 10; i++ { 778 go func(id int) { 779 fmt.Println("id =", id) 780 }(i) 781 }`}, { 782 name: "nest", code: ` 783 f := func(x, y int) int { return x + y } 784 go func(x int){ 785 go f(x, 20) 786 }(10)`}, { 787 name: "multiret", code: ` 788 import "fmt" 789 func xy(x, y int) { 790 fmt.Println(x, y) 791 } 792 func two() (int, int) { 793 return 3, 4 794 } 795 go xy(two())`}, { 796 name: "ellipsis", code: ` 797 import "fmt" 798 func xy(arg ...int) { 799 fmt.Println(arg) 800 } 801 func array() []int { 802 return []int{3, 4} 803 } 804 go xy(array()...)`}, 805 } 806 for _, tt := range tests { 807 t.Run(tt.name, func(t *testing.T) { 808 result := Convert(tt.code, &Config{LgoPkgPath: "lgo/pkg0", RegisterVars: true}) 809 if result.Err != nil { 810 t.Error(result.Err) 811 return 812 } 813 checkGolden(t, result.Src, fmt.Sprintf("testdata/wrap_gostmt_%s.golden", tt.name)) 814 }) 815 } 816 } 817 818 // Demostrates how converter keeps comments. 819 func TestConvert_comments(t *testing.T) { 820 result := Convert(`// Top-level comment 821 // The second line of the top-level comment 822 823 // dangling comments 824 825 // fn does nothing 826 func fn() { 827 // Do nothing 828 } 829 830 // MyType represents something 831 type MyType struct { 832 Name string // name 833 Age int // age 834 } 835 836 // Hello returns a hello message 837 func (m *MyType) Hello() string { 838 return "Hello " + m.Name 839 } 840 841 type MyInterface interface { 842 // DoSomething does something 843 DoSomething(x int) float32 844 Hello() string // Say hello 845 } 846 847 var ( 848 x int = 10 // Something 849 // y is string 850 y = "hello" 851 ) 852 const ( 853 c = "constant" // This is constant 854 // d is also constant 855 d = 123 856 ) 857 858 // alice is Alice 859 alice := &MyType{"Alice", 12} 860 bob := &MyType{"Bob", 45} // bob is Bob 861 862 // i is interface 863 var i interface{} = alice 864 var j interface{} = bob // j is also interface 865 `, &Config{LgoPkgPath: "lgo/pkg0"}) 866 if result.Err != nil { 867 t.Error(result.Err) 868 return 869 } 870 checkGolden(t, result.Src, "testdata/comments.golden") 871 } 872 873 func TestConvert_commentFirstLine(t *testing.T) { 874 result := Convert(`// fn does nothing 875 func fn() { 876 // Do nothing 877 }`, &Config{LgoPkgPath: "lgo/pkg0"}) 878 if result.Err != nil { 879 t.Error(result.Err) 880 return 881 } 882 checkGolden(t, result.Src, "testdata/comments_firstline.golden") 883 } 884 885 func TestConvert_commentFirstLineWithCore(t *testing.T) { 886 result := Convert(`// fn does nothing 887 func fn() { 888 } 889 <-_ctx.Done() 890 `, &Config{LgoPkgPath: "lgo/pkg0"}) 891 if result.Err != nil { 892 t.Error(result.Err) 893 return 894 } 895 checkGolden(t, result.Src, "testdata/comments_firstline__withcore.golden") 896 } 897 898 func TestConvert_commentFirstLineSlashAsterisk(t *testing.T) { 899 result := Convert(`/* fn does nothing */ 900 func fn() { 901 // Do nothing 902 }`, &Config{LgoPkgPath: "lgo/pkg0"}) 903 if result.Err != nil { 904 t.Error(result.Err) 905 return 906 } 907 // TODO: Fix this case 908 checkGolden(t, result.Src, "testdata/comments_firstline_slashasterisk.golden") 909 } 910 911 func TestConvert_commentFirstTrailing(t *testing.T) { 912 result := Convert(`const x = 10 // x is const int 913 `, &Config{LgoPkgPath: "lgo/pkg0"}) 914 if result.Err != nil { 915 t.Error(result.Err) 916 return 917 } 918 checkGolden(t, result.Src, "testdata/comments_firstline_trailing.golden") 919 } 920 921 func TestConvert_commentLastLine(t *testing.T) { 922 result := Convert(`const x int = 123 // x is x`, &Config{LgoPkgPath: "lgo/pkg0"}) 923 if result.Err != nil { 924 t.Error(result.Err) 925 return 926 } 927 checkGolden(t, result.Src, "testdata/comments_lastline.golden") 928 } 929 930 func Test_prependPrefixToID(t *testing.T) { 931 prefix := "Ref_" 932 tests := []struct { 933 name string 934 expect string 935 }{ 936 {name: "x", expect: "Ref_x"}, 937 {name: "x.y", expect: "x.Ref_y"}, 938 } 939 for _, tt := range tests { 940 ident := &ast.Ident{Name: tt.name} 941 prependPrefixToID(ident, prefix) 942 if ident.Name != tt.expect { 943 t.Errorf("Expected %q for %q but got %q", tt.expect, tt.name, ident.Name) 944 } 945 } 946 } 947 948 func TestConvert_workaroundBug11(t *testing.T) { 949 result := Convert(` 950 type Data struct { 951 value string 952 } 953 func (d Data) Value() string { 954 return d.value 955 } 956 type WithValue interface { 957 Value() string 958 } 959 `, &Config{LgoPkgPath: "lgo/pkg0"}) 960 if result.Err != nil { 961 t.Error(result.Err) 962 return 963 } 964 data := result.Pkg.Scope().Lookup("Data") 965 966 result = Convert(` 967 type Person struct { 968 name string 969 } 970 func (p *Person) GetName() string { 971 return p.name 972 } 973 `, &Config{LgoPkgPath: "lgo/pkg1"}) 974 if result.Err != nil { 975 t.Error(result.Err) 976 return 977 } 978 person := result.Pkg.Scope().Lookup("Person") 979 980 result = Convert(`d := Data{"hello"} 981 p := Person{"Alice"}`, &Config{ 982 Olds: []types.Object{data, person}, 983 LgoPkgPath: "lgo/pkg2", 984 }) 985 if result.Err != nil { 986 t.Error(result.Err) 987 return 988 } 989 d := result.Pkg.Scope().Lookup("d") 990 p := result.Pkg.Scope().Lookup("p") 991 992 result = Convert(`d.Value()`, &Config{Olds: []types.Object{d, p}}) 993 if result.Err != nil { 994 t.Error(result.Err) 995 return 996 } 997 checkGolden(t, result.Src, "testdata/bug11_single.golden") 998 result = Convert(`{ 999 d.Value() 1000 p.GetName() 1001 }`, &Config{Olds: []types.Object{d, p}}) 1002 if result.Err != nil { 1003 t.Error(result.Err) 1004 return 1005 } 1006 checkGolden(t, result.Src, "testdata/bug11_multi.golden") 1007 } 1008 1009 func TestConvert_underScoreInDefine(t *testing.T) { 1010 result := Convert(` 1011 import "os" 1012 _, err := os.Create("/path/file.txt") 1013 `, &Config{LgoPkgPath: "lgo/pkg0"}) 1014 // _ is *os.File, but do not define `var _ *os.File`. 1015 // See https://github.com/yunabe/lgo/issues/13 1016 if result.Err != nil { 1017 t.Fatal(result.Err) 1018 } 1019 checkGolden(t, result.Src, "testdata/underscore_in_define.golden") 1020 } 1021 1022 func TestConvert_labeledBranch(t *testing.T) { 1023 result := Convert(` 1024 import "fmt" 1025 1026 var i, j int 1027 outer: 1028 for i := 0;; i++ { 1029 for j := 0; j < i; j++ { 1030 if i > 10 && j > 10 { 1031 break outer 1032 } 1033 } 1034 } 1035 1036 if i + j > 0 { 1037 goto ok 1038 } 1039 1040 ok: 1041 fmt.Println("OK!") 1042 `, &Config{LgoPkgPath: "lgo/pkg0"}) 1043 if result.Err != nil { 1044 t.Fatal(result.Err) 1045 } 1046 checkGolden(t, result.Src, "testdata/labeled_branch.golden") 1047 } 1048 1049 func TestConvert_varUnderScoreOnly(t *testing.T) { 1050 result := Convert("var _ int", &Config{LgoPkgPath: "lgo/pkg0"}) 1051 if result.Err != nil { 1052 t.Fatal(result.Err) 1053 } 1054 checkGolden(t, result.Src, "testdata/var_uderscore_only.golden") 1055 } 1056 1057 type pkgInstallRecorder struct { 1058 pkgs []string 1059 } 1060 1061 func (r *pkgInstallRecorder) Install(pkgs []string) error { 1062 r.pkgs = pkgs 1063 return nil 1064 } 1065 1066 func TestConvert_installArchive(t *testing.T) { 1067 r := &pkgInstallRecorder{} 1068 SetPackageArchiveInstaller(r) 1069 defer SetPackageArchiveInstaller(nil) 1070 1071 Convert(` 1072 import "fmt" 1073 import "github.com/yunabe/dummypkg9171" 1074 `, &Config{LgoPkgPath: "lgo/pkg0"}) 1075 if len(r.pkgs) != 1 || r.pkgs[0] != "github.com/yunabe/dummypkg9171" { 1076 t.Errorf("Got %v; want [\"github.com/yunabe/dummypkg9171\"]", r.pkgs) 1077 } 1078 }