summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--test/run.go87
-rw-r--r--test/testlib51
2 files changed, 105 insertions, 33 deletions
diff --git a/test/run.go b/test/run.go
index 5e167d6b0..353553240 100644
--- a/test/run.go
+++ b/test/run.go
@@ -27,6 +27,7 @@ import (
"sort"
"strconv"
"strings"
+ "unicode"
)
var (
@@ -299,14 +300,17 @@ func goDirPackages(longdir string) ([][]string, error) {
return pkgs, nil
}
+type context struct {
+ GOOS string
+ GOARCH string
+}
+
// shouldTest looks for build tags in a source file and returns
// whether the file should be used according to the tags.
func shouldTest(src string, goos, goarch string) (ok bool, whyNot string) {
if idx := strings.Index(src, "\npackage"); idx >= 0 {
src = src[:idx]
}
- notgoos := "!" + goos
- notgoarch := "!" + goarch
for _, line := range strings.Split(src, "\n") {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "//") {
@@ -318,29 +322,59 @@ func shouldTest(src string, goos, goarch string) (ok bool, whyNot string) {
if len(line) == 0 || line[0] != '+' {
continue
}
+ ctxt := &context{
+ GOOS: goos,
+ GOARCH: goarch,
+ }
words := strings.Fields(line)
if words[0] == "+build" {
- for _, word := range words {
- switch word {
- case goos, goarch:
- return true, ""
- case notgoos, notgoarch:
- continue
- default:
- if word[0] == '!' {
- // NOT something-else
- return true, ""
- }
+ ok := false
+ for _, word := range words[1:] {
+ if ctxt.match(word) {
+ ok = true
+ break
}
}
- // no matching tag found.
- return false, line
+ if !ok {
+ // no matching tag found.
+ return false, line
+ }
}
}
- // no build tags.
+ // no build tags
return true, ""
}
+func (ctxt *context) match(name string) bool {
+ if name == "" {
+ return false
+ }
+ if i := strings.Index(name, ","); i >= 0 {
+ // comma-separated list
+ return ctxt.match(name[:i]) && ctxt.match(name[i+1:])
+ }
+ if strings.HasPrefix(name, "!!") { // bad syntax, reject always
+ return false
+ }
+ if strings.HasPrefix(name, "!") { // negation
+ return len(name) > 1 && !ctxt.match(name[1:])
+ }
+
+ // Tags must be letters, digits, underscores or dots.
+ // Unlike in Go identifiers, all digits are fine (e.g., "386").
+ for _, c := range name {
+ if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' {
+ return false
+ }
+ }
+
+ if name == ctxt.GOOS || name == ctxt.GOARCH {
+ return true
+ }
+
+ return false
+}
+
func init() { checkShouldTest() }
// run runs a test.
@@ -815,7 +849,7 @@ func defaultRunOutputLimit() int {
return cpu
}
-// checkShouldTest runs canity checks on the shouldTest function.
+// checkShouldTest runs sanity checks on the shouldTest function.
func checkShouldTest() {
assert := func(ok bool, _ string) {
if !ok {
@@ -823,11 +857,28 @@ func checkShouldTest() {
}
}
assertNot := func(ok bool, _ string) { assert(!ok, "") }
+
+ // Simple tests.
assert(shouldTest("// +build linux", "linux", "arm"))
assert(shouldTest("// +build !windows", "linux", "arm"))
assertNot(shouldTest("// +build !windows", "windows", "amd64"))
- assertNot(shouldTest("// +build arm 386", "linux", "amd64"))
+
+ // A file with no build tags will always be tested.
assert(shouldTest("// This is a test.", "os", "arch"))
+
+ // Build tags separated by a space are OR-ed together.
+ assertNot(shouldTest("// +build arm 386", "linux", "amd64"))
+
+ // Build tags seperated by a comma are AND-ed together.
+ assertNot(shouldTest("// +build !windows,!plan9", "windows", "amd64"))
+ assertNot(shouldTest("// +build !windows,!plan9", "plan9", "386"))
+
+ // Build tags on multiple lines are AND-ed together.
+ assert(shouldTest("// +build !windows\n// +build amd64", "linux", "amd64"))
+ assertNot(shouldTest("// +build !windows\n// +build amd64", "windows", "amd64"))
+
+ // Test that (!a OR !b) matches anything.
+ assert(shouldTest("// +build !windows !plan9", "windows", "amd64"))
}
// envForDir returns a copy of the environment
diff --git a/test/testlib b/test/testlib
index de138b1d1..4a17f4feb 100644
--- a/test/testlib
+++ b/test/testlib
@@ -16,29 +16,50 @@ pkgs() {
done | sort
}
+_match() {
+ case $1 in
+ *,*)
+ #echo >&2 "match comma separated $1"
+ first=$(echo $1 | sed 's/,.*//')
+ rest=$(echo $1 | sed 's/[^,]*,//')
+ if _match $first && _match $rest; then
+ return 0
+ fi
+ return 1
+ ;;
+ '!'*)
+ #echo >&2 "match negation $1"
+ neg=$(echo $1 | sed 's/^!//')
+ if _match $neg; then
+ return 1
+ fi
+ return 0
+ ;;
+ $GOARCH|$GOOS)
+ #echo >&2 "match GOARCH or GOOS $1"
+ return 0
+ ;;
+ esac
+ return 1
+}
+
# +build aborts execution if the supplied tags don't match,
# i.e. none of the tags (x or !x) matches GOARCH or GOOS.
+build() {
if (( $# == 0 )); then
return
fi
+ m=0
for tag; do
- case $tag in
- $GOARCH|$GOOS)
- #echo >&2 "match $tag in $1"
- return # don't exclude.
- ;;
- '!'$GOARCH|'!'$GOOS)
- ;;
- '!'*)
- # not x where x is neither GOOS nor GOARCH.
- #echo >&2 "match $tag in $1"
- return # don't exclude
- ;;
- esac
+ if _match $tag; then
+ m=1
+ fi
done
- # no match.
- exit 0
+ if [ $m = 0 ]; then
+ #echo >&2 no match
+ exit 0
+ fi
+ unset m
}
compile() {