aboutsummaryrefslogtreecommitdiff
path: root/starlarktest
diff options
context:
space:
mode:
authorAlan Donovan <adonovan@google.com>2018-10-23 11:05:09 -0400
committerAlan Donovan <adonovan@google.com>2018-10-23 11:05:09 -0400
commite3deafefac22db7bfd4877d37614fe5db4b03881 (patch)
treee3de93e8b63abffb95767055fb392714cc8cc8a1 /starlarktest
parentce5c2fa1ad6a8fa4beb4eacdbd3bf9997162d144 (diff)
downloadstarlark-go-e3deafefac22db7bfd4877d37614fe5db4b03881.tar.gz
rename skylark -> starlark
Change-Id: Iebd0e040ff674b2f9da39bf5242c8afaa7f4ddc8
Diffstat (limited to 'starlarktest')
-rw-r--r--starlarktest/assert.star50
-rw-r--r--starlarktest/starlarktest.go137
2 files changed, 187 insertions, 0 deletions
diff --git a/starlarktest/assert.star b/starlarktest/assert.star
new file mode 100644
index 0000000..f453aac
--- /dev/null
+++ b/starlarktest/assert.star
@@ -0,0 +1,50 @@
+
+# Predeclared built-ins for this module:
+#
+# error(msg): report an error in Go's test framework without halting execution.
+# catch(f): evaluate f() and returns its evaluation error message, if any
+# matches(str, pattern): report whether str matches regular expression pattern.
+# struct: a constructor for a simple HasFields implementation.
+# _freeze(x): freeze the value x and everything reachable from it.
+#
+# Clients may use these functions to define their own testing abstractions.
+
+def _eq(x, y):
+ if x != y:
+ error("%r != %r" % (x, y))
+
+def _ne(x, y):
+ if x == y:
+ error("%r == %r" % (x, y))
+
+def _true(cond, msg="assertion failed"):
+ if not cond:
+ error(msg)
+
+def _lt(x, y):
+ if not (x < y):
+ error("%s is not less than %s" % (x, y))
+
+def _contains(x, y):
+ if y not in x:
+ error("%s does not contain %s" % (x, y))
+
+def _fails(f, pattern):
+ "assert_fails asserts that evaluation of f() fails with the specified error."
+ msg = catch(f)
+ if msg == None:
+ error("evaluation succeeded unexpectedly (want error matching %r)" % pattern)
+ elif not matches(pattern, msg):
+ error("regular expression (%s) did not match error (%s)" % (pattern, msg))
+
+freeze = _freeze # an exported global whose value is the built-in freeze function
+
+assert = struct(
+ fail = error,
+ eq = _eq,
+ ne = _ne,
+ true = _true,
+ lt = _lt,
+ contains = _contains,
+ fails = _fails,
+)
diff --git a/starlarktest/starlarktest.go b/starlarktest/starlarktest.go
new file mode 100644
index 0000000..d1d38fe
--- /dev/null
+++ b/starlarktest/starlarktest.go
@@ -0,0 +1,137 @@
+// Copyright 2017 The Bazel 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 starlarktest defines utilities for testing Starlark programs.
+//
+// Clients can call LoadAssertModule to load a module that defines
+// several functions useful for testing. See assert.star for its
+// definition.
+//
+// The assert.error function, which reports errors to the current Go
+// testing.T, requires that clients call SetTest(thread, t) before use.
+package starlarktest
+
+import (
+ "bytes"
+ "fmt"
+ "go/build"
+ "path/filepath"
+ "regexp"
+ "sync"
+
+ "github.com/google/starlark"
+ "github.com/google/starlark/starlarkstruct"
+)
+
+const localKey = "Reporter"
+
+// A Reporter is a value to which errors may be reported.
+// It is satisfied by *testing.T.
+type Reporter interface {
+ Error(args ...interface{})
+}
+
+// SetReporter associates an error reporter (such as a testing.T in
+// a Go test) with the Starlark thread so that Starlark programs may
+// report errors to it.
+func SetReporter(thread *starlark.Thread, r Reporter) {
+ thread.SetLocal(localKey, r)
+}
+
+// GetReporter returns the Starlark thread's error reporter.
+// It must be preceded by a call to SetReporter.
+func GetReporter(thread *starlark.Thread) Reporter {
+ r, ok := thread.Local(localKey).(Reporter)
+ if !ok {
+ panic("internal error: starlarktest.SetReporter was not called")
+ }
+ return r
+}
+
+var (
+ once sync.Once
+ assert starlark.StringDict
+ assertErr error
+)
+
+// LoadAssertModule loads the assert module.
+// It is concurrency-safe and idempotent.
+func LoadAssertModule() (starlark.StringDict, error) {
+ once.Do(func() {
+ predeclared := starlark.StringDict{
+ "error": starlark.NewBuiltin("error", error_),
+ "catch": starlark.NewBuiltin("catch", catch),
+ "matches": starlark.NewBuiltin("matches", matches),
+ "struct": starlark.NewBuiltin("struct", starlarkstruct.Make),
+ "_freeze": starlark.NewBuiltin("freeze", freeze),
+ }
+ filename := DataFile("starlark/starlarktest", "assert.star")
+ thread := new(starlark.Thread)
+ assert, assertErr = starlark.ExecFile(thread, filename, nil, predeclared)
+ })
+ return assert, assertErr
+}
+
+// catch(f) evaluates f() and returns its evaluation error message
+// if it failed or None if it succeeded.
+func catch(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
+ var fn starlark.Callable
+ if err := starlark.UnpackArgs("catch", args, kwargs, "fn", &fn); err != nil {
+ return nil, err
+ }
+ if _, err := starlark.Call(thread, fn, nil, nil); err != nil {
+ return starlark.String(err.Error()), nil
+ }
+ return starlark.None, nil
+}
+
+// matches(pattern, str) reports whether string str matches the regular expression pattern.
+func matches(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
+ var pattern, str string
+ if err := starlark.UnpackArgs("matches", args, kwargs, "pattern", &pattern, "str", &str); err != nil {
+ return nil, err
+ }
+ ok, err := regexp.MatchString(pattern, str)
+ if err != nil {
+ return nil, fmt.Errorf("matches: %s", err)
+ }
+ return starlark.Bool(ok), nil
+}
+
+// error(x) reports an error to the Go test framework.
+func error_(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
+ if len(args) != 1 {
+ return nil, fmt.Errorf("error: got %d arguments, want 1", len(args))
+ }
+ var buf bytes.Buffer
+ thread.Caller().WriteBacktrace(&buf)
+ buf.WriteString("Error: ")
+ if s, ok := starlark.AsString(args[0]); ok {
+ buf.WriteString(s)
+ } else {
+ buf.WriteString(args[0].String())
+ }
+ GetReporter(thread).Error(buf.String())
+ return starlark.None, nil
+}
+
+// freeze(x) freezes its operand.
+func freeze(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
+ if len(kwargs) > 0 {
+ return nil, fmt.Errorf("freeze does not accept keyword arguments")
+ }
+ if len(args) != 1 {
+ return nil, fmt.Errorf("freeze got %d arguments, wants 1", len(args))
+ }
+ args[0].Freeze()
+ return args[0], nil
+}
+
+// DataFile returns the effective filename of the specified
+// test data resource. The function abstracts differences between
+// 'go build', under which a test runs in its package directory,
+// and Blaze, under which a test runs in the root of the tree.
+var DataFile = func(pkgdir, filename string) string {
+ return filepath.Join(build.Default.GOPATH, "src/github.com/google", pkgdir, filename)
+}