github.com/traefik/yaegi@v0.15.1/cmd/yaegi/test.go (about)

     1  package main
     2  
     3  import (
     4  	"errors"
     5  	"flag"
     6  	"fmt"
     7  	"go/build"
     8  	"os"
     9  	"path/filepath"
    10  	"regexp"
    11  	"strconv"
    12  	"strings"
    13  	"testing"
    14  
    15  	"github.com/traefik/yaegi/interp"
    16  	"github.com/traefik/yaegi/stdlib"
    17  	"github.com/traefik/yaegi/stdlib/syscall"
    18  	"github.com/traefik/yaegi/stdlib/unrestricted"
    19  	"github.com/traefik/yaegi/stdlib/unsafe"
    20  )
    21  
    22  func test(arg []string) (err error) {
    23  	var (
    24  		bench     string
    25  		benchmem  bool
    26  		benchtime string
    27  		count     string
    28  		cpu       string
    29  		failfast  bool
    30  		run       string
    31  		short     bool
    32  		tags      string
    33  		timeout   string
    34  		verbose   bool
    35  	)
    36  
    37  	// The following flags are initialized from environment.
    38  	useSyscall, _ := strconv.ParseBool(os.Getenv("YAEGI_SYSCALL"))
    39  	useUnrestricted, _ := strconv.ParseBool(os.Getenv("YAEGI_UNRESTRICTED"))
    40  	useUnsafe, _ := strconv.ParseBool(os.Getenv("YAEGI_UNSAFE"))
    41  
    42  	tflag := flag.NewFlagSet("test", flag.ContinueOnError)
    43  	tflag.StringVar(&bench, "bench", "", "Run only those benchmarks matching a regular expression.")
    44  	tflag.BoolVar(&benchmem, "benchmem", false, "Print memory allocation statistics for benchmarks.")
    45  	tflag.StringVar(&benchtime, "benchtime", "", "Run enough iterations of each benchmark to take t.")
    46  	tflag.StringVar(&count, "count", "", "Run each test and benchmark n times (default 1).")
    47  	tflag.StringVar(&cpu, "cpu", "", "Specify a list of GOMAXPROCS values for which the tests or benchmarks should be executed.")
    48  	tflag.BoolVar(&failfast, "failfast", false, "Do not start new tests after the first test failure.")
    49  	tflag.StringVar(&run, "run", "", "Run only those tests matching a regular expression.")
    50  	tflag.BoolVar(&short, "short", false, "Tell long-running tests to shorten their run time.")
    51  	tflag.StringVar(&tags, "tags", "", "Set a list of build tags.")
    52  	tflag.StringVar(&timeout, "timeout", "", "If a test binary runs longer than duration d, panic.")
    53  	tflag.BoolVar(&useUnrestricted, "unrestricted", useUnrestricted, "Include unrestricted symbols.")
    54  	tflag.BoolVar(&useUnsafe, "unsafe", useUnsafe, "Include usafe symbols.")
    55  	tflag.BoolVar(&useSyscall, "syscall", useSyscall, "Include syscall symbols.")
    56  	tflag.BoolVar(&verbose, "v", false, "Verbose output: log all tests as they are run.")
    57  	tflag.Usage = func() {
    58  		fmt.Println("Usage: yaegi test [options] [path]")
    59  		fmt.Println("Options:")
    60  		tflag.PrintDefaults()
    61  	}
    62  	if err = tflag.Parse(arg); err != nil {
    63  		return err
    64  	}
    65  	args := tflag.Args()
    66  	path := "."
    67  	if len(args) > 0 {
    68  		path = args[0]
    69  	}
    70  
    71  	// Overwrite os.Args with correct flags to setup testing.Init.
    72  	tf := []string{""}
    73  	if bench != "" {
    74  		tf = append(tf, "-test.bench", bench)
    75  	}
    76  	if benchmem {
    77  		tf = append(tf, "-test.benchmem")
    78  	}
    79  	if benchtime != "" {
    80  		tf = append(tf, "-test.benchtime", benchtime)
    81  	}
    82  	if count != "" {
    83  		tf = append(tf, "-test.count", count)
    84  	}
    85  	if cpu != "" {
    86  		tf = append(tf, "-test.cpu", cpu)
    87  	}
    88  	if failfast {
    89  		tf = append(tf, "-test.failfast")
    90  	}
    91  	if run != "" {
    92  		tf = append(tf, "-test.run", run)
    93  	}
    94  	if short {
    95  		tf = append(tf, "-test.short")
    96  	}
    97  	if timeout != "" {
    98  		tf = append(tf, "-test.timeout", timeout)
    99  	}
   100  	if verbose {
   101  		tf = append(tf, "-test.v")
   102  	}
   103  	testing.Init()
   104  	os.Args = tf
   105  	flag.Parse()
   106  	path += string(filepath.Separator)
   107  	var dir string
   108  
   109  	switch strings.Split(path, string(filepath.Separator))[0] {
   110  	case ".", "..", string(filepath.Separator):
   111  		dir = path
   112  	default:
   113  		dir = filepath.Join(build.Default.GOPATH, "src", path)
   114  	}
   115  	if err = os.Chdir(dir); err != nil {
   116  		return err
   117  	}
   118  
   119  	i := interp.New(interp.Options{
   120  		GoPath:       build.Default.GOPATH,
   121  		BuildTags:    strings.Split(tags, ","),
   122  		Env:          os.Environ(),
   123  		Unrestricted: useUnrestricted,
   124  	})
   125  	if err := i.Use(stdlib.Symbols); err != nil {
   126  		return err
   127  	}
   128  	if err := i.Use(interp.Symbols); err != nil {
   129  		return err
   130  	}
   131  	if useSyscall {
   132  		if err := i.Use(syscall.Symbols); err != nil {
   133  			return err
   134  		}
   135  		// Using a environment var allows a nested interpreter to import the syscall package.
   136  		if err := os.Setenv("YAEGI_SYSCALL", "1"); err != nil {
   137  			return err
   138  		}
   139  	}
   140  	if useUnrestricted {
   141  		if err := i.Use(unrestricted.Symbols); err != nil {
   142  			return err
   143  		}
   144  		if err := os.Setenv("YAEGI_UNRESTRICTED", "1"); err != nil {
   145  			return err
   146  		}
   147  	}
   148  	if useUnsafe {
   149  		if err := i.Use(unsafe.Symbols); err != nil {
   150  			return err
   151  		}
   152  		if err := os.Setenv("YAEGI_UNSAFE", "1"); err != nil {
   153  			return err
   154  		}
   155  	}
   156  	if err = i.EvalTest(path); err != nil {
   157  		return err
   158  	}
   159  
   160  	benchmarks := []testing.InternalBenchmark{}
   161  	tests := []testing.InternalTest{}
   162  	syms, ok := i.Symbols(path)[path]
   163  	if !ok {
   164  		return errors.New("No tests found")
   165  	}
   166  	for name, sym := range syms {
   167  		switch fun := sym.Interface().(type) {
   168  		case func(*testing.B):
   169  			benchmarks = append(benchmarks, testing.InternalBenchmark{name, fun})
   170  		case func(*testing.T):
   171  			tests = append(tests, testing.InternalTest{name, fun})
   172  		}
   173  	}
   174  
   175  	testing.Main(regexp.MatchString, tests, benchmarks, nil)
   176  	return nil
   177  }