golang.org/x/exp@v0.0.0-20240506185415-9bf2ced13842/constraints/constraints_test.go (about)

     1  // Copyright 2021 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  package constraints
     6  
     7  import (
     8  	"fmt"
     9  	"os"
    10  	"os/exec"
    11  	"path/filepath"
    12  	"regexp"
    13  	"runtime"
    14  	"testing"
    15  )
    16  
    17  type (
    18  	testSigned[T Signed]     struct{ f T }
    19  	testUnsigned[T Unsigned] struct{ f T }
    20  	testInteger[T Integer]   struct{ f T }
    21  	testFloat[T Float]       struct{ f T }
    22  	testComplex[T Complex]   struct{ f T }
    23  	testOrdered[T Ordered]   struct{ f T }
    24  )
    25  
    26  // TestTypes passes if it compiles.
    27  type TestTypes struct {
    28  	_ testSigned[int]
    29  	_ testSigned[int64]
    30  	_ testUnsigned[uint]
    31  	_ testUnsigned[uintptr]
    32  	_ testInteger[int8]
    33  	_ testInteger[uint8]
    34  	_ testInteger[uintptr]
    35  	_ testFloat[float32]
    36  	_ testComplex[complex64]
    37  	_ testOrdered[int]
    38  	_ testOrdered[float64]
    39  	_ testOrdered[string]
    40  }
    41  
    42  var prolog = []byte(`
    43  package constrainttest
    44  
    45  import "golang.org/x/exp/constraints"
    46  
    47  type (
    48  	testSigned[T constraints.Signed]     struct{ f T }
    49  	testUnsigned[T constraints.Unsigned] struct{ f T }
    50  	testInteger[T constraints.Integer]   struct{ f T }
    51  	testFloat[T constraints.Float]       struct{ f T }
    52  	testComplex[T constraints.Complex]   struct{ f T }
    53  	testOrdered[T constraints.Ordered]   struct{ f T }
    54  )
    55  `)
    56  
    57  func TestFailure(t *testing.T) {
    58  	switch runtime.GOOS {
    59  	case "android", "js", "ios":
    60  		t.Skipf("can't run go tool on %s", runtime.GOOS)
    61  	}
    62  
    63  	var exeSuffix string
    64  	if runtime.GOOS == "windows" {
    65  		exeSuffix = ".exe"
    66  	}
    67  	gocmd := filepath.Join(runtime.GOROOT(), "bin", "go"+exeSuffix)
    68  	if _, err := os.Stat(gocmd); err != nil {
    69  		t.Skipf("skipping because can't stat %s: %v", gocmd, err)
    70  	}
    71  
    72  	tmpdir := t.TempDir()
    73  
    74  	cwd, err := os.Getwd()
    75  	if err != nil {
    76  		t.Fatal(err)
    77  	}
    78  	// This package is golang.org/x/exp/constraints, so the root of the x/exp
    79  	// module is the parent directory of the directory in which this test runs.
    80  	expModDir := filepath.Dir(cwd)
    81  
    82  	modFile := fmt.Sprintf(`module constraintest
    83  
    84  go 1.18
    85  
    86  replace golang.org/x/exp => %s
    87  `, expModDir)
    88  	if err := os.WriteFile(filepath.Join(tmpdir, "go.mod"), []byte(modFile), 0666); err != nil {
    89  		t.Fatal(err)
    90  	}
    91  
    92  	// Write the prolog as its own file so that 'go mod tidy' has something to inspect.
    93  	// This will ensure that the go.mod and go.sum files include any dependencies
    94  	// needed by the constraints package (which should just be some version of
    95  	// x/exp itself).
    96  	if err := os.WriteFile(filepath.Join(tmpdir, "prolog.go"), []byte(prolog), 0666); err != nil {
    97  		t.Fatal(err)
    98  	}
    99  
   100  	tidyCmd := exec.Command(gocmd, "mod", "tidy")
   101  	tidyCmd.Dir = tmpdir
   102  	tidyCmd.Env = append(os.Environ(), "PWD="+tmpdir)
   103  	if out, err := tidyCmd.CombinedOutput(); err != nil {
   104  		t.Fatalf("%v: %v\n%s", tidyCmd, err, out)
   105  	} else {
   106  		t.Logf("%v:\n%s", tidyCmd, out)
   107  	}
   108  
   109  	// Test for types that should not satisfy a constraint.
   110  	// For each pair of constraint and type, write a Go file
   111  	//     var V constraint[type]
   112  	// For example,
   113  	//     var V testSigned[uint]
   114  	// This should not compile, as testSigned (above) uses
   115  	// constraints.Signed, and uint does not satisfy that constraint.
   116  	// Therefore, the build of that code should fail.
   117  	for i, test := range []struct {
   118  		constraint, typ string
   119  	}{
   120  		{"testSigned", "uint"},
   121  		{"testUnsigned", "int"},
   122  		{"testInteger", "float32"},
   123  		{"testFloat", "int8"},
   124  		{"testComplex", "float64"},
   125  		{"testOrdered", "bool"},
   126  	} {
   127  		i := i
   128  		test := test
   129  		t.Run(fmt.Sprintf("%s %d", test.constraint, i), func(t *testing.T) {
   130  			t.Parallel()
   131  			name := fmt.Sprintf("go%d.go", i)
   132  			f, err := os.Create(filepath.Join(tmpdir, name))
   133  			if err != nil {
   134  				t.Fatal(err)
   135  			}
   136  			if _, err := f.Write(prolog); err != nil {
   137  				t.Fatal(err)
   138  			}
   139  			if _, err := fmt.Fprintf(f, "var V %s[%s]\n", test.constraint, test.typ); err != nil {
   140  				t.Fatal(err)
   141  			}
   142  			if err := f.Close(); err != nil {
   143  				t.Fatal(err)
   144  			}
   145  			cmd := exec.Command(gocmd, "build", name)
   146  			cmd.Dir = tmpdir
   147  			if out, err := cmd.CombinedOutput(); err == nil {
   148  				t.Error("build succeeded, but expected to fail")
   149  			} else if len(out) > 0 {
   150  				t.Logf("%s", out)
   151  				if !wantRx.Match(out) {
   152  					t.Errorf("output does not match %q", wantRx)
   153  				}
   154  			} else {
   155  				t.Error("no error output, expected something")
   156  			}
   157  		})
   158  	}
   159  }
   160  
   161  var wantRx = regexp.MustCompile("does not (implement|satisfy)")