github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/misc/genstd/mapping_test.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"go/ast"
     6  	"go/parser"
     7  	"os"
     8  	"path/filepath"
     9  	"sync"
    10  	"testing"
    11  
    12  	"github.com/stretchr/testify/assert"
    13  	"github.com/stretchr/testify/require"
    14  )
    15  
    16  const testdataDir = "github.com/gnolang/gno/misc/genstd/testdata/"
    17  
    18  var initWD = func() string {
    19  	d, err := os.Getwd()
    20  	if err != nil {
    21  		panic(err)
    22  	}
    23  	return d
    24  }()
    25  
    26  func chdir(t *testing.T, s string) {
    27  	t.Helper()
    28  
    29  	os.Chdir(filepath.Join(initWD, s))
    30  	t.Cleanup(func() {
    31  		os.Chdir(initWD)
    32  		dirsOnce = sync.Once{}
    33  		memoGitRoot, memoRelPath = "", ""
    34  	})
    35  }
    36  
    37  func Test_linkFunctions(t *testing.T) {
    38  	chdir(t, "testdata/linkFunctions")
    39  
    40  	pkgs, err := walkStdlibs(".")
    41  	require.NoError(t, err)
    42  
    43  	mappings := linkFunctions(pkgs)
    44  	require.Len(t, mappings, 8)
    45  
    46  	const (
    47  		ret = 1 << iota
    48  		param
    49  		machine
    50  	)
    51  	str := func(i int) string {
    52  		s := "Fn"
    53  		if i&machine != 0 {
    54  			s += "Machine"
    55  		}
    56  		if i&param != 0 {
    57  			s += "Param"
    58  		}
    59  		if i&ret != 0 {
    60  			s += "Ret"
    61  		}
    62  		return s
    63  	}
    64  
    65  	for i, v := range mappings {
    66  		exp := str(i)
    67  		assert.Equal(t, exp, v.GnoFunc)
    68  		assert.Equal(t, exp, v.GoFunc)
    69  		assert.Equal(t, "std", v.GnoImportPath)
    70  		assert.Equal(t, testdataDir+"linkFunctions/std", v.GoImportPath)
    71  
    72  		assert.Equal(t, i&machine != 0, v.MachineParam, "MachineParam should match expected value")
    73  		if i&param != 0 {
    74  			// require, otherwise the following would panic
    75  			require.Len(t, v.Params, 1)
    76  			p := v.Params[0]
    77  			assert.Equal(t, "int", p.GnoType())
    78  			assert.Equal(t, "int", p.GoQualifiedName())
    79  			assert.False(t, p.IsTypedValue)
    80  		} else {
    81  			assert.Len(t, v.Params, 0)
    82  		}
    83  		if i&ret != 0 {
    84  			// require, otherwise the following would panic
    85  			require.Len(t, v.Results, 1)
    86  			p := v.Results[0]
    87  			assert.Equal(t, "int", p.GnoType())
    88  			assert.Equal(t, "int", p.GoQualifiedName())
    89  			assert.False(t, p.IsTypedValue)
    90  		} else {
    91  			assert.Len(t, v.Results, 0)
    92  		}
    93  	}
    94  }
    95  
    96  func Test_linkFunctions_unexp(t *testing.T) {
    97  	chdir(t, "testdata/linkFunctions_unexp")
    98  
    99  	pkgs, err := walkStdlibs(".")
   100  	require.NoError(t, err)
   101  
   102  	mappings := linkFunctions(pkgs)
   103  	require.Len(t, mappings, 2)
   104  
   105  	assert.Equal(t, false, mappings[0].MachineParam)
   106  	assert.Equal(t, "t1", mappings[0].GnoFunc)
   107  	assert.Equal(t, "X_t1", mappings[0].GoFunc)
   108  
   109  	assert.Equal(t, true, mappings[1].MachineParam)
   110  	assert.Equal(t, "t2", mappings[1].GnoFunc)
   111  	assert.Equal(t, "X_t2", mappings[1].GoFunc)
   112  }
   113  
   114  func Test_linkFunctions_TypedValue(t *testing.T) {
   115  	chdir(t, "testdata/linkFunctions_TypedValue")
   116  
   117  	pkgs, err := walkStdlibs(".")
   118  	require.NoError(t, err)
   119  
   120  	mappings := linkFunctions(pkgs)
   121  	require.Len(t, mappings, 3)
   122  
   123  	assert.Equal(t, false, mappings[0].MachineParam)
   124  	assert.Equal(t, "TVParam", mappings[0].GnoFunc)
   125  	assert.Equal(t, "TVParam", mappings[0].GoFunc)
   126  	assert.Len(t, mappings[0].Results, 0)
   127  	_ = assert.Len(t, mappings[0].Params, 1) &&
   128  		assert.Equal(t, true, mappings[0].Params[0].IsTypedValue) &&
   129  		assert.Equal(t, "struct{m1 map[string]interface{}}", mappings[0].Params[0].GnoType())
   130  
   131  	assert.Equal(t, false, mappings[1].MachineParam)
   132  	assert.Equal(t, "TVResult", mappings[1].GnoFunc)
   133  	assert.Equal(t, "TVResult", mappings[1].GoFunc)
   134  	assert.Len(t, mappings[1].Params, 0)
   135  	_ = assert.Len(t, mappings[1].Results, 1) &&
   136  		assert.Equal(t, true, mappings[1].Results[0].IsTypedValue) &&
   137  		assert.Equal(t, "interface{S() map[int]Banker}", mappings[1].Results[0].GnoType())
   138  
   139  	assert.Equal(t, true, mappings[2].MachineParam)
   140  	assert.Equal(t, "TVFull", mappings[2].GnoFunc)
   141  	assert.Equal(t, "TVFull", mappings[2].GoFunc)
   142  	assert.Len(t, mappings[2].Params, 1)
   143  	assert.Len(t, mappings[2].Results, 1)
   144  }
   145  
   146  func Test_linkFunctions_noMatch(t *testing.T) {
   147  	chdir(t, "testdata/linkFunctions_noMatch")
   148  
   149  	pkgs, err := walkStdlibs(".")
   150  	require.NoError(t, err)
   151  
   152  	defer func() {
   153  		r := recover()
   154  		assert.NotNil(t, r)
   155  		assert.Contains(t, fmt.Sprint(r), "no matching go function declaration")
   156  	}()
   157  
   158  	linkFunctions(pkgs)
   159  }
   160  
   161  func Test_linkFunctions_noMatchSig(t *testing.T) {
   162  	chdir(t, "testdata/linkFunctions_noMatchSig")
   163  
   164  	pkgs, err := walkStdlibs(".")
   165  	require.NoError(t, err)
   166  
   167  	defer func() {
   168  		r := recover()
   169  		assert.NotNil(t, r)
   170  		assert.Contains(t, fmt.Sprint(r), "doesn't match signature of go function")
   171  	}()
   172  
   173  	linkFunctions(pkgs)
   174  }
   175  
   176  // typesEqual - separate tests.
   177  
   178  var typesEqualMapping = &mapping{
   179  	GnoImportPath: "std",
   180  	GnoFunc:       "Fn",
   181  	GoImportPath:  "github.com/gnolang/gno/gnovm/stdlibs/std",
   182  	GoFunc:        "Fn",
   183  	goImports: []*ast.ImportSpec{
   184  		{
   185  			Name: &ast.Ident{Name: "gno"},
   186  			Path: &ast.BasicLit{Value: `"github.com/gnolang/gno/gnovm/pkg/gnolang"`},
   187  		},
   188  		{
   189  			Path: &ast.BasicLit{Value: `"github.com/gnolang/gno/tm2/pkg/crypto"`},
   190  		},
   191  	},
   192  	gnoImports: []*ast.ImportSpec{
   193  		{
   194  			Path: &ast.BasicLit{Value: `"std"`},
   195  		},
   196  		{
   197  			Path: &ast.BasicLit{Value: `"math"`},
   198  		},
   199  	},
   200  }
   201  
   202  func Test_typesEqual(t *testing.T) {
   203  	tt := []struct {
   204  		gnoe, goe   string
   205  		errContains string
   206  	}{
   207  		{"int", "int", ""},
   208  		{"*[11][]rune", "*[11][ ]rune", ""},
   209  
   210  		{"madeup", "madeup", "non-builtin type"},
   211  
   212  		{"int", "string", "does not match"},
   213  		{"*int", "int", "does not match"},
   214  		{"string", "*string", "does not match"},
   215  		{"*string", "*int", "does not match"},
   216  
   217  		{"[]int", "[1]int", "does not match"},
   218  		{"[1]int", "[]int", "does not match"},
   219  		{"[2]int", "[2]string", "does not match"},
   220  		// valid, but unsupported (only BasicLits)
   221  		{"[(11)]int", "[(11)]string", "does not match"},
   222  
   223  		// even though mathematically equal, for simplicity we don't implement
   224  		// "true" basic lit equivalence
   225  		{"[8]int", "[0x8]int", "does not match"},
   226  	}
   227  
   228  	for idx, tv := range tt {
   229  		t.Run(fmt.Sprintf("%02d_%s", idx, tv.gnoe), func(t *testing.T) {
   230  			gnoe, err := parser.ParseExpr(tv.gnoe)
   231  			require.NoError(t, err)
   232  			goe, err := parser.ParseExpr(tv.goe)
   233  			require.NoError(t, err)
   234  
   235  			err = typesEqualMapping.typesEqual(gnoe, goe)
   236  			if tv.errContains == "" {
   237  				assert.NoError(t, err)
   238  			} else {
   239  				_ = assert.Error(t, err) &&
   240  					assert.Contains(t, err.Error(), tv.errContains)
   241  			}
   242  		})
   243  	}
   244  }
   245  
   246  func Test_typesEqual_panic(t *testing.T) {
   247  	tt := []struct {
   248  		gnoe, goe string
   249  		panic     string
   250  	}{
   251  		{"map[string]string", "map[string]string", "not implemented"},
   252  		{"func(s string)", "func(s string)", "not implemented"},
   253  		{"interface{}", "interface{}", "not implemented"},
   254  		{"struct{}", "struct{}", "not implemented"},
   255  		{"1 + 2", "1 + 2", "invalid expression"},
   256  	}
   257  
   258  	for _, tv := range tt {
   259  		t.Run(tv.gnoe, func(t *testing.T) {
   260  			gnoe, err := parser.ParseExpr(tv.gnoe)
   261  			require.NoError(t, err)
   262  			goe, err := parser.ParseExpr(tv.goe)
   263  			require.NoError(t, err)
   264  
   265  			defer func() {
   266  				r := recover()
   267  				if tv.panic == "" {
   268  					assert.Nil(t, r)
   269  				} else {
   270  					assert.Contains(t, fmt.Sprint(r), tv.panic)
   271  				}
   272  			}()
   273  
   274  			result := typesEqualMapping.typesEqual(gnoe, goe)
   275  			assert.Nil(t, result)
   276  		})
   277  	}
   278  }