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