github.com/gopherjs/gopherjs@v1.19.0-beta1.0.20240506212314-27071a8796e4/compiler/internal/typeparams/instance_test.go (about)

     1  package typeparams
     2  
     3  import (
     4  	"go/types"
     5  	"testing"
     6  
     7  	"github.com/google/go-cmp/cmp"
     8  	"github.com/google/go-cmp/cmp/cmpopts"
     9  	"github.com/gopherjs/gopherjs/internal/srctesting"
    10  	"github.com/gopherjs/gopherjs/internal/testingx"
    11  )
    12  
    13  func instanceOpts() cmp.Options {
    14  	return cmp.Options{
    15  		// Instances are represented by their IDs for diffing purposes.
    16  		cmp.Transformer("Instance", func(i Instance) string {
    17  			return i.String()
    18  		}),
    19  		// Order of instances in a slice doesn't matter, sort them by ID.
    20  		cmpopts.SortSlices(func(a, b Instance) bool {
    21  			return a.String() < b.String()
    22  		}),
    23  	}
    24  }
    25  
    26  func TestInstanceString(t *testing.T) {
    27  	const src = `package testcase
    28  
    29  	type Ints []int
    30  
    31  	type Typ[T any, V any] []T
    32  	func (t Typ[T, V]) Method(x T) {}
    33  
    34  	type typ[T any, V any] []T
    35  	func (t typ[T, V]) method(x T) {}
    36  
    37  	func Fun[U any, W any](x, y U) {}
    38  	func fun[U any, W any](x, y U) {}
    39  	`
    40  	f := srctesting.New(t)
    41  	_, pkg := f.Check("pkg/test", f.Parse("test.go", src))
    42  	mustType := testingx.Must[types.Type](t)
    43  
    44  	tests := []struct {
    45  		descr          string
    46  		instance       Instance
    47  		wantStr        string
    48  		wantTypeString string
    49  	}{{
    50  		descr: "exported type",
    51  		instance: Instance{
    52  			Object: pkg.Scope().Lookup("Typ"),
    53  			TArgs:  []types.Type{types.Typ[types.Int], types.Typ[types.String]},
    54  		},
    55  		wantStr:        "pkg/test.Typ<int, string>",
    56  		wantTypeString: "testcase.Typ[int, string]",
    57  	}, {
    58  		descr: "exported method",
    59  		instance: Instance{
    60  			Object: pkg.Scope().Lookup("Typ").Type().(*types.Named).Method(0),
    61  			TArgs:  []types.Type{types.Typ[types.Int], types.Typ[types.String]},
    62  		},
    63  		wantStr: "pkg/test.Typ.Method<int, string>",
    64  	}, {
    65  		descr: "exported function",
    66  		instance: Instance{
    67  			Object: pkg.Scope().Lookup("Fun"),
    68  			TArgs:  []types.Type{types.Typ[types.Int], types.Typ[types.String]},
    69  		},
    70  		wantStr: "pkg/test.Fun<int, string>",
    71  	}, {
    72  		descr: "unexported type",
    73  		instance: Instance{
    74  			Object: pkg.Scope().Lookup("typ"),
    75  			TArgs:  []types.Type{types.Typ[types.Int], types.Typ[types.String]},
    76  		},
    77  		wantStr:        "pkg/test.typ<int, string>",
    78  		wantTypeString: "testcase.typ[int, string]",
    79  	}, {
    80  		descr: "unexported method",
    81  		instance: Instance{
    82  			Object: pkg.Scope().Lookup("typ").Type().(*types.Named).Method(0),
    83  			TArgs:  []types.Type{types.Typ[types.Int], types.Typ[types.String]},
    84  		},
    85  		wantStr: "pkg/test.typ.method<int, string>",
    86  	}, {
    87  		descr: "unexported function",
    88  		instance: Instance{
    89  			Object: pkg.Scope().Lookup("fun"),
    90  			TArgs:  []types.Type{types.Typ[types.Int], types.Typ[types.String]},
    91  		},
    92  		wantStr: "pkg/test.fun<int, string>",
    93  	}, {
    94  		descr: "no type params",
    95  		instance: Instance{
    96  			Object: pkg.Scope().Lookup("Ints"),
    97  		},
    98  		wantStr:        "pkg/test.Ints",
    99  		wantTypeString: "testcase.Ints",
   100  	}, {
   101  		descr: "complex parameter type",
   102  		instance: Instance{
   103  			Object: pkg.Scope().Lookup("fun"),
   104  			TArgs: []types.Type{
   105  				types.NewSlice(types.Typ[types.Int]),
   106  				mustType(types.Instantiate(nil, pkg.Scope().Lookup("typ").Type(), []types.Type{
   107  					types.Typ[types.Int],
   108  					types.Typ[types.String],
   109  				}, true)),
   110  			},
   111  		},
   112  		wantStr: "pkg/test.fun<[]int, pkg/test.typ[int, string]>",
   113  	}}
   114  
   115  	for _, test := range tests {
   116  		t.Run(test.descr, func(t *testing.T) {
   117  			got := test.instance.String()
   118  			if got != test.wantStr {
   119  				t.Errorf("Got: instance string %q. Want: %q.", got, test.wantStr)
   120  			}
   121  			if test.wantTypeString != "" {
   122  				got = test.instance.TypeString()
   123  				if got != test.wantTypeString {
   124  					t.Errorf("Got: instance type string %q. Want: %q.", got, test.wantTypeString)
   125  				}
   126  			}
   127  		})
   128  	}
   129  }
   130  
   131  func TestInstanceQueue(t *testing.T) {
   132  	const src = `package test
   133  	type Typ[T any, V any] []T
   134  	func Fun[U any, W any](x, y U) {}
   135  	`
   136  	f := srctesting.New(t)
   137  	_, pkg := f.Check("pkg/test", f.Parse("test.go", src))
   138  
   139  	i1 := Instance{
   140  		Object: pkg.Scope().Lookup("Typ"),
   141  		TArgs:  []types.Type{types.Typ[types.String], types.Typ[types.String]},
   142  	}
   143  	i2 := Instance{
   144  		Object: pkg.Scope().Lookup("Typ"),
   145  		TArgs:  []types.Type{types.Typ[types.Int], types.Typ[types.Int]},
   146  	}
   147  	i3 := Instance{
   148  		Object: pkg.Scope().Lookup("Fun"),
   149  		TArgs:  []types.Type{types.Typ[types.String], types.Typ[types.String]},
   150  	}
   151  
   152  	set := InstanceSet{}
   153  	set.Add(i1, i2)
   154  
   155  	if ex := set.exhausted(); ex {
   156  		t.Errorf("Got: set.exhausted() = true. Want: false")
   157  	}
   158  
   159  	gotValues := set.Values()
   160  	wantValues := []Instance{i1, i2}
   161  	if diff := cmp.Diff(wantValues, gotValues, instanceOpts()); diff != "" {
   162  		t.Errorf("set.Values() returned diff (-want,+got):\n%s", diff)
   163  	}
   164  
   165  	p1, ok := set.next()
   166  	if !ok {
   167  		t.Errorf("Got: _, ok := set.next(); ok == false. Want: true.")
   168  	}
   169  	p2, ok := set.next()
   170  	if !ok {
   171  		t.Errorf("Got: _, ok := set.next(); ok == false. Want: true.")
   172  	}
   173  	if ex := set.exhausted(); !ex {
   174  		t.Errorf("Got: set.exhausted() = false. Want: true")
   175  	}
   176  
   177  	_, ok = set.next()
   178  	if ok {
   179  		t.Errorf("Got: _, ok := set.next(); ok == true. Want: false.")
   180  	}
   181  
   182  	set.Add(i1) // Has been enqueued before.
   183  	if ex := set.exhausted(); !ex {
   184  		t.Errorf("Got: set.exhausted() = false. Want: true")
   185  	}
   186  
   187  	set.Add(i3)
   188  	p3, ok := set.next()
   189  	if !ok {
   190  		t.Errorf("Got: _, ok := set.next(); ok == false. Want: true.")
   191  	}
   192  
   193  	added := []Instance{i1, i2, i3}
   194  	processed := []Instance{p1, p2, p3}
   195  
   196  	diff := cmp.Diff(added, processed, instanceOpts())
   197  	if diff != "" {
   198  		t.Errorf("Processed instances differ from added (-want,+got):\n%s", diff)
   199  	}
   200  
   201  	gotValues = set.Values()
   202  	wantValues = []Instance{i1, i2, i3}
   203  	if diff := cmp.Diff(wantValues, gotValues, instanceOpts()); diff != "" {
   204  		t.Errorf("set.Values() returned diff (-want,+got):\n%s", diff)
   205  	}
   206  }
   207  
   208  func TestInstancesByPackage(t *testing.T) {
   209  	f := srctesting.New(t)
   210  
   211  	const src1 = `package foo
   212  	type Typ[T any, V any] []T
   213  	`
   214  	_, foo := f.Check("pkg/foo", f.Parse("foo.go", src1))
   215  
   216  	const src2 = `package bar
   217  	func Fun[U any, W any](x, y U) {}
   218  	`
   219  	_, bar := f.Check("pkg/bar", f.Parse("bar.go", src2))
   220  
   221  	i1 := Instance{
   222  		Object: foo.Scope().Lookup("Typ"),
   223  		TArgs:  []types.Type{types.Typ[types.String], types.Typ[types.String]},
   224  	}
   225  	i2 := Instance{
   226  		Object: foo.Scope().Lookup("Typ"),
   227  		TArgs:  []types.Type{types.Typ[types.Int], types.Typ[types.Int]},
   228  	}
   229  	i3 := Instance{
   230  		Object: bar.Scope().Lookup("Fun"),
   231  		TArgs:  []types.Type{types.Typ[types.String], types.Typ[types.String]},
   232  	}
   233  
   234  	t.Run("Add", func(t *testing.T) {
   235  		instByPkg := PackageInstanceSets{}
   236  		instByPkg.Add(i1, i2, i3)
   237  
   238  		gotFooInstances := instByPkg.Pkg(foo).Values()
   239  		wantFooInstances := []Instance{i1, i2}
   240  		if diff := cmp.Diff(wantFooInstances, gotFooInstances, instanceOpts()); diff != "" {
   241  			t.Errorf("instByPkg.Pkg(foo).Values() returned diff (-want,+got):\n%s", diff)
   242  		}
   243  
   244  		gotValues := instByPkg.Pkg(bar).Values()
   245  		wantValues := []Instance{i3}
   246  		if diff := cmp.Diff(wantValues, gotValues, instanceOpts()); diff != "" {
   247  			t.Errorf("instByPkg.Pkg(bar).Values() returned diff (-want,+got):\n%s", diff)
   248  		}
   249  	})
   250  
   251  	t.Run("ID", func(t *testing.T) {
   252  		instByPkg := PackageInstanceSets{}
   253  		instByPkg.Add(i1, i2, i3)
   254  
   255  		got := []int{
   256  			instByPkg.ID(i1),
   257  			instByPkg.ID(i2),
   258  			instByPkg.ID(i3),
   259  		}
   260  		want := []int{0, 1, 0}
   261  
   262  		if diff := cmp.Diff(want, got); diff != "" {
   263  			t.Errorf("unexpected instance IDs assigned (-want,+got):\n%s", diff)
   264  		}
   265  	})
   266  }