github.com/switchupcb/yaegi@v0.10.2/interp/interp_consistent_test.go (about)

     1  package interp_test
     2  
     3  import (
     4  	"go/build"
     5  	"io/ioutil"
     6  	"os"
     7  	"os/exec"
     8  	"path/filepath"
     9  	"strings"
    10  	"testing"
    11  
    12  	"github.com/switchupcb/yaegi/interp"
    13  	"github.com/switchupcb/yaegi/stdlib"
    14  	"github.com/switchupcb/yaegi/stdlib/unsafe"
    15  )
    16  
    17  func TestInterpConsistencyBuild(t *testing.T) {
    18  	if testing.Short() {
    19  		t.Skip("short mode")
    20  	}
    21  	dir := filepath.Join("..", "_test", "tmp")
    22  	if _, err := os.Stat(dir); os.IsNotExist(err) {
    23  		if err := os.Mkdir(dir, 0o700); err != nil {
    24  			t.Fatal(err)
    25  		}
    26  	}
    27  
    28  	baseDir := filepath.Join("..", "_test")
    29  	files, err := ioutil.ReadDir(baseDir)
    30  	if err != nil {
    31  		t.Fatal(err)
    32  	}
    33  
    34  	for _, file := range files {
    35  		if filepath.Ext(file.Name()) != ".go" ||
    36  			file.Name() == "assign11.go" || // expect error
    37  			file.Name() == "assign12.go" || // expect error
    38  			file.Name() == "assign15.go" || // expect error
    39  			file.Name() == "bad0.go" || // expect error
    40  			file.Name() == "const9.go" || // expect error
    41  			file.Name() == "export1.go" || // non-main package
    42  			file.Name() == "export0.go" || // non-main package
    43  			file.Name() == "for7.go" || // expect error
    44  			file.Name() == "fun21.go" || // expect error
    45  			file.Name() == "fun22.go" || // expect error
    46  			file.Name() == "fun23.go" || // expect error
    47  			file.Name() == "fun24.go" || // expect error
    48  			file.Name() == "fun25.go" || // expect error
    49  			file.Name() == "if2.go" || // expect error
    50  			file.Name() == "import6.go" || // expect error
    51  			file.Name() == "init1.go" || // expect error
    52  			file.Name() == "io0.go" || // use random number
    53  			file.Name() == "issue-1093.go" || // expect error
    54  			file.Name() == "issue-1276.go" || // expect error
    55  			file.Name() == "op1.go" || // expect error
    56  			file.Name() == "op7.go" || // expect error
    57  			file.Name() == "op9.go" || // expect error
    58  			file.Name() == "bltn0.go" || // expect error
    59  			file.Name() == "method16.go" || // private struct field
    60  			file.Name() == "method39.go" || // expect error
    61  			file.Name() == "switch8.go" || // expect error
    62  			file.Name() == "switch9.go" || // expect error
    63  			file.Name() == "switch13.go" || // expect error
    64  			file.Name() == "switch19.go" || // expect error
    65  			file.Name() == "time0.go" || // display time (similar to random number)
    66  			file.Name() == "factor.go" || // bench
    67  			file.Name() == "fib.go" || // bench
    68  
    69  			file.Name() == "type5.go" || // used to illustrate a limitation with no workaround, related to the fact that the reflect package does not allow the creation of named types
    70  			file.Name() == "type6.go" || // used to illustrate a limitation with no workaround, related to the fact that the reflect package does not allow the creation of named types
    71  
    72  			file.Name() == "redeclaration0.go" || // expect error
    73  			file.Name() == "redeclaration1.go" || // expect error
    74  			file.Name() == "redeclaration2.go" || // expect error
    75  			file.Name() == "redeclaration3.go" || // expect error
    76  			file.Name() == "redeclaration4.go" || // expect error
    77  			file.Name() == "redeclaration5.go" || // expect error
    78  			file.Name() == "redeclaration-global0.go" || // expect error
    79  			file.Name() == "redeclaration-global1.go" || // expect error
    80  			file.Name() == "redeclaration-global2.go" || // expect error
    81  			file.Name() == "redeclaration-global3.go" || // expect error
    82  			file.Name() == "redeclaration-global4.go" || // expect error
    83  			file.Name() == "redeclaration-global5.go" || // expect error
    84  			file.Name() == "redeclaration-global6.go" || // expect error
    85  			file.Name() == "redeclaration-global7.go" || // expect error
    86  			file.Name() == "pkgname0.go" || // has deps
    87  			file.Name() == "pkgname1.go" || // expect error
    88  			file.Name() == "pkgname2.go" || // has deps
    89  			file.Name() == "ipp_as_key.go" || // has deps
    90  			file.Name() == "restricted0.go" || // expect error
    91  			file.Name() == "restricted1.go" || // expect error
    92  			file.Name() == "restricted2.go" || // expect error
    93  			file.Name() == "restricted3.go" || // expect error
    94  			file.Name() == "server6.go" || // syntax parsing
    95  			file.Name() == "server5.go" || // syntax parsing
    96  			file.Name() == "server4.go" || // syntax parsing
    97  			file.Name() == "server3.go" || // syntax parsing
    98  			file.Name() == "server2.go" || // syntax parsing
    99  			file.Name() == "server1a.go" || // syntax parsing
   100  			file.Name() == "server1.go" || // syntax parsing
   101  			file.Name() == "server0.go" || // syntax parsing
   102  			file.Name() == "server.go" || // syntax parsing
   103  			file.Name() == "range9.go" || // expect error
   104  			file.Name() == "unsafe6.go" || // needs go.mod to be 1.17
   105  			file.Name() == "unsafe7.go" || // needs go.mod to be 1.17
   106  			file.Name() == "type27.go" || // expect error
   107  			file.Name() == "type28.go" || // expect error
   108  			file.Name() == "type29.go" || // expect error
   109  			file.Name() == "type30.go" || // expect error
   110  			file.Name() == "type31.go" || // expect error
   111  			file.Name() == "type32.go" || // expect error
   112  			file.Name() == "type33.go" { // expect error
   113  			continue
   114  		}
   115  
   116  		file := file
   117  		t.Run(file.Name(), func(t *testing.T) {
   118  			filePath := filepath.Join(baseDir, file.Name())
   119  
   120  			// catch stdout
   121  			backupStdout := os.Stdout
   122  			defer func() {
   123  				os.Stdout = backupStdout
   124  			}()
   125  			r, w, _ := os.Pipe()
   126  			os.Stdout = w
   127  
   128  			i := interp.New(interp.Options{GoPath: build.Default.GOPATH})
   129  			if err := i.Use(stdlib.Symbols); err != nil {
   130  				t.Fatal(err)
   131  			}
   132  			if err := i.Use(interp.Symbols); err != nil {
   133  				t.Fatal(err)
   134  			}
   135  			if err := i.Use(unsafe.Symbols); err != nil {
   136  				t.Fatal(err)
   137  			}
   138  
   139  			_, err = i.EvalPath(filePath)
   140  			if err != nil {
   141  				t.Fatal(err)
   142  			}
   143  
   144  			// read stdout
   145  			if err = w.Close(); err != nil {
   146  				t.Fatal(err)
   147  			}
   148  			outInterp, err := ioutil.ReadAll(r)
   149  			if err != nil {
   150  				t.Fatal(err)
   151  			}
   152  
   153  			// restore Stdout
   154  			os.Stdout = backupStdout
   155  
   156  			bin := filepath.Join(dir, strings.TrimSuffix(file.Name(), ".go"))
   157  
   158  			cmdBuild := exec.Command("go", "build", "-tags=dummy", "-o", bin, filePath)
   159  			outBuild, err := cmdBuild.CombinedOutput()
   160  			if err != nil {
   161  				t.Log(string(outBuild))
   162  				t.Fatal(err)
   163  			}
   164  
   165  			cmd := exec.Command(bin)
   166  			outRun, err := cmd.CombinedOutput()
   167  			if err != nil {
   168  				t.Log(string(outRun))
   169  				t.Fatal(err)
   170  			}
   171  
   172  			if string(outInterp) != string(outRun) {
   173  				t.Errorf("\nGot: %q,\n want: %q", string(outInterp), string(outRun))
   174  			}
   175  		})
   176  	}
   177  }
   178  
   179  func TestInterpErrorConsistency(t *testing.T) {
   180  	testCases := []struct {
   181  		fileName       string
   182  		expectedInterp string
   183  		expectedExec   string
   184  	}{
   185  		{
   186  			fileName:       "assign11.go",
   187  			expectedInterp: "6:2: assignment mismatch: 3 variables but fmt.Println returns 2 values",
   188  			expectedExec:   "6:10: assignment mismatch: 3 variables but fmt.Println returns 2 values",
   189  		},
   190  		{
   191  			fileName:       "assign12.go",
   192  			expectedInterp: "6:2: assignment mismatch: 3 variables but fmt.Println returns 2 values",
   193  			expectedExec:   "6:10: assignment mismatch: 3 variables but fmt.Println returns 2 values",
   194  		},
   195  		{
   196  			fileName:       "bad0.go",
   197  			expectedInterp: "1:1: expected 'package', found println",
   198  			expectedExec:   "1:1: expected 'package', found println",
   199  		},
   200  		{
   201  			fileName:       "const9.go",
   202  			expectedInterp: "5:2: constant definition loop",
   203  			expectedExec:   "5:2: constant definition loop",
   204  		},
   205  		{
   206  			fileName:       "if2.go",
   207  			expectedInterp: "7:5: non-bool used as if condition",
   208  			expectedExec:   "7:2: non-bool i % 1000000 (type int) used as if condition",
   209  		},
   210  		{
   211  			fileName:       "for7.go",
   212  			expectedInterp: "4:14: non-bool used as for condition",
   213  			expectedExec:   "4:2: non-bool i (type int) used as for condition",
   214  		},
   215  		{
   216  			fileName:       "fun21.go",
   217  			expectedInterp: "4:2: not enough arguments to return",
   218  			expectedExec:   "4:2: not enough arguments to return",
   219  		},
   220  		{
   221  			fileName:       "fun22.go",
   222  			expectedInterp: "6:2: not enough arguments in call to time.Date",
   223  			expectedExec:   "6:11: not enough arguments in call to time.Date",
   224  		},
   225  		{
   226  			fileName:       "fun23.go",
   227  			expectedInterp: "3:17: too many arguments to return",
   228  			expectedExec:   "3:17: too many arguments to return",
   229  		},
   230  		{
   231  			fileName:       "issue-1093.go",
   232  			expectedInterp: "9:6: cannot use type untyped string as type int in assignment",
   233  			expectedExec:   `9:4: cannot use "a" + b() (type string) as type int in assignment`,
   234  		},
   235  		{
   236  			fileName:       "op1.go",
   237  			expectedInterp: "5:2: invalid operation: mismatched types int and untyped float",
   238  			expectedExec:   "5:4: constant 1.3 truncated to integer",
   239  		},
   240  		{
   241  			fileName:       "bltn0.go",
   242  			expectedInterp: "4:7: use of builtin println not in function call",
   243  		},
   244  		{
   245  			fileName:       "import6.go",
   246  			expectedInterp: "import cycle not allowed",
   247  			expectedExec:   "import cycle not allowed",
   248  		},
   249  		{
   250  			fileName:       "switch8.go",
   251  			expectedInterp: "5:2: fallthrough statement out of place",
   252  			expectedExec:   "5:2: fallthrough statement out of place",
   253  		},
   254  		{
   255  			fileName:       "switch9.go",
   256  			expectedInterp: "9:3: cannot fallthrough in type switch",
   257  			expectedExec:   "9:3: cannot fallthrough in type switch",
   258  		},
   259  		{
   260  			fileName:       "switch13.go",
   261  			expectedInterp: "9:2: i is not a type",
   262  			expectedExec:   "9:2: i (type interface {}) is not a type",
   263  		},
   264  		{
   265  			fileName:       "switch19.go",
   266  			expectedInterp: "37:2: duplicate case Bir in type switch",
   267  			expectedExec:   "37:2: duplicate case Bir in type switch",
   268  		},
   269  	}
   270  
   271  	for _, test := range testCases {
   272  		t.Run(test.fileName, func(t *testing.T) {
   273  			if len(test.expectedInterp) == 0 && len(test.expectedExec) == 0 {
   274  				t.Fatal("at least expectedInterp must be define")
   275  			}
   276  
   277  			filePath := filepath.Join("..", "_test", test.fileName)
   278  
   279  			i := interp.New(interp.Options{GoPath: build.Default.GOPATH})
   280  			if err := i.Use(stdlib.Symbols); err != nil {
   281  				t.Fatal(err)
   282  			}
   283  
   284  			_, errEval := i.EvalPath(filePath)
   285  			if errEval == nil {
   286  				t.Fatal("An error is expected but got none.")
   287  			}
   288  
   289  			if !strings.Contains(errEval.Error(), test.expectedInterp) {
   290  				t.Errorf("got %q, want: %q", errEval.Error(), test.expectedInterp)
   291  			}
   292  
   293  			cmd := exec.Command("go", "run", filePath)
   294  			outRun, errExec := cmd.CombinedOutput()
   295  			if errExec == nil {
   296  				t.Log(string(outRun))
   297  				t.Fatal("An error is expected but got none.")
   298  			}
   299  
   300  			if len(test.expectedExec) == 0 && !strings.Contains(string(outRun), test.expectedInterp) {
   301  				t.Errorf("got %q, want: %q", string(outRun), test.expectedInterp)
   302  			} else if !strings.Contains(string(outRun), test.expectedExec) {
   303  				t.Errorf("got %q, want: %q", string(outRun), test.expectedExec)
   304  			}
   305  		})
   306  	}
   307  }