summaryrefslogtreecommitdiffstats
path: root/src/go/build/constraint/expr_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/go/build/constraint/expr_test.go')
-rw-r--r--src/go/build/constraint/expr_test.go317
1 files changed, 317 insertions, 0 deletions
diff --git a/src/go/build/constraint/expr_test.go b/src/go/build/constraint/expr_test.go
new file mode 100644
index 0000000..4979f8b
--- /dev/null
+++ b/src/go/build/constraint/expr_test.go
@@ -0,0 +1,317 @@
+// Copyright 2020 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 constraint
+
+import (
+ "fmt"
+ "reflect"
+ "strings"
+ "testing"
+)
+
+var exprStringTests = []struct {
+ x Expr
+ out string
+}{
+ {
+ x: tag("abc"),
+ out: "abc",
+ },
+ {
+ x: not(tag("abc")),
+ out: "!abc",
+ },
+ {
+ x: not(and(tag("abc"), tag("def"))),
+ out: "!(abc && def)",
+ },
+ {
+ x: and(tag("abc"), or(tag("def"), tag("ghi"))),
+ out: "abc && (def || ghi)",
+ },
+ {
+ x: or(and(tag("abc"), tag("def")), tag("ghi")),
+ out: "(abc && def) || ghi",
+ },
+}
+
+func TestExprString(t *testing.T) {
+ for i, tt := range exprStringTests {
+ t.Run(fmt.Sprint(i), func(t *testing.T) {
+ s := tt.x.String()
+ if s != tt.out {
+ t.Errorf("String() mismatch:\nhave %s\nwant %s", s, tt.out)
+ }
+ })
+ }
+}
+
+var lexTests = []struct {
+ in string
+ out string
+}{
+ {"", ""},
+ {"x", "x"},
+ {"x.y", "x.y"},
+ {"x_y", "x_y"},
+ {"αx", "αx"},
+ {"αx²", "αx err: invalid syntax at ²"},
+ {"go1.2", "go1.2"},
+ {"x y", "x y"},
+ {"x!y", "x ! y"},
+ {"&&||!()xy yx ", "&& || ! ( ) xy yx"},
+ {"x~", "x err: invalid syntax at ~"},
+ {"x ~", "x err: invalid syntax at ~"},
+ {"x &", "x err: invalid syntax at &"},
+ {"x &y", "x err: invalid syntax at &"},
+}
+
+func TestLex(t *testing.T) {
+ for i, tt := range lexTests {
+ t.Run(fmt.Sprint(i), func(t *testing.T) {
+ p := &exprParser{s: tt.in}
+ out := ""
+ for {
+ tok, err := lexHelp(p)
+ if tok == "" && err == nil {
+ break
+ }
+ if out != "" {
+ out += " "
+ }
+ if err != nil {
+ out += "err: " + err.Error()
+ break
+ }
+ out += tok
+ }
+ if out != tt.out {
+ t.Errorf("lex(%q):\nhave %s\nwant %s", tt.in, out, tt.out)
+ }
+ })
+ }
+}
+
+func lexHelp(p *exprParser) (tok string, err error) {
+ defer func() {
+ if e := recover(); e != nil {
+ if e, ok := e.(*SyntaxError); ok {
+ err = e
+ return
+ }
+ panic(e)
+ }
+ }()
+
+ p.lex()
+ return p.tok, nil
+}
+
+var parseExprTests = []struct {
+ in string
+ x Expr
+}{
+ {"x", tag("x")},
+ {"x&&y", and(tag("x"), tag("y"))},
+ {"x||y", or(tag("x"), tag("y"))},
+ {"(x)", tag("x")},
+ {"x||y&&z", or(tag("x"), and(tag("y"), tag("z")))},
+ {"x&&y||z", or(and(tag("x"), tag("y")), tag("z"))},
+ {"x&&(y||z)", and(tag("x"), or(tag("y"), tag("z")))},
+ {"(x||y)&&z", and(or(tag("x"), tag("y")), tag("z"))},
+ {"!(x&&y)", not(and(tag("x"), tag("y")))},
+}
+
+func TestParseExpr(t *testing.T) {
+ for i, tt := range parseExprTests {
+ t.Run(fmt.Sprint(i), func(t *testing.T) {
+ x, err := parseExpr(tt.in)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if x.String() != tt.x.String() {
+ t.Errorf("parseExpr(%q):\nhave %s\nwant %s", tt.in, x, tt.x)
+ }
+ })
+ }
+}
+
+var parseExprErrorTests = []struct {
+ in string
+ err error
+}{
+ {"x && ", &SyntaxError{Offset: 5, Err: "unexpected end of expression"}},
+ {"x && (", &SyntaxError{Offset: 6, Err: "missing close paren"}},
+ {"x && ||", &SyntaxError{Offset: 5, Err: "unexpected token ||"}},
+ {"x && !", &SyntaxError{Offset: 6, Err: "unexpected end of expression"}},
+ {"x && !!", &SyntaxError{Offset: 6, Err: "double negation not allowed"}},
+ {"x !", &SyntaxError{Offset: 2, Err: "unexpected token !"}},
+ {"x && (y", &SyntaxError{Offset: 5, Err: "missing close paren"}},
+}
+
+func TestParseError(t *testing.T) {
+ for i, tt := range parseExprErrorTests {
+ t.Run(fmt.Sprint(i), func(t *testing.T) {
+ x, err := parseExpr(tt.in)
+ if err == nil {
+ t.Fatalf("parseExpr(%q) = %v, want error", tt.in, x)
+ }
+ if !reflect.DeepEqual(err, tt.err) {
+ t.Fatalf("parseExpr(%q): wrong error:\nhave %#v\nwant %#v", tt.in, err, tt.err)
+ }
+ })
+ }
+}
+
+var exprEvalTests = []struct {
+ in string
+ ok bool
+ tags string
+}{
+ {"x", false, "x"},
+ {"x && y", false, "x y"},
+ {"x || y", false, "x y"},
+ {"!x && yes", true, "x yes"},
+ {"yes || y", true, "y yes"},
+}
+
+func TestExprEval(t *testing.T) {
+ for i, tt := range exprEvalTests {
+ t.Run(fmt.Sprint(i), func(t *testing.T) {
+ x, err := parseExpr(tt.in)
+ if err != nil {
+ t.Fatal(err)
+ }
+ tags := make(map[string]bool)
+ wantTags := make(map[string]bool)
+ for _, tag := range strings.Fields(tt.tags) {
+ wantTags[tag] = true
+ }
+ hasTag := func(tag string) bool {
+ tags[tag] = true
+ return tag == "yes"
+ }
+ ok := x.Eval(hasTag)
+ if ok != tt.ok || !reflect.DeepEqual(tags, wantTags) {
+ t.Errorf("Eval(%#q):\nhave ok=%v, tags=%v\nwant ok=%v, tags=%v",
+ tt.in, ok, tags, tt.ok, wantTags)
+ }
+ })
+ }
+}
+
+var parsePlusBuildExprTests = []struct {
+ in string
+ x Expr
+}{
+ {"x", tag("x")},
+ {"x,y", and(tag("x"), tag("y"))},
+ {"x y", or(tag("x"), tag("y"))},
+ {"x y,z", or(tag("x"), and(tag("y"), tag("z")))},
+ {"x,y z", or(and(tag("x"), tag("y")), tag("z"))},
+ {"x,!y !z", or(and(tag("x"), not(tag("y"))), not(tag("z")))},
+ {"!! x", or(tag("ignore"), tag("x"))},
+ {"!!x", tag("ignore")},
+ {"!x", not(tag("x"))},
+ {"!", tag("ignore")},
+}
+
+func TestParsePlusBuildExpr(t *testing.T) {
+ for i, tt := range parsePlusBuildExprTests {
+ t.Run(fmt.Sprint(i), func(t *testing.T) {
+ x := parsePlusBuildExpr(tt.in)
+ if x.String() != tt.x.String() {
+ t.Errorf("parsePlusBuildExpr(%q):\nhave %v\nwant %v", tt.in, x, tt.x)
+ }
+ })
+ }
+}
+
+var constraintTests = []struct {
+ in string
+ x Expr
+ err error
+}{
+ {"//+build x y", or(tag("x"), tag("y")), nil},
+ {"// +build x y \n", or(tag("x"), tag("y")), nil},
+ {"// +build x y \n ", nil, errNotConstraint},
+ {"// +build x y \nmore", nil, errNotConstraint},
+ {" //+build x y", nil, errNotConstraint},
+
+ {"//go:build x && y", and(tag("x"), tag("y")), nil},
+ {"//go:build x && y\n", and(tag("x"), tag("y")), nil},
+ {"//go:build x && y\n ", nil, errNotConstraint},
+ {"//go:build x && y\nmore", nil, errNotConstraint},
+ {" //go:build x && y", nil, errNotConstraint},
+}
+
+func TestParse(t *testing.T) {
+ for i, tt := range constraintTests {
+ t.Run(fmt.Sprint(i), func(t *testing.T) {
+ x, err := Parse(tt.in)
+ if err != nil {
+ if tt.err == nil {
+ t.Errorf("Constraint(%q): unexpected error: %v", tt.in, err)
+ } else if tt.err != err {
+ t.Errorf("Constraint(%q): error %v, want %v", tt.in, err, tt.err)
+ }
+ return
+ }
+ if tt.err != nil {
+ t.Errorf("Constraint(%q) = %v, want error %v", tt.in, x, tt.err)
+ return
+ }
+ if x.String() != tt.x.String() {
+ t.Errorf("Constraint(%q):\nhave %v\nwant %v", tt.in, x, tt.x)
+ }
+ })
+ }
+}
+
+var plusBuildLinesTests = []struct {
+ in string
+ out []string
+ err error
+}{
+ {"x", []string{"x"}, nil},
+ {"x && !y", []string{"x,!y"}, nil},
+ {"x || y", []string{"x y"}, nil},
+ {"x && (y || z)", []string{"x", "y z"}, nil},
+ {"!(x && y)", []string{"!x !y"}, nil},
+ {"x || (y && z)", []string{"x y,z"}, nil},
+ {"w && (x || (y && z))", []string{"w", "x y,z"}, nil},
+ {"v || (w && (x || (y && z)))", nil, errComplex},
+}
+
+func TestPlusBuildLines(t *testing.T) {
+ for i, tt := range plusBuildLinesTests {
+ t.Run(fmt.Sprint(i), func(t *testing.T) {
+ x, err := parseExpr(tt.in)
+ if err != nil {
+ t.Fatal(err)
+ }
+ lines, err := PlusBuildLines(x)
+ if err != nil {
+ if tt.err == nil {
+ t.Errorf("PlusBuildLines(%q): unexpected error: %v", tt.in, err)
+ } else if tt.err != err {
+ t.Errorf("PlusBuildLines(%q): error %v, want %v", tt.in, err, tt.err)
+ }
+ return
+ }
+ if tt.err != nil {
+ t.Errorf("PlusBuildLines(%q) = %v, want error %v", tt.in, lines, tt.err)
+ return
+ }
+ var want []string
+ for _, line := range tt.out {
+ want = append(want, "// +build "+line)
+ }
+ if !reflect.DeepEqual(lines, want) {
+ t.Errorf("PlusBuildLines(%q):\nhave %q\nwant %q", tt.in, lines, want)
+ }
+ })
+ }
+}