gitlab.com/ethan.reesor/vscode-notebooks/yaegi@v0.0.0-20220417214422-5c573557938e/interp/interp_consistent_test.go (about)

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