Source file src/go/build/constraint/expr_test.go

     1  // Copyright 2020 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 constraint
     6  
     7  import (
     8  	"fmt"
     9  	"reflect"
    10  	"strings"
    11  	"testing"
    12  )
    13  
    14  var exprStringTests = []struct {
    15  	x   Expr
    16  	out string
    17  }{
    18  	{
    19  		x:   tag("abc"),
    20  		out: "abc",
    21  	},
    22  	{
    23  		x:   not(tag("abc")),
    24  		out: "!abc",
    25  	},
    26  	{
    27  		x:   not(and(tag("abc"), tag("def"))),
    28  		out: "!(abc && def)",
    29  	},
    30  	{
    31  		x:   and(tag("abc"), or(tag("def"), tag("ghi"))),
    32  		out: "abc && (def || ghi)",
    33  	},
    34  	{
    35  		x:   or(and(tag("abc"), tag("def")), tag("ghi")),
    36  		out: "(abc && def) || ghi",
    37  	},
    38  }
    39  
    40  func TestExprString(t *testing.T) {
    41  	for i, tt := range exprStringTests {
    42  		t.Run(fmt.Sprint(i), func(t *testing.T) {
    43  			s := tt.x.String()
    44  			if s != tt.out {
    45  				t.Errorf("String() mismatch:\nhave %s\nwant %s", s, tt.out)
    46  			}
    47  		})
    48  	}
    49  }
    50  
    51  var lexTests = []struct {
    52  	in  string
    53  	out string
    54  }{
    55  	{"", ""},
    56  	{"x", "x"},
    57  	{"x.y", "x.y"},
    58  	{"x_y", "x_y"},
    59  	{"αx", "αx"},
    60  	{"αx²", "αx err: invalid syntax at ²"},
    61  	{"go1.2", "go1.2"},
    62  	{"x y", "x y"},
    63  	{"x!y", "x ! y"},
    64  	{"&&||!()xy yx ", "&& || ! ( ) xy yx"},
    65  	{"x~", "x err: invalid syntax at ~"},
    66  	{"x ~", "x err: invalid syntax at ~"},
    67  	{"x &", "x err: invalid syntax at &"},
    68  	{"x &y", "x err: invalid syntax at &"},
    69  }
    70  
    71  func TestLex(t *testing.T) {
    72  	for i, tt := range lexTests {
    73  		t.Run(fmt.Sprint(i), func(t *testing.T) {
    74  			p := &exprParser{s: tt.in}
    75  			out := ""
    76  			for {
    77  				tok, err := lexHelp(p)
    78  				if tok == "" && err == nil {
    79  					break
    80  				}
    81  				if out != "" {
    82  					out += " "
    83  				}
    84  				if err != nil {
    85  					out += "err: " + err.Error()
    86  					break
    87  				}
    88  				out += tok
    89  			}
    90  			if out != tt.out {
    91  				t.Errorf("lex(%q):\nhave %s\nwant %s", tt.in, out, tt.out)
    92  			}
    93  		})
    94  	}
    95  }
    96  
    97  func lexHelp(p *exprParser) (tok string, err error) {
    98  	defer func() {
    99  		if e := recover(); e != nil {
   100  			if e, ok := e.(*SyntaxError); ok {
   101  				err = e
   102  				return
   103  			}
   104  			panic(e)
   105  		}
   106  	}()
   107  
   108  	p.lex()
   109  	return p.tok, nil
   110  }
   111  
   112  var parseExprTests = []struct {
   113  	in string
   114  	x  Expr
   115  }{
   116  	{"x", tag("x")},
   117  	{"x&&y", and(tag("x"), tag("y"))},
   118  	{"x||y", or(tag("x"), tag("y"))},
   119  	{"(x)", tag("x")},
   120  	{"x||y&&z", or(tag("x"), and(tag("y"), tag("z")))},
   121  	{"x&&y||z", or(and(tag("x"), tag("y")), tag("z"))},
   122  	{"x&&(y||z)", and(tag("x"), or(tag("y"), tag("z")))},
   123  	{"(x||y)&&z", and(or(tag("x"), tag("y")), tag("z"))},
   124  	{"!(x&&y)", not(and(tag("x"), tag("y")))},
   125  }
   126  
   127  func TestParseExpr(t *testing.T) {
   128  	for i, tt := range parseExprTests {
   129  		t.Run(fmt.Sprint(i), func(t *testing.T) {
   130  			x, err := parseExpr(tt.in)
   131  			if err != nil {
   132  				t.Fatal(err)
   133  			}
   134  			if x.String() != tt.x.String() {
   135  				t.Errorf("parseExpr(%q):\nhave %s\nwant %s", tt.in, x, tt.x)
   136  			}
   137  		})
   138  	}
   139  }
   140  
   141  var parseExprErrorTests = []struct {
   142  	in  string
   143  	err error
   144  }{
   145  	{"x && ", &SyntaxError{Offset: 5, Err: "unexpected end of expression"}},
   146  	{"x && (", &SyntaxError{Offset: 6, Err: "missing close paren"}},
   147  	{"x && ||", &SyntaxError{Offset: 5, Err: "unexpected token ||"}},
   148  	{"x && !", &SyntaxError{Offset: 6, Err: "unexpected end of expression"}},
   149  	{"x && !!", &SyntaxError{Offset: 6, Err: "double negation not allowed"}},
   150  	{"x !", &SyntaxError{Offset: 2, Err: "unexpected token !"}},
   151  	{"x && (y", &SyntaxError{Offset: 5, Err: "missing close paren"}},
   152  }
   153  
   154  func TestParseError(t *testing.T) {
   155  	for i, tt := range parseExprErrorTests {
   156  		t.Run(fmt.Sprint(i), func(t *testing.T) {
   157  			x, err := parseExpr(tt.in)
   158  			if err == nil {
   159  				t.Fatalf("parseExpr(%q) = %v, want error", tt.in, x)
   160  			}
   161  			if !reflect.DeepEqual(err, tt.err) {
   162  				t.Fatalf("parseExpr(%q): wrong error:\nhave %#v\nwant %#v", tt.in, err, tt.err)
   163  			}
   164  		})
   165  	}
   166  }
   167  
   168  var exprEvalTests = []struct {
   169  	in   string
   170  	ok   bool
   171  	tags string
   172  }{
   173  	{"x", false, "x"},
   174  	{"x && y", false, "x y"},
   175  	{"x || y", false, "x y"},
   176  	{"!x && yes", true, "x yes"},
   177  	{"yes || y", true, "y yes"},
   178  }
   179  
   180  func TestExprEval(t *testing.T) {
   181  	for i, tt := range exprEvalTests {
   182  		t.Run(fmt.Sprint(i), func(t *testing.T) {
   183  			x, err := parseExpr(tt.in)
   184  			if err != nil {
   185  				t.Fatal(err)
   186  			}
   187  			tags := make(map[string]bool)
   188  			wantTags := make(map[string]bool)
   189  			for _, tag := range strings.Fields(tt.tags) {
   190  				wantTags[tag] = true
   191  			}
   192  			hasTag := func(tag string) bool {
   193  				tags[tag] = true
   194  				return tag == "yes"
   195  			}
   196  			ok := x.Eval(hasTag)
   197  			if ok != tt.ok || !reflect.DeepEqual(tags, wantTags) {
   198  				t.Errorf("Eval(%#q):\nhave ok=%v, tags=%v\nwant ok=%v, tags=%v",
   199  					tt.in, ok, tags, tt.ok, wantTags)
   200  			}
   201  		})
   202  	}
   203  }
   204  
   205  var parsePlusBuildExprTests = []struct {
   206  	in string
   207  	x  Expr
   208  }{
   209  	{"x", tag("x")},
   210  	{"x,y", and(tag("x"), tag("y"))},
   211  	{"x y", or(tag("x"), tag("y"))},
   212  	{"x y,z", or(tag("x"), and(tag("y"), tag("z")))},
   213  	{"x,y z", or(and(tag("x"), tag("y")), tag("z"))},
   214  	{"x,!y !z", or(and(tag("x"), not(tag("y"))), not(tag("z")))},
   215  	{"!! x", or(tag("ignore"), tag("x"))},
   216  	{"!!x", tag("ignore")},
   217  	{"!x", not(tag("x"))},
   218  	{"!", tag("ignore")},
   219  	{"", tag("ignore")},
   220  }
   221  
   222  func TestParsePlusBuildExpr(t *testing.T) {
   223  	for i, tt := range parsePlusBuildExprTests {
   224  		t.Run(fmt.Sprint(i), func(t *testing.T) {
   225  			x := parsePlusBuildExpr(tt.in)
   226  			if x.String() != tt.x.String() {
   227  				t.Errorf("parsePlusBuildExpr(%q):\nhave %v\nwant %v", tt.in, x, tt.x)
   228  			}
   229  		})
   230  	}
   231  }
   232  
   233  var constraintTests = []struct {
   234  	in  string
   235  	x   Expr
   236  	err string
   237  }{
   238  	{"//+build !", tag("ignore"), ""},
   239  	{"//+build", tag("ignore"), ""},
   240  	{"//+build x y", or(tag("x"), tag("y")), ""},
   241  	{"// +build x y \n", or(tag("x"), tag("y")), ""},
   242  	{"// +build x y \n ", nil, "not a build constraint"},
   243  	{"// +build x y \nmore", nil, "not a build constraint"},
   244  	{" //+build x y", nil, "not a build constraint"},
   245  
   246  	{"//go:build x && y", and(tag("x"), tag("y")), ""},
   247  	{"//go:build x && y\n", and(tag("x"), tag("y")), ""},
   248  	{"//go:build x && y\n ", nil, "not a build constraint"},
   249  	{"//go:build x && y\nmore", nil, "not a build constraint"},
   250  	{" //go:build x && y", nil, "not a build constraint"},
   251  	{"//go:build\n", nil, "unexpected end of expression"},
   252  }
   253  
   254  func TestParse(t *testing.T) {
   255  	for i, tt := range constraintTests {
   256  		t.Run(fmt.Sprint(i), func(t *testing.T) {
   257  			x, err := Parse(tt.in)
   258  			if err != nil {
   259  				if tt.err == "" {
   260  					t.Errorf("Constraint(%q): unexpected error: %v", tt.in, err)
   261  				} else if !strings.Contains(err.Error(), tt.err) {
   262  					t.Errorf("Constraint(%q): error %v, want %v", tt.in, err, tt.err)
   263  				}
   264  				return
   265  			}
   266  			if tt.err != "" {
   267  				t.Errorf("Constraint(%q) = %v, want error %v", tt.in, x, tt.err)
   268  				return
   269  			}
   270  			if x.String() != tt.x.String() {
   271  				t.Errorf("Constraint(%q):\nhave %v\nwant %v", tt.in, x, tt.x)
   272  			}
   273  		})
   274  	}
   275  }
   276  
   277  var plusBuildLinesTests = []struct {
   278  	in  string
   279  	out []string
   280  	err error
   281  }{
   282  	{"x", []string{"x"}, nil},
   283  	{"x && !y", []string{"x,!y"}, nil},
   284  	{"x || y", []string{"x y"}, nil},
   285  	{"x && (y || z)", []string{"x", "y z"}, nil},
   286  	{"!(x && y)", []string{"!x !y"}, nil},
   287  	{"x || (y && z)", []string{"x y,z"}, nil},
   288  	{"w && (x || (y && z))", []string{"w", "x y,z"}, nil},
   289  	{"v || (w && (x || (y && z)))", nil, errComplex},
   290  }
   291  
   292  func TestPlusBuildLines(t *testing.T) {
   293  	for i, tt := range plusBuildLinesTests {
   294  		t.Run(fmt.Sprint(i), func(t *testing.T) {
   295  			x, err := parseExpr(tt.in)
   296  			if err != nil {
   297  				t.Fatal(err)
   298  			}
   299  			lines, err := PlusBuildLines(x)
   300  			if err != nil {
   301  				if tt.err == nil {
   302  					t.Errorf("PlusBuildLines(%q): unexpected error: %v", tt.in, err)
   303  				} else if tt.err != err {
   304  					t.Errorf("PlusBuildLines(%q): error %v, want %v", tt.in, err, tt.err)
   305  				}
   306  				return
   307  			}
   308  			if tt.err != nil {
   309  				t.Errorf("PlusBuildLines(%q) = %v, want error %v", tt.in, lines, tt.err)
   310  				return
   311  			}
   312  			var want []string
   313  			for _, line := range tt.out {
   314  				want = append(want, "// +build "+line)
   315  			}
   316  			if !reflect.DeepEqual(lines, want) {
   317  				t.Errorf("PlusBuildLines(%q):\nhave %q\nwant %q", tt.in, lines, want)
   318  			}
   319  		})
   320  	}
   321  }
   322  

View as plain text