golang.org/x/playground@v0.0.0-20230418134305-14ebe15bcd59/tests.go (about)

     1  // Copyright 2014 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Test tests are linked into the main binary and are run as part of
     6  // the Docker build step.
     7  
     8  package main
     9  
    10  import (
    11  	"context"
    12  	"fmt"
    13  	stdlog "log"
    14  	"net"
    15  	"os"
    16  	"reflect"
    17  	"strings"
    18  	"time"
    19  )
    20  
    21  type compileTest struct {
    22  	name               string // test name
    23  	prog, want, errors string
    24  	wantFunc           func(got string) error // alternative to want
    25  	withVet            bool
    26  	wantEvents         []Event
    27  	wantVetErrors      string
    28  }
    29  
    30  func (s *server) test() {
    31  	if _, err := net.ResolveIPAddr("ip", "sandbox_dev.sandnet."); err != nil {
    32  		log.Fatalf("sandbox_dev.sandnet not available")
    33  	}
    34  	os.Setenv("DEBUG_FORCE_GVISOR", "1")
    35  	os.Setenv("SANDBOX_BACKEND_URL", "http://sandbox_dev.sandnet/run")
    36  	s.runTests()
    37  }
    38  
    39  func (s *server) runTests() {
    40  	if err := s.healthCheck(context.Background()); err != nil {
    41  		stdlog.Fatal(err)
    42  	}
    43  
    44  	failed := false
    45  	for i, t := range tests {
    46  		stdlog.Printf("testing case %d (%q)...\n", i, t.name)
    47  		resp, err := compileAndRun(context.Background(), &request{Body: t.prog, WithVet: t.withVet})
    48  		if err != nil {
    49  			stdlog.Fatal(err)
    50  		}
    51  		if t.wantEvents != nil {
    52  			if !reflect.DeepEqual(resp.Events, t.wantEvents) {
    53  				stdlog.Printf("resp.Events = %q, want %q", resp.Events, t.wantEvents)
    54  				failed = true
    55  			}
    56  			continue
    57  		}
    58  		if t.errors != "" {
    59  			if resp.Errors != t.errors {
    60  				stdlog.Printf("resp.Errors = %q, want %q", resp.Errors, t.errors)
    61  				failed = true
    62  			}
    63  			continue
    64  		}
    65  		if resp.Errors != "" {
    66  			stdlog.Printf("resp.Errors = %q, want %q", resp.Errors, t.errors)
    67  			failed = true
    68  			continue
    69  		}
    70  		if resp.VetErrors != t.wantVetErrors {
    71  			stdlog.Printf("resp.VetErrs = %q, want %q", resp.VetErrors, t.wantVetErrors)
    72  			failed = true
    73  			continue
    74  		}
    75  		if t.withVet && (resp.VetErrors != "") == resp.VetOK {
    76  			stdlog.Printf("resp.VetErrs & VetOK inconsistent; VetErrs = %q; VetOK = %v", resp.VetErrors, resp.VetOK)
    77  			failed = true
    78  			continue
    79  		}
    80  		if len(resp.Events) == 0 {
    81  			stdlog.Printf("unexpected output: %q, want %q", "", t.want)
    82  			failed = true
    83  			continue
    84  		}
    85  		var b strings.Builder
    86  		for _, e := range resp.Events {
    87  			b.WriteString(e.Message)
    88  		}
    89  		if t.wantFunc != nil {
    90  			if err := t.wantFunc(b.String()); err != nil {
    91  				stdlog.Printf("%v\n", err)
    92  				failed = true
    93  			}
    94  		} else {
    95  			if !strings.Contains(b.String(), t.want) {
    96  				stdlog.Printf("unexpected output: %q, want %q", b.String(), t.want)
    97  				failed = true
    98  			}
    99  		}
   100  	}
   101  	if failed {
   102  		stdlog.Fatalf("FAILED")
   103  	}
   104  	fmt.Println("OK")
   105  }
   106  
   107  var tests = []compileTest{
   108  	{
   109  		name: "timezones_available",
   110  		prog: `
   111  package main
   112  
   113  import "time"
   114  
   115  func main() {
   116  	loc, err := time.LoadLocation("America/New_York")
   117  	if err != nil {
   118  		panic(err.Error())
   119  	}
   120  	println(loc.String())
   121  }
   122  `, want: "America/New_York"},
   123  
   124  	{
   125  		name: "faketime_works",
   126  		prog: `
   127  package main
   128  
   129  import (
   130  	"fmt"
   131  	"time"
   132  )
   133  
   134  func main() {
   135  	fmt.Println(time.Now())
   136  }
   137  `, want: "2009-11-10 23:00:00 +0000 UTC"},
   138  
   139  	{
   140  		name: "faketime_tickers",
   141  		prog: `
   142  package main
   143  
   144  import (
   145  	"fmt"
   146  	"time"
   147  )
   148  
   149  func main() {
   150  	t1 := time.Tick(time.Second * 3)
   151  	t2 := time.Tick(time.Second * 7)
   152  	t3 := time.Tick(time.Second * 11)
   153  	end := time.After(time.Second * 19)
   154  	want := "112131211"
   155  	var got []byte
   156  	for {
   157  		var c byte
   158  		select {
   159  		case <-t1:
   160  			c = '1'
   161  		case <-t2:
   162  			c = '2'
   163  		case <-t3:
   164  			c = '3'
   165  		case <-end:
   166  			if g := string(got); g != want {
   167  				fmt.Printf("got %q, want %q\n", g, want)
   168  			} else {
   169  				fmt.Println("timers fired as expected")
   170  			}
   171  			return
   172  		}
   173  		got = append(got, c)
   174  	}
   175  }
   176  `, want: "timers fired as expected"},
   177  	{
   178  		name: "must_be_package_main",
   179  		prog: `
   180  package test
   181  
   182  func main() {
   183  	println("test")
   184  }
   185  `, want: "", errors: "package name must be main"},
   186  	{
   187  		name: "filesystem_contents",
   188  		prog: `
   189  package main
   190  
   191  import (
   192  	"fmt"
   193  	"os"
   194  	"path/filepath"
   195  )
   196  
   197  func main() {
   198  	filepath.Walk("/", func(path string, info os.FileInfo, err error) error {
   199  		if path == "/proc" || path == "/sys" {
   200  			return filepath.SkipDir
   201  		}
   202  		fmt.Println(path)
   203  		return nil
   204  	})
   205  }
   206  `, wantFunc: func(got string) error {
   207  			// The environment for the old nacl sandbox:
   208  			if strings.TrimSpace(got) == `/
   209  /dev
   210  /dev/null
   211  /dev/random
   212  /dev/urandom
   213  /dev/zero
   214  /etc
   215  /etc/group
   216  /etc/hosts
   217  /etc/passwd
   218  /etc/resolv.conf
   219  /tmp
   220  /usr
   221  /usr/local
   222  /usr/local/go
   223  /usr/local/go/lib
   224  /usr/local/go/lib/time
   225  /usr/local/go/lib/time/zoneinfo.zip` {
   226  				return nil
   227  			}
   228  			have := map[string]bool{}
   229  			for _, f := range strings.Split(got, "\n") {
   230  				have[f] = true
   231  			}
   232  			for _, expect := range []string{
   233  				"/.dockerenv",
   234  				"/etc/hostname",
   235  				"/dev/zero",
   236  				"/lib/ld-linux-x86-64.so.2",
   237  				"/lib/libc.so.6",
   238  				"/etc/nsswitch.conf",
   239  				"/bin/env",
   240  				"/tmpfs",
   241  			} {
   242  				if !have[expect] {
   243  					return fmt.Errorf("missing expected sandbox file %q; got:\n%s", expect, got)
   244  				}
   245  			}
   246  			return nil
   247  		},
   248  	},
   249  	{
   250  		name: "test_passes",
   251  		prog: `
   252  package main
   253  
   254  import "testing"
   255  
   256  func TestSanity(t *testing.T) {
   257  	if 1+1 != 2 {
   258  		t.Error("uhh...")
   259  	}
   260  }
   261  `, want: `=== RUN   TestSanity
   262  --- PASS: TestSanity (0.00s)
   263  PASS`},
   264  
   265  	{
   266  		name: "test_without_import",
   267  		prog: `
   268  package main
   269  
   270  func TestSanity(t *testing.T) {
   271  	t.Error("uhh...")
   272  }
   273  
   274  func ExampleNotExecuted() {
   275  	// Output: it should not run
   276  }
   277  `, want: "", errors: "./prog_test.go:4:20: undefined: testing\n"},
   278  
   279  	{
   280  		name: "test_with_import_ignored",
   281  		prog: `
   282  package main
   283  
   284  import (
   285  	"fmt"
   286  	"testing"
   287  )
   288  
   289  func TestSanity(t *testing.T) {
   290  	t.Error("uhh...")
   291  }
   292  
   293  func main() {
   294  	fmt.Println("test")
   295  }
   296  `, want: "test"},
   297  
   298  	{
   299  		name: "example_runs",
   300  		prog: `
   301  package main//comment
   302  
   303  import "fmt"
   304  
   305  func ExampleOutput() {
   306  	fmt.Println("The output")
   307  	// Output: The output
   308  }
   309  `, want: `=== RUN   ExampleOutput
   310  --- PASS: ExampleOutput (0.00s)
   311  PASS`},
   312  
   313  	{
   314  		name: "example_unordered",
   315  		prog: `
   316  package main//comment
   317  
   318  import "fmt"
   319  
   320  func ExampleUnorderedOutput() {
   321  	fmt.Println("2")
   322  	fmt.Println("1")
   323  	fmt.Println("3")
   324  	// Unordered output: 3
   325  	// 2
   326  	// 1
   327  }
   328  `, want: `=== RUN   ExampleUnorderedOutput
   329  --- PASS: ExampleUnorderedOutput (0.00s)
   330  PASS`},
   331  
   332  	{
   333  		name: "example_fail",
   334  		prog: `
   335  package main
   336  
   337  import "fmt"
   338  
   339  func ExampleEmptyOutput() {
   340  	// Output:
   341  }
   342  
   343  func ExampleEmptyOutputFail() {
   344  	fmt.Println("1")
   345  	// Output:
   346  }
   347  `, want: `=== RUN   ExampleEmptyOutput
   348  --- PASS: ExampleEmptyOutput (0.00s)
   349  === RUN   ExampleEmptyOutputFail
   350  --- FAIL: ExampleEmptyOutputFail (0.00s)
   351  got:
   352  1
   353  want:
   354  
   355  FAIL`},
   356  
   357  	// Run program without executing this example function.
   358  	{
   359  		name: "example_no_output_skips_run",
   360  		prog: `
   361  package main
   362  
   363  func ExampleNoOutput() {
   364  	panic(1)
   365  }
   366  `, want: `testing: warning: no tests to run
   367  PASS`},
   368  
   369  	{
   370  		name: "example_output",
   371  		prog: `
   372  package main
   373  
   374  import "fmt"
   375  
   376  func ExampleShouldNotRun() {
   377  	fmt.Println("The output")
   378  	// Output: The output
   379  }
   380  
   381  func main() {
   382  	fmt.Println("Main")
   383  }
   384  `, want: "Main"},
   385  
   386  	{
   387  		name: "stdout_stderr_merge",
   388  		prog: `
   389  package main
   390  
   391  import (
   392  	"fmt"
   393  	"os"
   394  )
   395  
   396  func main() {
   397  	fmt.Fprintln(os.Stdout, "A")
   398  	fmt.Fprintln(os.Stderr, "B")
   399  	fmt.Fprintln(os.Stdout, "A")
   400  	fmt.Fprintln(os.Stdout, "A")
   401  }
   402  `, want: "A\nB\nA\nA\n"},
   403  
   404  	// Integration test for runtime.write fake timestamps.
   405  	{
   406  		name: "faketime_write_interaction",
   407  		prog: `
   408  package main
   409  
   410  import (
   411  	"fmt"
   412  	"os"
   413  	"time"
   414  )
   415  
   416  func main() {
   417  	fmt.Fprintln(os.Stdout, "A")
   418  	fmt.Fprintln(os.Stderr, "B")
   419  	fmt.Fprintln(os.Stdout, "A")
   420  	fmt.Fprintln(os.Stdout, "A")
   421  	time.Sleep(time.Second)
   422  	fmt.Fprintln(os.Stderr, "B")
   423  	time.Sleep(time.Second)
   424  	fmt.Fprintln(os.Stdout, "A")
   425  }
   426  `, wantEvents: []Event{
   427  			{"A\n", "stdout", 0},
   428  			{"B\n", "stderr", time.Nanosecond},
   429  			{"A\nA\n", "stdout", time.Nanosecond},
   430  			{"B\n", "stderr", time.Second - 2*time.Nanosecond},
   431  			{"A\n", "stdout", time.Second},
   432  		}},
   433  
   434  	{
   435  		name: "third_party_imports",
   436  		prog: `
   437  package main
   438  import ("fmt"; "github.com/bradfitz/iter")
   439  func main() { for i := range iter.N(5) { fmt.Println(i) } }
   440  `,
   441  		want: "0\n1\n2\n3\n4\n",
   442  	},
   443  
   444  	{
   445  		name:          "compile_with_vet",
   446  		withVet:       true,
   447  		wantVetErrors: "./prog.go:5:2: fmt.Printf format %v reads arg #1, but call has 0 args\n",
   448  		prog: `
   449  package main
   450  import "fmt"
   451  func main() {
   452  	fmt.Printf("hi %v")
   453  }
   454  `,
   455  	},
   456  
   457  	{
   458  		name:    "compile_without_vet",
   459  		withVet: false,
   460  		prog: `
   461  package main
   462  import "fmt"
   463  func main() {
   464  	fmt.Printf("hi %v")
   465  }
   466  `,
   467  	},
   468  
   469  	{
   470  		name:          "compile_modules_with_vet",
   471  		withVet:       true,
   472  		wantVetErrors: "./prog.go:6:2: fmt.Printf format %v reads arg #1, but call has 0 args\n",
   473  		prog: `
   474  package main
   475  import ("fmt"; "github.com/bradfitz/iter")
   476  func main() {
   477  	for i := range iter.N(5) { fmt.Println(i) }
   478  	fmt.Printf("hi %v")
   479  }
   480  `,
   481  	},
   482  
   483  	{
   484  		name: "multi_file_basic",
   485  		prog: `
   486  package main
   487  const foo = "bar"
   488  
   489  -- two.go --
   490  package main
   491  func main() {
   492    println(foo)
   493  }
   494  `,
   495  		wantEvents: []Event{
   496  			{"bar\n", "stderr", 0},
   497  		},
   498  	},
   499  
   500  	{
   501  		name:    "multi_file_use_package",
   502  		withVet: true,
   503  		prog: `
   504  package main
   505  
   506  import "play.test/foo"
   507  
   508  func main() {
   509      foo.Hello()
   510  }
   511  
   512  -- go.mod --
   513  module play.test
   514  
   515  -- foo/foo.go --
   516  package foo
   517  
   518  import "fmt"
   519  
   520  func Hello() { fmt.Println("hello world") }
   521  `,
   522  	},
   523  	{
   524  		name: "timeouts_handled_gracefully",
   525  		prog: `
   526  package main
   527  
   528  import (
   529  	"time"
   530  )
   531  
   532  func main() {
   533  	c := make(chan struct{})
   534  
   535  	go func() {
   536  		defer close(c)
   537  		for {
   538  			time.Sleep(10 * time.Millisecond)
   539  		}
   540  	}()
   541  
   542  	<-c
   543  }
   544  `, want: "timeout running program"},
   545  	{
   546  		name: "timezone_info_exists",
   547  		prog: `
   548  package main
   549  
   550  import (
   551  	"fmt"
   552  	"time"
   553  )
   554  
   555  func main() {
   556  	loc, _ := time.LoadLocation("Europe/Berlin")
   557  
   558  	// This will look for the name CEST in the Europe/Berlin time zone.
   559  	const longForm = "Jan 2, 2006 at 3:04pm (MST)"
   560  	t, _ := time.ParseInLocation(longForm, "Jul 9, 2012 at 5:02am (CEST)", loc)
   561  	fmt.Println(t)
   562  
   563  	// Note: without explicit zone, returns time in given location.
   564  	const shortForm = "2006-Jan-02"
   565  	t, _ = time.ParseInLocation(shortForm, "2012-Jul-09", loc)
   566  	fmt.Println(t)
   567  
   568  }
   569  `, want: "2012-07-09 05:02:00 +0200 CEST\n2012-07-09 00:00:00 +0200 CEST\n"},
   570  	{
   571  		name: "cgo_enabled_0",
   572  		prog: `
   573  package main
   574  
   575  import (
   576  	"fmt"
   577  	"net"
   578  )
   579  
   580  func main() {
   581  	fmt.Println(net.ParseIP("1.2.3.4"))
   582  }
   583  `, want: "1.2.3.4\n"},
   584  	{
   585  		name: "fuzz_executed",
   586  		prog: `
   587  package main
   588  
   589  import "testing"
   590  
   591  func FuzzSanity(f *testing.F) {
   592  	f.Add("a")
   593  	f.Fuzz(func(t *testing.T, v string) {
   594  	})
   595  }
   596  `, want: `=== RUN   FuzzSanity
   597  === RUN   FuzzSanity/seed#0
   598  --- PASS: FuzzSanity (0.00s)
   599      --- PASS: FuzzSanity/seed#0 (0.00s)
   600  PASS`},
   601  	{
   602  		name: "test_main",
   603  		prog: `
   604  package main
   605  
   606  import (
   607  	"os"
   608  	"testing"
   609  )
   610  
   611  func TestMain(m *testing.M) {
   612  	os.Exit(m.Run())
   613  }`, want: `testing: warning: no tests to run
   614  PASS`,
   615  	},
   616  	{
   617  		name: "multiple_files_no_banner",
   618  		prog: `
   619  package main
   620  
   621  func main() {
   622  	print()
   623  }
   624  
   625  -- foo.go --
   626  package main
   627  
   628  import "fmt"
   629  
   630  func print() {
   631  	=fmt.Println("Hello, playground")
   632  }
   633  `, errors: `./foo.go:6:2: syntax error: unexpected =, expecting }
   634  `,
   635  	},
   636  }