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)")