diff --git a/BUILD.bazel b/BUILD.bazel index 6f3b2390e2..21c31ee7e9 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -89,3 +89,17 @@ lines_sorted_test( exports_files(["AUTHORS"]) go_info() + +filegroup( + name = "all_files", + testonly = True, + srcs = [ + "BUILD.bazel", + "WORKSPACE", + "//extras:all_files", + "//go:all_files", + "//proto:all_files", + "//third_party:all_files", + ], + visibility = ["//visibility:public"], +) diff --git a/extras/BUILD.bazel b/extras/BUILD.bazel index 6f3d97126c..833cf23633 100644 --- a/extras/BUILD.bazel +++ b/extras/BUILD.bazel @@ -3,3 +3,10 @@ filegroup( srcs = glob(["*.bzl"]) + ["//go/private:all_rules"], visibility = ["//visibility:public"], ) + +filegroup( + name = "all_files", + testonly = True, + srcs = glob(["**"]), + visibility = ["//visibility:public"], +) diff --git a/go/BUILD.bazel b/go/BUILD.bazel index 4e14fe7dbe..cfcf1a3039 100644 --- a/go/BUILD.bazel +++ b/go/BUILD.bazel @@ -1,3 +1,15 @@ +filegroup( + name = "all_files", + testonly = True, + srcs = glob(["**"]) + [ + "//go/platform:all_files", + "//go/toolchain:all_files", + "//go/tools:all_files", + "//go/private:all_files", + ], + visibility = ["//visibility:public"], +) + filegroup( name = "all_rules", srcs = glob(["*.bzl"]) + ["//go/private:all_rules"], diff --git a/go/platform/BUILD.bazel b/go/platform/BUILD.bazel index bf31d757f6..56714b5610 100644 --- a/go/platform/BUILD.bazel +++ b/go/platform/BUILD.bazel @@ -10,3 +10,10 @@ package(default_visibility = ["//visibility:public"]) load(":list.bzl", "declare_config_settings") declare_config_settings() + +filegroup( + name = "all_files", + testonly = True, + srcs = glob(["**"]), + visibility = ["//visibility:public"], +) diff --git a/go/private/BUILD.bazel b/go/private/BUILD.bazel index c719fe1b43..d67db0fdb1 100644 --- a/go/private/BUILD.bazel +++ b/go/private/BUILD.bazel @@ -4,6 +4,13 @@ filegroup( visibility = ["//visibility:public"], ) +filegroup( + name = "all_files", + testonly = True, + srcs = glob(["**"]), + visibility = ["//visibility:public"], +) + config_setting( name = "strip-always", values = {"strip": "always"}, diff --git a/go/toolchain/BUILD.bazel b/go/toolchain/BUILD.bazel index 65fa47664d..ae85df6a4c 100644 --- a/go/toolchain/BUILD.bazel +++ b/go/toolchain/BUILD.bazel @@ -6,3 +6,10 @@ load( package(default_visibility = ["//visibility:public"]) declare_constraints() + +filegroup( + name = "all_files", + testonly = True, + srcs = glob(["**"]), + visibility = ["//visibility:public"], +) diff --git a/go/tools/BUILD.bazel b/go/tools/BUILD.bazel index e69de29bb2..221ab42fd4 100644 --- a/go/tools/BUILD.bazel +++ b/go/tools/BUILD.bazel @@ -0,0 +1,10 @@ +filegroup( + name = "all_files", + testonly = True, + srcs = [ + "//go/tools/bazel:all_files", + "//go/tools/builders:all_files", + "//go/tools/coverdata:all_files", + ], + visibility = ["//visibility:public"], +) diff --git a/go/tools/bazel/BUILD.bazel b/go/tools/bazel/BUILD.bazel index 4f5d70869b..6fbd408601 100644 --- a/go/tools/bazel/BUILD.bazel +++ b/go/tools/bazel/BUILD.bazel @@ -19,3 +19,10 @@ go_test( ) # Runfiles functionality in this package is tested by //tests/core/runfiles. + +filegroup( + name = "all_files", + testonly = True, + srcs = glob(["**"]), + visibility = ["//visibility:public"], +) diff --git a/go/tools/bazel_testing/BUILD.bazel b/go/tools/bazel_testing/BUILD.bazel new file mode 100644 index 0000000000..1d0d2cfe9d --- /dev/null +++ b/go/tools/bazel_testing/BUILD.bazel @@ -0,0 +1,12 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = ["bazel_testing.go"], + importpath = "github.com/bazelbuild/rules_go/go/tools/bazel_testing", + visibility = ["//visibility:public"], + deps = [ + "//go/tools/bazel:go_default_library", + "//go/tools/internal/txtar:go_default_library", + ], +) diff --git a/go/tools/bazel_testing/bazel_testing.go b/go/tools/bazel_testing/bazel_testing.go new file mode 100644 index 0000000000..34f6a0b4a4 --- /dev/null +++ b/go/tools/bazel_testing/bazel_testing.go @@ -0,0 +1,359 @@ +// Copyright 2019 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package bazel_testing provides an integration testing framework for +// testing rules_go with Bazel. +// +// Tests may be written by declaring a go_bazel_test target instead of +// a go_test (go_bazel_test is defined in def.bzl here), then calling +// TestMain. Tests are run in a synthetic test workspace. Tests may run +// bazel commands with RunBazel. +package bazel_testing + +import ( + "bytes" + "flag" + "fmt" + "io" + "io/ioutil" + "os" + "os/exec" + "os/signal" + "path" + "path/filepath" + "runtime" + "sort" + "strings" + "testing" + "text/template" + + "github.com/bazelbuild/rules_go/go/tools/bazel" + "github.com/bazelbuild/rules_go/go/tools/internal/txtar" +) + +// Args is a list of arguments to TestMain. It's defined as a struct so +// that new optional arguments may be added without breaking compatibility. +type Args struct { + // Main is a text archive containing files in the main workspace. + // The text archive format is parsed by + // //go/tools/internal/txtar:go_default_library, which is copied from + // cmd/go/internal/txtar. If this archive does not contain a WORKSPACE file, + // a default file will be synthesized. + Main string + + // WorkspaceSuffix is a string that should be appended to the end + // of the default generated WORKSPACE file. + WorkspaceSuffix string +} + +// debug may be set to make the test print the test workspace path and stop +// instead of running tests. +const debug = false + +// TestMain should be called by tests using this framework from a function named +// "TestMain". For example: +// +// func TestMain(m *testing.M) { +// os.Exit(bazel_testing.TestMain(m, bazel_testing.Args{...})) +// } +// +// TestMain constructs a set of workspaces and changes the working directory to +// the main workspace. +func TestMain(m *testing.M, args Args) { + // Defer os.Exit with the correct code. This ensures other deferred cleanup + // functions are run first. + code := 1 + defer func() { + if r := recover(); r != nil { + fmt.Fprintf(os.Stderr, "panic: %v\n", r) + code = 1 + } + os.Exit(code) + }() + + flag.Parse() + + workspaceDir, cleanup, err := setupWorkspace(args) + defer cleanup() + if err != nil { + fmt.Fprintf(os.Stderr, "error: %v\n", err) + return + } + + if debug { + fmt.Fprintf(os.Stderr, "test setup in %s\n", workspaceDir) + interrupted := make(chan os.Signal) + signal.Notify(interrupted, os.Interrupt) + <-interrupted + return + } + + if err := os.Chdir(workspaceDir); err != nil { + fmt.Fprintf(os.Stderr, "%v\n", err) + return + } + defer exec.Command("bazel", "shutdown").Run() + + code = m.Run() +} + +// RunBazel invokes a bazel command with a list of arguments. +// +// If the command starts but exits with a non-zero status, a *StderrExitError +// will be returned which wraps the original *exec.ExitError. +func RunBazel(args ...string) error { + cmd := exec.Command("bazel", args...) + for _, e := range os.Environ() { + // Filter environment variables set by the bazel test wrapper script. + // These confuse recursive invocations of Bazel. + if strings.HasPrefix(e, "TEST_") || strings.HasPrefix(e, "RUNFILES_") { + continue + } + cmd.Env = append(cmd.Env, e) + } + + buf := &bytes.Buffer{} + cmd.Stderr = buf + err := cmd.Run() + if eErr, ok := err.(*exec.ExitError); ok { + eErr.Stderr = buf.Bytes() + err = &StderrExitError{Err: eErr} + } + return err +} + +// StderrExitError wraps *exec.ExitError and prints the complete stderr output +// from a command. +type StderrExitError struct { + Err *exec.ExitError +} + +func (e *StderrExitError) Error() string { + sb := &strings.Builder{} + sb.Write(e.Err.Stderr) + sb.WriteString(e.Err.Error()) + return sb.String() +} + +func setupWorkspace(args Args) (dir string, cleanup func(), err error) { + var cleanups []func() + cleanup = func() { + for i := len(cleanups) - 1; i >= 0; i-- { + cleanups[i]() + } + } + defer func() { + if err != nil { + cleanup() + cleanup = nil + } + }() + + // Find a suitable cache directory. We want something persistent where we + // can store a bazel output base across test runs, even for multiple tests. + var cacheDir, outBaseDir string + if tmpDir := os.Getenv("TEST_TMPDIR"); tmpDir != "" { + // TEST_TMPDIR is set by Bazel's test wrapper. Bazel itself uses this to + // detect that it's run by a test. When invoked like this, Bazel sets + // its output base directory to a temporary directory. This wastes a lot + // of time (a simple test takes 45s instead of 3s). We use TEST_TMPDIR + // to find a persistent location in the execroot. We won't pass TEST_TMPDIR + // to bazel in RunBazel. + tmpDir = filepath.Clean(tmpDir) + if i := strings.Index(tmpDir, string(os.PathSeparator)+"execroot"+string(os.PathSeparator)); i >= 0 { + outBaseDir = tmpDir[:i] + cacheDir = filepath.Join(outBaseDir, "bazel_testing") + } else { + cacheDir = filepath.Join(tmpDir, "bazel_testing") + } + } else { + // The test is not invoked by Bazel, so just use the user's cache. + cacheDir, err = os.UserCacheDir() + if err != nil { + return "", cleanup, err + } + cacheDir = filepath.Join(cacheDir, "bazel_testing") + } + + // TODO(jayconrod): any other directories needed for caches? + execDir := filepath.Join(cacheDir, "bazel_go_test") + if err := os.RemoveAll(execDir); err != nil { + return "", cleanup, err + } + cleanups = append(cleanups, func() { os.RemoveAll(execDir) }) + + // Extract test files for the main workspace. + mainDir := filepath.Join(execDir, "main") + if err := os.MkdirAll(mainDir, 0777); err != nil { + return "", cleanup, err + } + if err := extractTxtar(mainDir, args.Main); err != nil { + return "", cleanup, fmt.Errorf("building main workspace: %v", err) + } + + // Copy or data files for rules_go, or whatever was passed in. + runfiles, err := bazel.ListRunfiles() + if err != nil { + return "", cleanup, err + } + type runfileKey struct{ workspace, short string } + runfileMap := make(map[runfileKey]string) + for _, rf := range runfiles { + runfileMap[runfileKey{rf.Workspace, rf.ShortPath}] = rf.Path + } + workspaceNames := make(map[string]bool) + for _, argPath := range flag.Args() { + shortPath := path.Clean(argPath) + if !strings.HasPrefix(shortPath, "external/") { + return "", cleanup, fmt.Errorf("unexpected file: %s", argPath) + } + shortPath = shortPath[len("external/"):] + var workspace string + if i := strings.IndexByte(shortPath, '/'); i < 0 { + return "", cleanup, fmt.Errorf("unexpected file: %s", argPath) + } else { + workspace = shortPath[:i] + shortPath = shortPath[i+1:] + } + workspaceNames[workspace] = true + srcPath, ok := runfileMap[runfileKey{workspace, shortPath}] + if !ok { + return "", cleanup, fmt.Errorf("unknown runfile: %s", argPath) + } + + dstPath := filepath.Join(execDir, workspace, shortPath) + if err := copyOrLink(dstPath, srcPath); err != nil { + return "", cleanup, err + } + } + + // If there's no WORKSPACE file, create one. + workspacePath := filepath.Join(mainDir, "WORKSPACE") + if _, err := os.Stat(workspacePath); os.IsNotExist(err) { + w, err := os.Create(workspacePath) + if err != nil { + return "", cleanup, err + } + defer func() { + if cerr := w.Close(); err == nil && cerr != nil { + err = cerr + } + }() + info := workspaceTemplateInfo{Suffix: args.WorkspaceSuffix} + for name := range workspaceNames { + info.WorkspaceNames = append(info.WorkspaceNames, name) + } + sort.Strings(info.WorkspaceNames) + if outBaseDir != "" { + goSDKPath := filepath.Join(outBaseDir, "external", "go_sdk") + rel, err := filepath.Rel(mainDir, goSDKPath) + if err != nil { + return "", cleanup, fmt.Errorf("could not find relative path from %q to %q for go_sdk", mainDir, goSDKPath) + } + rel = filepath.ToSlash(rel) + info.GoSDKPath = rel + } + if err := defaultWorkspaceTpl.Execute(w, info); err != nil { + return "", cleanup, err + } + } + + return mainDir, cleanup, nil +} + +func extractTxtar(dir, txt string) error { + ar := txtar.Parse([]byte(txt)) + for _, f := range ar.Files { + if err := ioutil.WriteFile(filepath.Join(dir, f.Name), f.Data, 0666); err != nil { + return err + } + } + return nil +} + +type workspaceTemplateInfo struct { + WorkspaceNames []string + GoSDKPath string + Suffix string +} + +var defaultWorkspaceTpl = template.Must(template.New("").Parse(` +{{range .WorkspaceNames}} +local_repository( + name = "{{.}}", + path = "../{{.}}", +) +{{end}} + +{{if not .GoSDKPath}} +load("@io_bazel_rules_go//go:deps.bzl", "go_rules_dependencies", "go_register_toolchains") + +go_rules_dependencies() + +go_register_toolchains(go_version = "host") +{{else}} +local_repository( + name = "local_go_sdk", + path = "{{.GoSDKPath}}", +) + +load("@io_bazel_rules_go//go:deps.bzl", "go_rules_dependencies", "go_register_toolchains", "go_wrap_sdk") + +go_rules_dependencies() + +go_wrap_sdk( + name = "go_sdk", + root_file = "@local_go_sdk//:ROOT", +) + +go_register_toolchains() +{{end}} +{{.Suffix}} +`)) + +func copyOrLink(dstPath, srcPath string) error { + if err := os.MkdirAll(filepath.Dir(dstPath), 0777); err != nil { + return err + } + + copy := func(dstPath, srcPath string) (err error) { + src, err := os.Open(srcPath) + if err != nil { + return err + } + defer src.Close() + + dst, err := os.Create(dstPath) + if err != nil { + return err + } + defer func() { + if cerr := dst.Close(); err == nil && cerr != nil { + err = cerr + } + }() + + _, err = io.Copy(dst, src) + return err + } + + if runtime.GOOS == "windows" { + return copy(dstPath, srcPath) + } + absSrcPath, err := filepath.Abs(srcPath) + if err != nil { + return err + } + return os.Symlink(absSrcPath, dstPath) +} diff --git a/go/tools/bazel_testing/def.bzl b/go/tools/bazel_testing/def.bzl new file mode 100644 index 0000000000..09cbf1ae5a --- /dev/null +++ b/go/tools/bazel_testing/def.bzl @@ -0,0 +1,56 @@ +# Copyright 2019 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +def go_bazel_test(rule_files = None, **kwargs): + """go_bazel_test is a wrapper for go_test that simplifies the use of + //go/tools/bazel_testing:go_default_library. Tests may be written + that don't explicitly depend on bazel_testing or rules_go files. + """ + + if not rule_files: + rule_files = ["@io_bazel_rules_go//:all_files"] + + # Add dependency on bazel_testing library. + kwargs.setdefault("deps", []) + kwargs["deps"] += ["@io_bazel_rules_go//go/tools/bazel_testing:go_default_library"] + + # Add data dependency on rules_go files. bazel_testing will copy or link + # these files in an external repo. + kwargs.setdefault("data", []) + kwargs["data"] += rule_files + + # Add paths to rules_go files to arguments. bazel_testing will copy or link + # these files. + kwargs.setdefault("args", []) + kwargs["args"] += ["--"] + ["$(locations {})".format(t) for t in rule_files] + + # Set rundir to the workspace root directory to ensure relative paths + # are interpreted correctly. + kwargs.setdefault("rundir", ".") + + # Set tags. + # local: don't run in sandbox or on remote executor. + # exclusive: run one test at a time, since they share a Bazel + # output directory. If we don't do this, tests must extract the bazel + # installation and start with a fresh cache every time, making them + # much slower. + kwargs.setdefault("tags", []) + if "local" not in kwargs["tags"]: + kwargs["tags"] += ["local"] + if "exclusive" not in kwargs["tags"]: + kwargs["tags"] += ["exclusive"] + + go_test(**kwargs) diff --git a/go/tools/builders/BUILD.bazel b/go/tools/builders/BUILD.bazel index bd31ed180b..b87dc1b77b 100644 --- a/go/tools/builders/BUILD.bazel +++ b/go/tools/builders/BUILD.bazel @@ -55,8 +55,8 @@ go_binary( go_source( name = "nogo_srcs", srcs = [ - "flags.go", "env.go", + "flags.go", "nogo_main.go", ], # //go/tools/builders:nogo_srcs is considered a different target by @@ -119,3 +119,10 @@ filegroup( srcs = glob(["*.go"]), visibility = ["//:__subpackages__"], ) + +filegroup( + name = "all_files", + testonly = True, + srcs = glob(["**"]), + visibility = ["//visibility:public"], +) diff --git a/go/tools/coverdata/BUILD.bazel b/go/tools/coverdata/BUILD.bazel index aa6789bc1d..02a108be81 100644 --- a/go/tools/coverdata/BUILD.bazel +++ b/go/tools/coverdata/BUILD.bazel @@ -6,3 +6,10 @@ go_tool_library( importpath = "github.com/bazelbuild/rules_go/go/tools/coverdata", visibility = ["//visibility:public"], ) + +filegroup( + name = "all_files", + testonly = True, + srcs = glob(["**"]), + visibility = ["//visibility:public"], +) diff --git a/go/tools/internal/txtar/BUILD.bazel b/go/tools/internal/txtar/BUILD.bazel new file mode 100644 index 0000000000..24174e109d --- /dev/null +++ b/go/tools/internal/txtar/BUILD.bazel @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = ["archive.go"], + importpath = "github.com/bazelbuild/rules_go/go/tools/internal/txtar", + visibility = ["//go/tools:__subpackages__"], +) + +go_test( + name = "go_default_test", + srcs = ["archive_test.go"], + embed = [":go_default_library"], +) diff --git a/go/tools/internal/txtar/archive.go b/go/tools/internal/txtar/archive.go new file mode 100644 index 0000000000..c384f33bdf --- /dev/null +++ b/go/tools/internal/txtar/archive.go @@ -0,0 +1,140 @@ +// Copyright 2018 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 txtar implements a trivial text-based file archive format. +// +// The goals for the format are: +// +// - be trivial enough to create and edit by hand. +// - be able to store trees of text files describing go command test cases. +// - diff nicely in git history and code reviews. +// +// Non-goals include being a completely general archive format, +// storing binary data, storing file modes, storing special files like +// symbolic links, and so on. +// +// Txtar format +// +// A txtar archive is zero or more comment lines and then a sequence of file entries. +// Each file entry begins with a file marker line of the form "-- FILENAME --" +// and is followed by zero or more file content lines making up the file data. +// The comment or file content ends at the next file marker line. +// The file marker line must begin with the three-byte sequence "-- " +// and end with the three-byte sequence " --", but the enclosed +// file name can be surrounding by additional white space, +// all of which is stripped. +// +// If the txtar file is missing a trailing newline on the final line, +// parsers should consider a final newline to be present anyway. +// +// There are no possible syntax errors in a txtar archive. +package txtar + +import ( + "bytes" + "fmt" + "io/ioutil" + "strings" +) + +// An Archive is a collection of files. +type Archive struct { + Comment []byte + Files []File +} + +// A File is a single file in an archive. +type File struct { + Name string // name of file ("foo/bar.txt") + Data []byte // text content of file +} + +// Format returns the serialized form of an Archive. +// It is assumed that the Archive data structure is well-formed: +// a.Comment and all a.File[i].Data contain no file marker lines, +// and all a.File[i].Name is non-empty. +func Format(a *Archive) []byte { + var buf bytes.Buffer + buf.Write(fixNL(a.Comment)) + for _, f := range a.Files { + fmt.Fprintf(&buf, "-- %s --\n", f.Name) + buf.Write(fixNL(f.Data)) + } + return buf.Bytes() +} + +// ParseFile parses the named file as an archive. +func ParseFile(file string) (*Archive, error) { + data, err := ioutil.ReadFile(file) + if err != nil { + return nil, err + } + return Parse(data), nil +} + +// Parse parses the serialized form of an Archive. +// The returned Archive holds slices of data. +func Parse(data []byte) *Archive { + a := new(Archive) + var name string + a.Comment, name, data = findFileMarker(data) + for name != "" { + f := File{name, nil} + f.Data, name, data = findFileMarker(data) + a.Files = append(a.Files, f) + } + return a +} + +var ( + newlineMarker = []byte("\n-- ") + marker = []byte("-- ") + markerEnd = []byte(" --") +) + +// findFileMarker finds the next file marker in data, +// extracts the file name, and returns the data before the marker, +// the file name, and the data after the marker. +// If there is no next marker, findFileMarker returns before = fixNL(data), name = "", after = nil. +func findFileMarker(data []byte) (before []byte, name string, after []byte) { + var i int + for { + if name, after = isMarker(data[i:]); name != "" { + return data[:i], name, after + } + j := bytes.Index(data[i:], newlineMarker) + if j < 0 { + return fixNL(data), "", nil + } + i += j + 1 // positioned at start of new possible marker + } +} + +// isMarker checks whether data begins with a file marker line. +// If so, it returns the name from the line and the data after the line. +// Otherwise it returns name == "" with an unspecified after. +func isMarker(data []byte) (name string, after []byte) { + if !bytes.HasPrefix(data, marker) { + return "", nil + } + if i := bytes.IndexByte(data, '\n'); i >= 0 { + data, after = data[:i], data[i+1:] + } + if !bytes.HasSuffix(data, markerEnd) { + return "", nil + } + return strings.TrimSpace(string(data[len(marker) : len(data)-len(markerEnd)])), after +} + +// If data is empty or ends in \n, fixNL returns data. +// Otherwise fixNL returns a new slice consisting of data with a final \n added. +func fixNL(data []byte) []byte { + if len(data) == 0 || data[len(data)-1] == '\n' { + return data + } + d := make([]byte, len(data)+1) + copy(d, data) + d[len(data)] = '\n' + return d +} diff --git a/go/tools/internal/txtar/archive_test.go b/go/tools/internal/txtar/archive_test.go new file mode 100644 index 0000000000..3f734f6762 --- /dev/null +++ b/go/tools/internal/txtar/archive_test.go @@ -0,0 +1,67 @@ +// Copyright 2018 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 txtar + +import ( + "bytes" + "fmt" + "reflect" + "testing" +) + +var tests = []struct { + name string + text string + parsed *Archive +}{ + { + name: "basic", + text: `comment1 +comment2 +-- file1 -- +File 1 text. +-- foo --- +More file 1 text. +-- file 2 -- +File 2 text. +-- empty -- +-- noNL -- +hello world`, + parsed: &Archive{ + Comment: []byte("comment1\ncomment2\n"), + Files: []File{ + {"file1", []byte("File 1 text.\n-- foo ---\nMore file 1 text.\n")}, + {"file 2", []byte("File 2 text.\n")}, + {"empty", []byte{}}, + {"noNL", []byte("hello world\n")}, + }, + }, + }, +} + +func Test(t *testing.T) { + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := Parse([]byte(tt.text)) + if !reflect.DeepEqual(a, tt.parsed) { + t.Fatalf("Parse: wrong output:\nhave:\n%s\nwant:\n%s", shortArchive(a), shortArchive(tt.parsed)) + } + text := Format(a) + a = Parse(text) + if !reflect.DeepEqual(a, tt.parsed) { + t.Fatalf("Parse after Format: wrong output:\nhave:\n%s\nwant:\n%s", shortArchive(a), shortArchive(tt.parsed)) + } + }) + } +} + +func shortArchive(a *Archive) string { + var buf bytes.Buffer + fmt.Fprintf(&buf, "comment: %q\n", a.Comment) + for _, f := range a.Files { + fmt.Fprintf(&buf, "file %q: %q\n", f.Name, f.Data) + } + return buf.String() +} diff --git a/proto/BUILD.bazel b/proto/BUILD.bazel index bfd4bd93f0..b779849e92 100644 --- a/proto/BUILD.bazel +++ b/proto/BUILD.bazel @@ -105,3 +105,10 @@ filegroup( srcs = glob(["*.bzl"]) + ["//proto/wkt:all_rules"], visibility = ["//:__subpackages__"], ) + +filegroup( + name = "all_files", + srcs = glob(["**"]) + ["//proto/wkt:all_files"], + testonly = True, + visibility = ["//:__subpackages__"], +) diff --git a/proto/wkt/BUILD.bazel b/proto/wkt/BUILD.bazel index 9603c07009..baea35a233 100644 --- a/proto/wkt/BUILD.bazel +++ b/proto/wkt/BUILD.bazel @@ -6,5 +6,12 @@ filegroup( name = "all_rules", testonly = True, srcs = glob(["*.bzl"]), - visibility = ["//:__subpackages__"], + visibility = ["//visibility:public"], +) + +filegroup( + name = "all_files", + testonly = True, + srcs = glob(["**"]), + visibility = ["//visibility:public"], ) diff --git a/tests/legacy/no_prefix/BUILD.bazel b/tests/legacy/no_prefix/BUILD.bazel index e3cf14f9d1..0e5bf29ca9 100644 --- a/tests/legacy/no_prefix/BUILD.bazel +++ b/tests/legacy/no_prefix/BUILD.bazel @@ -1,30 +1,7 @@ +load("@io_bazel_rules_go//go/tools/bazel_testing:def.bzl", "go_bazel_test") load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") -load("@io_bazel_rules_go//tests:bazel_tests.bzl", "bazel_test") -bazel_test( - name = "no_prefix", - command = "build", - targets = [ - ":go_default_library", - ":go_default_xtest", - ":cmd", - ], -) - -go_library( - name = "go_default_library", - srcs = ["no_prefix.go"], - importpath = "github.com/bazelbuild/rules_go/tests/no_prefix", -) - -go_test( - name = "go_default_xtest", +go_bazel_test( + name = "no_prefix_test", srcs = ["no_prefix_test.go"], - deps = [":go_default_library"], -) - -go_binary( - name = "cmd", - srcs = ["cmd.go"], - deps = [":go_default_library"], ) diff --git a/tests/legacy/no_prefix/no_prefix_test.go b/tests/legacy/no_prefix/no_prefix_test.go index 14ba318e98..c78cac7556 100644 --- a/tests/legacy/no_prefix/no_prefix_test.go +++ b/tests/legacy/no_prefix/no_prefix_test.go @@ -1,3 +1,70 @@ +// Copyright 2019 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package no_prefix_test + +import ( + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +const mainFiles = ` +-- BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = ["no_prefix.go"], + importpath = "github.com/bazelbuild/rules_go/tests/no_prefix", +) + +go_test( + name = "go_default_xtest", + srcs = ["no_prefix_test.go"], + deps = [":go_default_library"], +) + +go_binary( + name = "cmd", + srcs = ["cmd.go"], + deps = [":go_default_library"], +) + +-- no_prefix.go -- +package no_prefix + +-- no_prefix_test.go -- package no_prefix_test +-- cmd.go -- +package main + import _ "github.com/bazelbuild/rules_go/tests/no_prefix" + +func main() { +} +` + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Main: mainFiles, + }) +} + +func TestBuild(t *testing.T) { + if err := bazel_testing.RunBazel("build", ":all"); err != nil { + t.Fatal(err) + } +} diff --git a/third_party/BUILD.bazel b/third_party/BUILD.bazel index e69de29bb2..b398c33658 100644 --- a/third_party/BUILD.bazel +++ b/third_party/BUILD.bazel @@ -0,0 +1,8 @@ +exports_files(glob(["*.patch"])) + +filegroup( + name = "all_files", + testonly = True, + srcs = glob(["**"]), + visibility = ["//visibility:public"], +)