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  }