diff options
Diffstat (limited to 'tests/core')
361 files changed, 13163 insertions, 0 deletions
diff --git a/tests/core/README.rst b/tests/core/README.rst new file mode 100644 index 00000000..835b9ba7 --- /dev/null +++ b/tests/core/README.rst @@ -0,0 +1,35 @@ +Core Go rules tests +=================== + +This contains tests of the core go rules. + +Contents +-------- + +.. Child list start + +* `Misc configuration transition tests <transition/README.rst>`_ +* `Basic go_library functionality <go_library/README.rst>`_ +* `output_groups functionality <output_groups/README.rst>`_ +* `Basic -buildmode=plugin functionality <go_plugin/README.rst>`_ +* `go_bazel_test macro functionality <go_bazel_test/README.rst>`_ +* `Core Go rules tests <nogo/README.rst>`_ +* `go_proto_library importmap <go_proto_library_importmap/README.rst>`_ +* `Cross compilation <cross/README.rst>`_ +* `Basic go_proto_library functionality <go_proto_library/README.rst>`_ +* `c-archive / c-shared linkmodes <c_linkmodes/README.rst>`_ +* `Basic go_test functionality <go_test/README.rst>`_ +* `.. _#2067: https://github.com/bazelbuild/rules_go/issues/2067 <cgo/README.rst>`_ +* `Runfiles functionality <runfiles/README.rst>`_ +* `go_download_sdk <go_download_sdk/README.rst>`_ +* `go_embed_data <go_embed_data/README.rst>`_ +* `race instrumentation <race/README.rst>`_ +* `stdlib functionality <stdlib/README.rst>`_ +* `Basic go_binary functionality <go_binary/README.rst>`_ +* `Starlark unit tests <starlark/README.rst>`_ +* `.. _#2127: https://github.com/bazelbuild/rules_go/issues/2127 <coverage/README.rst>`_ +* `Import maps <importmap/README.rst>`_ +* `Basic go_path functionality <go_path/README.rst>`_ + +.. Child list end + diff --git a/tests/core/boringcrypto/BUILD.bazel b/tests/core/boringcrypto/BUILD.bazel new file mode 100644 index 00000000..9de03bb0 --- /dev/null +++ b/tests/core/boringcrypto/BUILD.bazel @@ -0,0 +1,6 @@ +load("@io_bazel_rules_go//go/tools/bazel_testing:def.bzl", "go_bazel_test") + +go_bazel_test( + name = "boringcrypto_test", + srcs = ["boringcrypto_test.go"], +) diff --git a/tests/core/boringcrypto/README.rst b/tests/core/boringcrypto/README.rst new file mode 100644 index 00000000..cd084f33 --- /dev/null +++ b/tests/core/boringcrypto/README.rst @@ -0,0 +1,11 @@ +Boringcrypto +=========== + +Tests to ensure that support for building with boringcrypto is working as expected. + +boringcrypto_test +-------------- + +Test that the build is failed if a non-local Go version less than 1.19 is requested to be built with +boringcrypto. Test that binaries built with boringcrypto stdlib have X:boringcrypto in version +information.
\ No newline at end of file diff --git a/tests/core/boringcrypto/boringcrypto_test.go b/tests/core/boringcrypto/boringcrypto_test.go new file mode 100644 index 00000000..ec7bebfd --- /dev/null +++ b/tests/core/boringcrypto/boringcrypto_test.go @@ -0,0 +1,115 @@ +// Copyright 2022 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 boringcrypto_test + +import ( + "bytes" + "os" + "os/exec" + "strings" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Main: ` +-- BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") + +go_binary( + name = "program", + srcs = ["main.go"], + deps = [":library"], + visibility = ["//visibility:public"], +) + +go_library( + name = "library", + srcs = ["library.go"], + importpath = "example.com/library" +) +-- main.go -- +//go:build goexperiment.boringcrypto + +package main + +import "example.com/library" + +func main() { + library.F() +} +-- library.go -- +package library + +func F() {} +`, + }) +} + +const origWrapSDK = `go_wrap_sdk( + name = "go_sdk", + root_file = "@local_go_sdk//:ROOT", +)` + +const wrapSDKBoringcrypto = `go_wrap_sdk( + name = "go_sdk", + root_file = "@local_go_sdk//:ROOT", + experiments = ["boringcrypto"], +)` + +func TestBoringcryptoExperimentPresent(t *testing.T) { + mustReplaceInFile(t, "WORKSPACE", origWrapSDK, wrapSDKBoringcrypto) + defer mustReplaceInFile(t, "WORKSPACE", wrapSDKBoringcrypto, origWrapSDK) + + if _, err := exec.LookPath("go"); err != nil { + t.Skip("go command is necessary to evaluate if boringcrypto experiment is present") + } + + cmd := bazel_testing.BazelCmd("build", "//:program") + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stdout + if err := cmd.Run(); err != nil { + t.Fatal("failed to run bazel build: ", err) + } + + out, err := exec.Command("go", "version", "bazel-bin/program_/program").CombinedOutput() + if err != nil { + t.Fatalf("failed to run go version command: %v\noutput was:\n%v", err, string(out)) + } + + if !strings.Contains(string(out), "X:boringcrypto") { + t.Fatalf(`version of binary: got %q, want string containing "X:boringcrypto"`, string(out)) + } +} + +func mustReplaceInFile(t *testing.T, path, old, new string) { + t.Helper() + if old == new { + return + } + data, err := os.ReadFile(path) + if err != nil { + t.Fatal(err) + } + if !bytes.Contains(data, []byte(old)) { + t.Fatalf("bytes to replace %q not found in file %q with contents, %q", old, path, data) + } + data = bytes.ReplaceAll(data, []byte(old), []byte(new)) + if err := os.WriteFile(path, data, 0666); err != nil { + t.Fatal(err) + } +} diff --git a/tests/core/build_stdlib/BUILD.bazel b/tests/core/build_stdlib/BUILD.bazel new file mode 100644 index 00000000..2d619fc3 --- /dev/null +++ b/tests/core/build_stdlib/BUILD.bazel @@ -0,0 +1,6 @@ +load("@io_bazel_rules_go//go/tools/bazel_testing:def.bzl", "go_bazel_test") + +go_bazel_test( + name = "build_stdlib_test", + srcs = ["build_stdlib_test.go"], +) diff --git a/tests/core/build_stdlib/README.rst b/tests/core/build_stdlib/README.rst new file mode 100644 index 00000000..38a0f100 --- /dev/null +++ b/tests/core/build_stdlib/README.rst @@ -0,0 +1,11 @@ +Building with standard libraries with missing .a files +=========== + +Tests to ensure that building with Go 1.20 and later versions of Go, which no longer +include precompiled standard library .a files, continues to work + +build_stdlib_test +-------------- + +Test that a simple binary depending on a simple library can build when the WORKSPACE's +go version is set to 1.20rc1.
\ No newline at end of file diff --git a/tests/core/build_stdlib/build_stdlib_test.go b/tests/core/build_stdlib/build_stdlib_test.go new file mode 100644 index 00000000..31851b3a --- /dev/null +++ b/tests/core/build_stdlib/build_stdlib_test.go @@ -0,0 +1,97 @@ +// Copyright 2022 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 build_stdlib_test + +import ( + "bytes" + "os" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Main: ` +-- BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") + +go_binary( + name = "program", + srcs = ["main.go"], + deps = [":library"], + visibility = ["//visibility:public"], +) + +go_library( + name = "library", + srcs = ["library.go"], + importpath = "example.com/library" +) +-- main.go -- +package main + +import "example.com/library" + +func main() { + library.F() +} +-- library.go -- +package library + +func F() {} +`, + }) +} + +const origWrapSDK = `go_wrap_sdk( + name = "go_sdk", + root_file = "@local_go_sdk//:ROOT", +) + +go_register_toolchains()` + +const toolchain120 = `go_register_toolchains(version = "1.20rc1")` + +func TestBoringcryptoExperimentPresent(t *testing.T) { + mustReplaceInFile(t, "WORKSPACE", origWrapSDK, toolchain120) + defer mustReplaceInFile(t, "WORKSPACE", toolchain120, origWrapSDK) + + cmd := bazel_testing.BazelCmd("build", "//:program") + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stdout + if err := cmd.Run(); err != nil { + t.Fatal("failed to run bazel build: ", err) + } + +} + +func mustReplaceInFile(t *testing.T, path, old, new string) { + t.Helper() + if old == new { + return + } + data, err := os.ReadFile(path) + if err != nil { + t.Fatal(err) + } + if !bytes.Contains(data, []byte(old)) { + t.Fatalf("bytes to replace %q not found in file %q with contents, %q", old, path, data) + } + data = bytes.ReplaceAll(data, []byte(old), []byte(new)) + if err := os.WriteFile(path, data, 0666); err != nil { + t.Fatal(err) + } +} diff --git a/tests/core/c_linkmodes/BUILD.bazel b/tests/core/c_linkmodes/BUILD.bazel new file mode 100644 index 00000000..ee7f68e6 --- /dev/null +++ b/tests/core/c_linkmodes/BUILD.bazel @@ -0,0 +1,139 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary") + +go_binary( + name = "adder_archive", + srcs = ["add.go"], + cgo = True, + linkmode = "c-archive", + tags = ["manual"], +) + +cc_test( + name = "c-archive_test", + srcs = select({ + "@io_bazel_rules_go//go/platform:windows": ["skip.c"], + "//conditions:default": ["add_test_archive.c"], + }), + deps = select({ + "@io_bazel_rules_go//go/platform:windows": [], + "//conditions:default": [":adder_archive"], + }), +) + +go_binary( + name = "c-archive_empty_hdr", + srcs = ["empty.go"], + cgo = True, + linkmode = "c-archive", + tags = ["manual"], +) + +cc_test( + name = "c-archive_empty_hdr_test", + srcs = select({ + "@io_bazel_rules_go//go/platform:windows": ["skip.c"], + "//conditions:default": ["c-archive_empty_hdr_test.c"], + }), + deps = select({ + "@io_bazel_rules_go//go/platform:windows": [], + "//conditions:default": [":c-archive_empty_hdr"], + }), +) + +go_binary( + name = "adder_shared", + srcs = ["add.go"], + cgo = True, + linkmode = "c-shared", + tags = ["manual"], +) + +cc_test( + name = "c-shared_test", + srcs = select({ + "@io_bazel_rules_go//go/platform:windows": ["skip.c"], + "//conditions:default": ["add_test_shared.c"], + }), + deps = select({ + "@io_bazel_rules_go//go/platform:windows": [], + "//conditions:default": [":adder_shared"], + }), +) + +go_binary( + name = "crypto", + srcs = [":crypto.go"], + cgo = True, + linkmode = "c-shared", + tags = ["manual"], + deps = ["@org_golang_x_crypto//nacl/box:go_default_library"], +) + +cc_test( + name = "c-shared_dl_test", + srcs = select({ + "@io_bazel_rules_go//go/platform:windows": ["skip.c"], + "//conditions:default": ["crypto_test_dl.c"], + }), + copts = select({ + "@io_bazel_rules_go//go/platform:windows": [], + "//conditions:default": ['-DSO=\\"$(rootpath :crypto)\\"'], + }), + data = select({ + "@io_bazel_rules_go//go/platform:windows": [], + "//conditions:default": [":crypto"], + }), + linkopts = select({ + "@io_bazel_rules_go//go/platform:windows": [], + "//conditions:default": ["-ldl"], + }), +) + +cc_library( + name = "adder_sandwich_cc", + srcs = ["add_sandwich.c"], + hdrs = ["add_sandwich.h"], + linkstatic = True, + alwayslink = True, +) + +go_binary( + name = "adder_sandwich_archive", + srcs = ["add_sandwich.go"], + cdeps = [":adder_sandwich_cc"], + cgo = True, + linkmode = "c-archive", + tags = ["manual"], +) + +cc_test( + name = "c-archive_sandwich_test", + srcs = select({ + "@io_bazel_rules_go//go/platform:windows": ["skip.c"], + "//conditions:default": ["add_test_sandwich.c"], + }), + deps = select({ + "@io_bazel_rules_go//go/platform:windows": [], + "//conditions:default": [":adder_sandwich_archive"], + }), +) + +go_binary( + name = "go_with_cgo_dep", + srcs = select({ + "@io_bazel_rules_go//go/platform:windows": ["empty.go"], + "//conditions:default": ["go_with_cgo_dep.go"], + }), + cgo = True, + linkmode = "c-archive", + deps = ["@org_golang_x_sys//unix"], +) + +cc_binary( + name = "go_with_cgo_dep_caller", + srcs = select({ + "@io_bazel_rules_go//go/platform:windows": ["skip.c"], + "//conditions:default": ["go_with_cgo_dep_caller.cc"], + }), + deps = [":go_with_cgo_dep"], +) diff --git a/tests/core/c_linkmodes/README.rst b/tests/core/c_linkmodes/README.rst new file mode 100644 index 00000000..dd517725 --- /dev/null +++ b/tests/core/c_linkmodes/README.rst @@ -0,0 +1,35 @@ +c-archive / c-shared linkmodes +============================== + +.. _go_binary: /docs/go/core/rules.md#go_binary +.. _#2132: https://github.com/bazelbuild/rules_go/issues/2132 +.. _#2138: https://github.com/bazelbuild/rules_go/issues/2138 + +Tests to ensure that c-archive link mode is working as expected. + +.. contents:: + +c-archive_test +-------------- + +Checks that a ``go_binary`` can be built in ``c-archive`` mode and linked into +a C/C++ binary as a dependency. + +c-archive_empty_hdr_test +------------------------ + +Checks that a ``go_binary`` built with in ``c-archive`` mode without cgo code +still produces an empty header file. Verifies `#2132`_. + +c-shared_test +------------- + +Checks that a ``go_binary`` can be built in ``c-shared`` mode and linked into +a C/C++ binary as a dependency. + +c-shared_dl_test +---------------- + +Checks that a ``go_binary`` can be built in ``c-shared`` mode and loaded +dynamically from a C/C++ binary. The binary depends on a package in +``org_golang_x_crypto`` with a fair amount of assembly code. Verifies `#2138`_. diff --git a/tests/core/c_linkmodes/add.go b/tests/core/c_linkmodes/add.go new file mode 100644 index 00000000..35084fea --- /dev/null +++ b/tests/core/c_linkmodes/add.go @@ -0,0 +1,11 @@ +package main + +// #define CGO_EXPORT_H_EXISTS +import "C" + +//export GoAdd +func GoAdd(a, b int) int { + return a + b +} + +func main() {} diff --git a/tests/core/c_linkmodes/add_sandwich.c b/tests/core/c_linkmodes/add_sandwich.c new file mode 100644 index 00000000..dcf6150f --- /dev/null +++ b/tests/core/c_linkmodes/add_sandwich.c @@ -0,0 +1,5 @@ +#include "add_sandwich.h" + +int add(int a, int b) { + return a + b; +} diff --git a/tests/core/c_linkmodes/add_sandwich.go b/tests/core/c_linkmodes/add_sandwich.go new file mode 100644 index 00000000..0073638f --- /dev/null +++ b/tests/core/c_linkmodes/add_sandwich.go @@ -0,0 +1,14 @@ +package main + +/* +#define CGO_EXPORT_H_EXISTS +#include "tests/core/c_linkmodes/add_sandwich.h" +*/ +import "C" + +//export GoAdd +func GoAdd(a, b int) int { + return int(C.add(C.int(a), C.int(b))) +} + +func main() {} diff --git a/tests/core/c_linkmodes/add_sandwich.h b/tests/core/c_linkmodes/add_sandwich.h new file mode 100644 index 00000000..6295ab95 --- /dev/null +++ b/tests/core/c_linkmodes/add_sandwich.h @@ -0,0 +1 @@ +int add(int a, int b); diff --git a/tests/core/c_linkmodes/add_test_archive.c b/tests/core/c_linkmodes/add_test_archive.c new file mode 100644 index 00000000..1e585a42 --- /dev/null +++ b/tests/core/c_linkmodes/add_test_archive.c @@ -0,0 +1,11 @@ +#include <assert.h> +#include "tests/core/c_linkmodes/adder_archive.h" + +#ifndef CGO_EXPORT_H_EXISTS +#error cgo header did not include define +#endif + +int main(int argc, char** argv) { + assert(GoAdd(42, 42) == 84); + return 0; +} diff --git a/tests/core/c_linkmodes/add_test_sandwich.c b/tests/core/c_linkmodes/add_test_sandwich.c new file mode 100644 index 00000000..26efff70 --- /dev/null +++ b/tests/core/c_linkmodes/add_test_sandwich.c @@ -0,0 +1,11 @@ +#include <assert.h> +#include "tests/core/c_linkmodes/adder_sandwich_archive.h" + +#ifndef CGO_EXPORT_H_EXISTS +#error cgo header did not include define +#endif + +int main(int argc, char** argv) { + assert(GoAdd(42, 42) == 84); + return 0; +} diff --git a/tests/core/c_linkmodes/add_test_shared.c b/tests/core/c_linkmodes/add_test_shared.c new file mode 100644 index 00000000..2f57712e --- /dev/null +++ b/tests/core/c_linkmodes/add_test_shared.c @@ -0,0 +1,11 @@ +#include <assert.h> +#include "tests/core/c_linkmodes/adder_shared.h" + +#ifndef CGO_EXPORT_H_EXISTS +#error cgo header did not include define +#endif + +int main(int argc, char** argv) { + assert(GoAdd(42, 42) == 84); + return 0; +} diff --git a/tests/core/c_linkmodes/c-archive_empty_hdr_test.c b/tests/core/c_linkmodes/c-archive_empty_hdr_test.c new file mode 100644 index 00000000..63ff5dfc --- /dev/null +++ b/tests/core/c_linkmodes/c-archive_empty_hdr_test.c @@ -0,0 +1,17 @@ +// 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. + +#include "tests/core/c_linkmodes/c-archive_empty_hdr.h" + +int main() { return 0; } diff --git a/tests/core/c_linkmodes/crypto.go b/tests/core/c_linkmodes/crypto.go new file mode 100644 index 00000000..a362c7ee --- /dev/null +++ b/tests/core/c_linkmodes/crypto.go @@ -0,0 +1,18 @@ +package main + +import "C" + +import ( + "crypto/rand" + + "golang.org/x/crypto/nacl/box" +) + +//export GoFn +func GoFn() { + box.GenerateKey(rand.Reader) + return +} + +func main() { +} diff --git a/tests/core/c_linkmodes/crypto_test_dl.c b/tests/core/c_linkmodes/crypto_test_dl.c new file mode 100644 index 00000000..36e21495 --- /dev/null +++ b/tests/core/c_linkmodes/crypto_test_dl.c @@ -0,0 +1,30 @@ +#include <dlfcn.h> +#include <stdio.h> + +#ifndef SO +#error No SO path defined +#endif + +int main() { + void* handle = dlopen(SO, RTLD_NOW); + if (!handle) { + printf("dlopen: %s\n", dlerror()); + return 1; + } + + typedef void (*gofn_t)(); + gofn_t gofn = (gofn_t)dlsym(handle, "GoFn"); + const char* dlsym_error = dlerror(); + if (dlsym_error) { + printf("dlsym: %s\n", dlerror()); + dlclose(handle); + return 1; + } + + gofn(); + + if (dlclose(handle)) { + printf("dlclose: %s\n", dlerror()); + } + return 0; +} diff --git a/tests/core/c_linkmodes/empty.go b/tests/core/c_linkmodes/empty.go new file mode 100644 index 00000000..38dd16da --- /dev/null +++ b/tests/core/c_linkmodes/empty.go @@ -0,0 +1,3 @@ +package main + +func main() {} diff --git a/tests/core/c_linkmodes/go_with_cgo_dep.go b/tests/core/c_linkmodes/go_with_cgo_dep.go new file mode 100644 index 00000000..cfaeb31f --- /dev/null +++ b/tests/core/c_linkmodes/go_with_cgo_dep.go @@ -0,0 +1,29 @@ +// Copyright 2021 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 main + +import "C" +import ( + "fmt" + + "golang.org/x/sys/unix" +) + +//export UnixHello +func UnixHello() { + fmt.Printf("Hello, %d\n", unix.Getuid()) +} + +func main() {} diff --git a/tests/core/c_linkmodes/go_with_cgo_dep_caller.cc b/tests/core/c_linkmodes/go_with_cgo_dep_caller.cc new file mode 100644 index 00000000..cc771076 --- /dev/null +++ b/tests/core/c_linkmodes/go_with_cgo_dep_caller.cc @@ -0,0 +1,20 @@ +// Copyright 2021 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. + +#include "tests/core/c_linkmodes/go_with_cgo_dep.h" + +int main() { + UnixHello(); + return 0; +} diff --git a/tests/core/c_linkmodes/skip.c b/tests/core/c_linkmodes/skip.c new file mode 100644 index 00000000..33c14ce1 --- /dev/null +++ b/tests/core/c_linkmodes/skip.c @@ -0,0 +1,3 @@ +int main() { + return 0; +} diff --git a/tests/core/cgo/BUILD.bazel b/tests/core/cgo/BUILD.bazel new file mode 100644 index 00000000..cc12203f --- /dev/null +++ b/tests/core/cgo/BUILD.bazel @@ -0,0 +1,474 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") +load("@io_bazel_rules_go//go/tools/bazel_testing:def.bzl", "go_bazel_test") + +go_test( + name = "opts_test", + srcs = ["adder_test.go"], + embed = [":opts"], +) + +genrule( + name = "generate_header_copts", + outs = ["generated_copts/generated_copts.h"], + cmd = "echo '#define GENERATED_COPTS 1' >$@", +) + +genrule( + name = "generate_header_cppopts", + outs = ["generated_cppopts/generated_cppopts.h"], + cmd = "echo '#define GENERATED_CPPOPTS 1' >$@", +) + +genrule( + name = "generate_header_cxxopts", + outs = ["generated_cxxopts/generated_cxxopts.h"], + cmd = "echo '#define GENERATED_CXXOPTS 1' >$@", +) + +cc_library( + name = "generated_headers", + hdrs = [ + "generated_copts/generated_copts.h", + "generated_cppopts/generated_cppopts.h", + "generated_cxxopts/generated_cxxopts.h", + ], +) + +COPTS_INCLUDE_PREFIX = (package_name() if repository_name() == "@" else "external/%s/%s" % ( + repository_name()[1:], + package_name(), +)) + +go_library( + name = "opts", + srcs = [ + "add.c", + "add.cpp", + "add.h", + "adder.go", + ] + select({ + "@io_bazel_rules_go//go/platform:darwin": [ + "add.m", + "add.mm", + ], + "//conditions:default": [], + }), + cdeps = [":generated_headers"], + cgo = True, + copts = [ + "-DRULES_GO_C", + "-I$(GENDIR)/%s/generated_copts" % COPTS_INCLUDE_PREFIX, + "-DDOLLAR_SIGN_C=$$", # the dollar sign should be escaped + ], + cppopts = [ + "-DRULES_GO_CPP", + "-I$(GENDIR)/%s/generated_cppopts" % COPTS_INCLUDE_PREFIX, + "-DDOLLAR_SIGN_CPP=$$", # the dollar sign should be escaped + ], + cxxopts = [ + "-DRULES_GO_CXX", + "-I$(GENDIR)/%s/generated_cxxopts" % COPTS_INCLUDE_PREFIX, + "-DDOLLAR_SIGN_CXX=$$", # the dollar sign should be escaped + ], + importpath = "github.com/bazelbuild/rules_go/tests/core/cxx", +) + +go_test( + name = "dylib_test", + srcs = ["dylib_test.go"], + embed = [":dylib_client"], + rundir = ".", + tags = ["manual"], # //tests/core/cgo:generate_imported_dylib.sh must be run first +) + +go_library( + name = "dylib_client", + srcs = ["dylib_client.go"], + cdeps = select({ + "@io_bazel_rules_go//go/platform:darwin": [":darwin_imported_dylib"], + "//conditions:default": [":linux_imported_dylib"], + # TODO(jayconrod): Support windows, skip others. + }), + cgo = True, + importpath = "github.com/bazelbuild/rules_go/tests/core/cgo/dylib", + tags = ["manual"], +) + +cc_import( + name = "darwin_imported_dylib", + shared_library = "libimported.dylib", + tags = ["manual"], +) + +cc_import( + name = "linux_imported_dylib", + shared_library = ":libimported.so", + tags = ["manual"], +) + +go_test( + name = "generated_dylib_test", + srcs = ["dylib_test.go"], + embed = [":generated_dylib_client"], + rundir = ".", +) + +go_library( + name = "generated_dylib_client", + srcs = ["dylib_client.go"], + cdeps = select({ + "@io_bazel_rules_go//go/platform:darwin": [":darwin_imported_generated_dylib"], + "//conditions:default": [":linux_imported_generated_dylib"], + # TODO(jayconrod): Support windows, skip others. + }), + cgo = True, + importpath = "github.com/bazelbuild/rules_go/tests/core/cgo/dylib", +) + +cc_import( + name = "darwin_imported_generated_dylib", + shared_library = ":libimported_generated.dylib", + tags = ["manual"], +) + +cc_binary( + name = "libimported_generated.dylib", + srcs = ["imported.c"], + linkopts = ["-Wl,-install_name,@rpath/libimported_generated.dylib"], + linkshared = True, + tags = ["manual"], +) + +cc_import( + name = "linux_imported_generated_dylib", + shared_library = ":libimported_generated.so", + tags = ["manual"], +) + +cc_binary( + name = "libimported_generated.so", + srcs = ["imported.c"], + linkshared = True, + tags = ["manual"], +) + +# //tests/core/cgo:generate_imported_dylib.sh must be run first +go_test( + name = "versioned_dylib_test", + srcs = ["dylib_test.go"], + embed = [":versioned_dylib_client"], + rundir = ".", + target_compatible_with = select({ + "@platforms//os:osx": [], + "@platforms//os:linux": [], + "//conditions:default": ["@platforms//:incompatible"], + }), +) + +go_library( + name = "versioned_dylib_client", + srcs = ["dylib_client.go"], + cdeps = select({ + "@io_bazel_rules_go//go/platform:darwin": [":darwin_imported_versioned_dylib"], + "//conditions:default": [":linux_imported_versioned_dylib"], + # TODO(jayconrod): Support windows, skip others. + }), + cgo = True, + importpath = "github.com/bazelbuild/rules_go/tests/core/cgo/dylib", + tags = ["manual"], +) + +cc_import( + name = "linux_imported_versioned_dylib", + shared_library = "libversioned.so.2", + tags = ["manual"], +) + +cc_import( + name = "darwin_imported_versioned_dylib", + shared_library = "libversioned.2.dylib", + tags = ["manual"], +) + +go_test( + name = "oracle_convention_darwin_dylib_test", + srcs = ["dylib_test.go"], + embed = [":oracle_convention_darwin_dylib_client"], + rundir = ".", + target_compatible_with = ["@platforms//os:macos"], +) + +go_library( + name = "oracle_convention_darwin_dylib_client", + srcs = ["dylib_client.go"], + cdeps = [":oracle_convention_darwin_dylib"], + cgo = True, + importpath = "github.com/bazelbuild/rules_go/tests/core/cgo/dylib", + target_compatible_with = ["@platforms//os:macos"], +) + +# //tests/core/cgo:generate_imported_dylib.sh must be run first +cc_library( + name = "oracle_convention_darwin_dylib", + srcs = [ + "libversioned.dylib", + "libversioned.dylib.2", + ], + target_compatible_with = ["@platforms//os:macos"], +) + +go_test( + name = "generated_versioned_dylib_test", + srcs = ["dylib_test.go"], + embed = [":generated_versioned_dylib_client"], + rundir = ".", +) + +go_library( + name = "generated_versioned_dylib_client", + srcs = ["dylib_client.go"], + cdeps = select({ + # This test exists just for versioned `.so`s on Linux, + # but we can reuse the above test's dylib so it passes on darwin, + # where filename suffixes are not used for library version. + "@io_bazel_rules_go//go/platform:darwin": [":darwin_imported_generated_dylib"], + "//conditions:default": [":linux_imported_generated_versioned_dylib"], + # TODO(jayconrod): Support windows, skip others. + }), + cgo = True, + importpath = "github.com/bazelbuild/rules_go/tests/core/cgo/dylib", +) + +cc_library( + name = "linux_imported_generated_versioned_dylib", + srcs = [":libimported_generated.so.2"], + linkstatic = False, + tags = ["manual"], +) + +cc_binary( + name = "libimported_generated.so.2", + srcs = ["imported.c"], + linkshared = True, + tags = ["manual"], +) + +go_test( + name = "cc_libs_test", + srcs = [ + "cc_libs_common.go", + "cc_libs_darwin_test.go", + "cc_libs_linux_test.go", + ], + data = [ + ":c_srcs", + ":cc_deps", + ":cc_srcs", + ":pure", + ], + deps = ["//go/tools/bazel:go_default_library"], +) + +go_binary( + name = "pure", + srcs = ["pure.go"], + out = "pure_bin", + cgo = True, + pure = "on", +) + +go_binary( + name = "c_srcs", + srcs = [ + "foo.c", + "foo.go", + ], + out = "c_srcs_bin", + cgo = True, +) + +go_binary( + name = "cc_srcs", + srcs = [ + "bar.cc", + "bar.go", + ], + out = "cc_srcs_bin", + cgo = True, +) + +go_binary( + name = "cc_deps", + srcs = ["bar.go"], + out = "cc_deps_bin", + cdeps = [":bar_dep"], + cgo = True, +) + +cc_library( + name = "bar_dep", + srcs = ["bar.cc"], +) + +go_test( + name = "race_test", + srcs = [ + "race_off.c", + "race_off.go", + "race_on.c", + "race_on.go", + "race_test.go", + ], + cgo = True, + race = "on", +) + +go_test( + name = "tag_test", + srcs = ["tag_test.go"], + data = [ + ":tag_cgo_bin", + ":tag_pure_bin", + ], + rundir = ".", + deps = ["//go/tools/bazel:go_default_library"], +) + +go_binary( + name = "tag_pure_bin", + srcs = [ + "tag_pure.go", + "tag_pure_err.c", + "tag_pure_err.go", + ], + cgo = True, + pure = "on", +) + +go_binary( + name = "tag_cgo_bin", + srcs = [ + "tag_cgo.go", + "tag_cgo_err.go", + ], + cgo = True, + pure = "off", +) + +go_test( + name = "cgo_link_test", + srcs = [ + "cgo_link_test.go", + "cgo_ref.go", + ], + cdeps = [":cgo_link_dep"], + cgo = True, +) + +cc_library( + name = "cgo_link_dep", + srcs = ["cgo_link_dep.c"], +) + +go_test( + name = "split_import_test", + srcs = [ + "split_import_i_test.go", + "split_import_x_test.go", + ], + embed = [":split_import_a"], + deps = [":split_import_b"], +) + +go_library( + name = "split_import_a", + srcs = ["split_import_a.go"], + importpath = "github.com/bazelbuild/rules_go/tests/core/cgo/split_import/a", +) + +go_library( + name = "split_import_b", + srcs = ["split_import_b.go"], + importpath = "github.com/bazelbuild/rules_go/tests/core/cgo/split_import/b", + deps = [ + ":split_import_a", + ":split_import_cgo", + ], +) + +go_library( + name = "split_import_cgo", + srcs = ["split_import_cgo.go"], + cdeps = [":split_import_c"], + cgo = True, + importpath = "github.com/bazelbuild/rules_go/tests/core/cgo/split_import/cgo", +) + +cc_library( + name = "split_import_c", + srcs = ["split_import_c.c"], + hdrs = ["split_import_c.h"], +) + +go_bazel_test( + name = "external_includes_test", + srcs = ["external_includes_test.go"], +) + +go_library( + name = "use_external_symbol", + srcs = ["use_external_symbol.go"], + cgo = True, + importpath = "github.com/bazelbuild/rules_go/tests/core/cgo/use_external_symbol", + tags = ["manual"], +) + +go_binary( + name = "provide_external_symbol", + srcs = ["provide_external_symbol.go"], + cgo = True, + target_compatible_with = select({ + "@platforms//os:osx": [], + "@platforms//os:linux": [], + "//conditions:default": ["@platforms//:incompatible"], + }), + deps = [":use_external_symbol"], +) + +cc_library( + name = "native_dep", + srcs = ["native_dep.c"], + hdrs = ["native_dep.h"], + # Force static linking to ensure that the build doesn't succeed by + # accidentally picking up the shared library in the search path. + linkstatic = True, +) + +go_library( + name = "transitive_dep", + srcs = ["transitive_dep.go"], + cdeps = [":native_dep"], + cgo = True, + importpath = "github.com/bazelbuild/rules_go/tests/core/cgo/transitive_dep", +) + +go_library( + name = "direct_dep", + srcs = ["direct_dep.go"], + importpath = "github.com/bazelbuild/rules_go/tests/core/cgo/direct_dep", + deps = [":transitive_dep"], +) + +go_binary( + name = "use_transitive_symbol", + srcs = ["use_transitive_symbol.go"], + cgo = True, + linkmode = "c-archive", + deps = [":direct_dep"], +) + +cc_binary( + name = "use_c_symbol_through_go", + srcs = ["use_c_symbol_through_go.c"], + deps = [":use_transitive_symbol"], +) diff --git a/tests/core/cgo/README.rst b/tests/core/cgo/README.rst new file mode 100644 index 00000000..a385cab3 --- /dev/null +++ b/tests/core/cgo/README.rst @@ -0,0 +1,67 @@ +.. _#2067: https://github.com/bazelbuild/rules_go/issues/2067 +.. _#2622: https://github.com/bazelbuild/rules_go/issues/2622 + +Basic cgo functionality +======================= + +opts_test +--------- + +Checks that different sets of options are passed to C and C++ sources in a +``go_library`` with ``cgo = True``. + +(generated_)?(versioned_)?dylib_test +------------------------------------ + +Checks that Go binaries can link against dynamic C libraries. Some libraries +(especially those provided with ``cc_import``) may only have dynamic versions, +and we should be able to link against them and find them at run-time. + +The non ``generated_`` tests are manual. The ``generate_imported_dylib.sh`` +script must be run before running the tests themselves. + +The ``generated_`` variants check that Go binaries can link against dynamic C +libraries that are generated by another rule, rather than being included in the +source tree. + +The ``versioned_`` variants check that Go binaries can link against dynamic C +libraries that are only available as a versioned shared library, like +``libfoo.so.1``, as used on Linux. + +cc_libs_test +------------ + +Checks that Go binaries that include cgo code may or may not link against +libstdc++, depending on how they're linked. This tests several binaries: + +* ``pure_bin`` - built in ``"pure"`` mode, should not depend on libstdc++. +* ``c_srcs`` - has no C++ code in sources, should not depend on libstdc++. +* ``cc_srcs`` - has some C++ code in sources, should depend on libstdc++. +* ``cc_deps`` - depends on a ``cc_library``, should depend on libstdc++ + because we don't know what's in it. + +race_test +--------- + +Checks that cgo code in a binary with ``race = "on"`` is compiled in race mode. +Verifies #1592. + +tag_test +-------- + +Checks that sources with ``// +build cgo`` are built when cgo is enabled +(whether or not ``cgo = True`` is set), and sources with ``// +build !cgo`` +are only built in pure mode. + +cdeps_link_test +--------------- + +Checks that libraries in ``cdeps`` are linked into the generated ``_cgo_.o`` +executable used to produce ``_cgo_imports.go``. Verifies `#2067`_. + +split_import_test +----------------- + +Checks that when a package with ``cdeps`` is recompiled due to a split test, +the input files from ``cdeps`` are included in the recompilation and are passed +to the linker. Verifies `#2622`_. diff --git a/tests/core/cgo/add.c b/tests/core/cgo/add.c new file mode 100644 index 00000000..fba09af2 --- /dev/null +++ b/tests/core/cgo/add.c @@ -0,0 +1,19 @@ +#include <add.h> +#include <generated_cppopts.h> +#include <generated_copts.h> + +#if !defined(RULES_GO_C) || !defined(RULES_GO_CPP) || defined(RULES_GO_CXX) +#error This is a C file, only RULES_GO_C and RULES_GO_CPP should be defined. +#endif + +#if !defined(GENERATED_COPTS) || !defined(GENERATED_CPPOPTS) || defined(GENERATED_CXXOPTS) +#error Generated headers should be correctly included +#endif + +int add_c(int a, int b) { + int $ = 0; + int sum = a + b; + sum += DOLLAR_SIGN_C; + sum += DOLLAR_SIGN_CPP; + return sum; +} diff --git a/tests/core/cgo/add.cpp b/tests/core/cgo/add.cpp new file mode 100644 index 00000000..e5dbc0a9 --- /dev/null +++ b/tests/core/cgo/add.cpp @@ -0,0 +1,19 @@ +#include "add.h" +#include <generated_cppopts.h> +#include <generated_cxxopts.h> + +#if !defined(RULES_GO_CPP) || !defined(RULES_GO_CXX) || defined(RULES_GO_C) +#error This is a C++ file, only RULES_GO_CXX and RULES_GO_CPP should be defined. +#endif + +#if !defined(GENERATED_CPPOPTS) || !defined(GENERATED_CXXOPTS) || defined(GENERATED_COPTS) +#error Generated headers should be correctly included +#endif + +int add_cpp(int a, int b) { + int $ = 0; + int sum = a + b; + sum += DOLLAR_SIGN_CXX; + sum += DOLLAR_SIGN_CPP; + return sum; +} diff --git a/tests/core/cgo/add.h b/tests/core/cgo/add.h new file mode 100644 index 00000000..eb2c7c77 --- /dev/null +++ b/tests/core/cgo/add.h @@ -0,0 +1,10 @@ +#ifdef __cplusplus +extern "C" { +#endif + +int add_c(int a, int b); +int add_cpp(int a, int b); + +#ifdef __cplusplus +} +#endif diff --git a/tests/core/cgo/add.m b/tests/core/cgo/add.m new file mode 100644 index 00000000..5a321b29 --- /dev/null +++ b/tests/core/cgo/add.m @@ -0,0 +1,11 @@ +#include "add.h" +#include <generated_cppopts.h> +#include <generated_copts.h> + +#if !defined(RULES_GO_C) || !defined(RULES_GO_CPP) || defined(RULES_GO_CXX) +#error This is an Objective-C file, only RULES_GO_C and RULES_GO_CPP should be defined. +#endif + +#if !defined(GENERATED_COPTS) || !defined(GENERATED_CPPOPTS) || defined(GENERATED_CXXOPTS) +#error Generated headers should be correctly included +#endif diff --git a/tests/core/cgo/add.mm b/tests/core/cgo/add.mm new file mode 100644 index 00000000..aed54ab5 --- /dev/null +++ b/tests/core/cgo/add.mm @@ -0,0 +1,11 @@ +#include "add.h" +#include <generated_cppopts.h> +#include <generated_cxxopts.h> + +#if !defined(RULES_GO_CPP) || !defined(RULES_GO_CXX) || defined(RULES_GO_C) +#error This is an Objective-C++ file, only RULES_GO_CXX and RULES_GO_CPP should be defined. +#endif + +#if !defined(GENERATED_CPPOPTS) || !defined(GENERATED_CXXOPTS) || defined(GENERATED_COPTS) +#error Generated headers should be correctly included +#endif diff --git a/tests/core/cgo/adder.go b/tests/core/cgo/adder.go new file mode 100644 index 00000000..e8e2f403 --- /dev/null +++ b/tests/core/cgo/adder.go @@ -0,0 +1,14 @@ +package objc + +/* +#include "add.h" +*/ +import "C" + +func AddC(a, b int32) int32 { + return int32(C.add_c(C.int(a), C.int(b))) +} + +func AddCPP(a, b int32) int32 { + return int32(C.add_cpp(C.int(a), C.int(b))) +} diff --git a/tests/core/cgo/adder_test.go b/tests/core/cgo/adder_test.go new file mode 100644 index 00000000..fc9bf451 --- /dev/null +++ b/tests/core/cgo/adder_test.go @@ -0,0 +1,19 @@ +package objc + +import ( + "fmt" + "math/rand" + "testing" +) + +func TestCPPAdder(t *testing.T) { + a := rand.Int31() + b := rand.Int31() + expected := a + b + if result := AddC(a, b); result != expected { + t.Error(fmt.Errorf("wrong result: got %d, expected %d", result, expected)) + } + if result := AddCPP(a, b); result != expected { + t.Error(fmt.Errorf("wrong result: got %d, expected %d", result, expected)) + } +} diff --git a/tests/core/cgo/bar.cc b/tests/core/cgo/bar.cc new file mode 100644 index 00000000..d72f5123 --- /dev/null +++ b/tests/core/cgo/bar.cc @@ -0,0 +1,9 @@ +#include <iostream> + +extern "C" { + +void bar() { + std::cout << "bar" << std::endl; +} + +} diff --git a/tests/core/cgo/bar.go b/tests/core/cgo/bar.go new file mode 100644 index 00000000..92562dd4 --- /dev/null +++ b/tests/core/cgo/bar.go @@ -0,0 +1,8 @@ +package main + +// void bar(); +import "C" + +func main() { + C.bar() +} diff --git a/tests/core/cgo/cc_libs_common.go b/tests/core/cgo/cc_libs_common.go new file mode 100644 index 00000000..40a04ed5 --- /dev/null +++ b/tests/core/cgo/cc_libs_common.go @@ -0,0 +1,34 @@ +package cc_libs_test + +import ( + "bytes" + "github.com/bazelbuild/rules_go/go/tools/bazel" + "io/ioutil" + "testing" +) + +// A distinctive substring contained in every absolute path pointing into the +// Bazel cache. +const execPathIndicator = "/execroot/io_bazel_rules_go" + +func verifyNoCachePaths(t *testing.T, shortPath string) { + binPath, err := bazel.Runfile(shortPath) + if err != nil { + t.Error(err) + } + binBytes, err := ioutil.ReadFile(binPath) + if err != nil { + t.Error(err) + } + if pos := bytes.Index(binBytes, []byte(execPathIndicator)); pos != -1 { + begin := pos - 150 + if begin < 0 { + begin = 0 + } + end := pos + 150 + if end > len(binBytes) { + end = len(binBytes) + } + t.Errorf("%s leaks an absolute path:\n%q", shortPath, binBytes[begin:end]) + } +} diff --git a/tests/core/cgo/cc_libs_darwin_test.go b/tests/core/cgo/cc_libs_darwin_test.go new file mode 100644 index 00000000..4201dbf2 --- /dev/null +++ b/tests/core/cgo/cc_libs_darwin_test.go @@ -0,0 +1,96 @@ +// Copyright 2018 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 cc_libs_test + +import ( + "debug/macho" + "path" + "strings" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel" +) + +func TestBinaries(t *testing.T) { + for _, test := range []struct { + shortPath string + wantLibs map[string]bool + }{ + { + shortPath: "tests/core/cgo/pure_bin", + // Since go1.11, pure binaries have a dependency on libSystem. + // This is true with go tool link -linkmode=internal + // and with CGO_ENABLED=0 go build ... + wantLibs: map[string]bool{"libc++": false}, + }, { + shortPath: "tests/core/cgo/c_srcs_bin", + wantLibs: map[string]bool{"libSystem": true, "libc++": false}, + }, { + shortPath: "tests/core/cgo/cc_srcs_bin", + wantLibs: map[string]bool{"libSystem": true, "libc++": true}, + }, { + shortPath: "tests/core/cgo/cc_deps_bin", + wantLibs: map[string]bool{"libSystem": true, "libc++": true}, + }, + } { + t.Run(path.Base(test.shortPath), func(t *testing.T) { + libs, err := listLibs(test.shortPath) + if err != nil { + t.Fatal(err) + } + haveLibs := make(map[string]bool) + for _, lib := range libs { + haveLibs[lib] = true + } + for haveLib := range haveLibs { + if wantLib, ok := test.wantLibs[haveLib]; ok && !wantLib { + t.Errorf("unexpected dependency on library %q", haveLib) + } + } + for wantLib, want := range test.wantLibs { + if want && !haveLibs[wantLib] { + t.Errorf("wanted dependency on library %q", wantLib) + } + } + + verifyNoCachePaths(t, test.shortPath) + }) + } +} + +func listLibs(shortPath string) ([]string, error) { + binPath, err := bazel.Runfile(shortPath) + if err != nil { + return nil, err + } + f, err := macho.Open(binPath) + if err != nil { + return nil, err + } + defer f.Close() + libs, err := f.ImportedLibraries() + if err != nil { + return nil, err + } + for i := range libs { + if pos := strings.LastIndexByte(libs[i], '/'); pos >= 0 { + libs[i] = libs[i][pos+1:] + } + if pos := strings.IndexByte(libs[i], '.'); pos >= 0 { + libs[i] = libs[i][:pos] + } + } + return libs, nil +} diff --git a/tests/core/cgo/cc_libs_linux_test.go b/tests/core/cgo/cc_libs_linux_test.go new file mode 100644 index 00000000..204a8fcf --- /dev/null +++ b/tests/core/cgo/cc_libs_linux_test.go @@ -0,0 +1,93 @@ +// Copyright 2018 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 cc_libs_test + +import ( + "debug/elf" + "path" + "strings" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel" +) + +func TestBinaries(t *testing.T) { + for _, test := range []struct { + shortPath string + wantLibs map[string]bool + }{ + { + shortPath: "tests/core/cgo/pure_bin", + wantLibs: map[string]bool{"libc": false, "libstdc++": false}, + }, { + shortPath: "tests/core/cgo/c_srcs_bin", + wantLibs: map[string]bool{"libc": true, "libstdc++": false}, + }, { + shortPath: "tests/core/cgo/cc_srcs_bin", + wantLibs: map[string]bool{"libc": true, "libstdc++": true}, + }, { + shortPath: "tests/core/cgo/cc_deps_bin", + wantLibs: map[string]bool{"libc": true, "libstdc++": true}, + }, + } { + t.Run(path.Base(test.shortPath), func(t *testing.T) { + libs, err := listLibs(test.shortPath) + if err != nil { + t.Fatal(err) + } + haveLibs := make(map[string]bool) + for _, lib := range libs { + haveLibs[lib] = true + } + for haveLib := range haveLibs { + if wantLib, ok := test.wantLibs[haveLib]; ok && !wantLib { + t.Errorf("unexpected dependency on library %q", haveLib) + } + } + for wantLib, want := range test.wantLibs { + if want && !haveLibs[wantLib] { + t.Errorf("wanted dependency on library %q", wantLib) + } + } + + verifyNoCachePaths(t, test.shortPath) + }) + } +} + +func listLibs(shortPath string) ([]string, error) { + binPath, err := bazel.Runfile(shortPath) + if err != nil { + return nil, err + } + f, err := elf.Open(binPath) + if err != nil { + return nil, err + } + defer f.Close() + libs, err := f.ImportedLibraries() + if err != nil { + return nil, err + } + for i := range libs { + if pos := strings.LastIndexByte(libs[i], '/'); pos >= 0 { + libs[i] = libs[i][pos+1:] + } + if pos := strings.IndexByte(libs[i], '.'); pos >= 0 { + libs[i] = libs[i][:pos] + } + } + return libs, nil +} diff --git a/tests/core/cgo/cgo_link_dep.c b/tests/core/cgo/cgo_link_dep.c new file mode 100644 index 00000000..c102d587 --- /dev/null +++ b/tests/core/cgo/cgo_link_dep.c @@ -0,0 +1,15 @@ +// 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. + +int f() { return 42; } diff --git a/tests/core/cgo/cgo_link_test.go b/tests/core/cgo/cgo_link_test.go new file mode 100644 index 00000000..fcd3d00f --- /dev/null +++ b/tests/core/cgo/cgo_link_test.go @@ -0,0 +1,24 @@ +// 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 cgo_link + +import "testing" + +func Test(t *testing.T) { + want := 42 + if got := F(); got != want { + t.Errorf("got %d; want %d", got, want) + } +} diff --git a/tests/core/cgo/cgo_ref.go b/tests/core/cgo/cgo_ref.go new file mode 100644 index 00000000..f425e188 --- /dev/null +++ b/tests/core/cgo/cgo_ref.go @@ -0,0 +1,20 @@ +// 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 cgo_link + +// int f(); +import "C" + +func F() int { return int(C.f()) } diff --git a/tests/core/cgo/direct_dep.go b/tests/core/cgo/direct_dep.go new file mode 100644 index 00000000..6f717863 --- /dev/null +++ b/tests/core/cgo/direct_dep.go @@ -0,0 +1,9 @@ +package direct_dep + +import ( + "github.com/bazelbuild/rules_go/tests/core/cgo/transitive_dep" +) + +func PrintGreeting() { + transitive_dep.PrintGreeting() +} diff --git a/tests/core/cgo/dylib_client.go b/tests/core/cgo/dylib_client.go new file mode 100644 index 00000000..7356d127 --- /dev/null +++ b/tests/core/cgo/dylib_client.go @@ -0,0 +1,10 @@ +package dylib + +/* +extern int foo(); +*/ +import "C" + +func Foo() int { + return int(C.foo()) +} diff --git a/tests/core/cgo/dylib_test.go b/tests/core/cgo/dylib_test.go new file mode 100644 index 00000000..03aa1f36 --- /dev/null +++ b/tests/core/cgo/dylib_test.go @@ -0,0 +1,10 @@ +package dylib + +import "testing" + +func TestFoo(t *testing.T) { + want := 42 + if got := Foo(); got != want { + t.Errorf("got %d ; want %d", got, want) + } +} diff --git a/tests/core/cgo/external_includes_test.go b/tests/core/cgo/external_includes_test.go new file mode 100644 index 00000000..0e1ba06a --- /dev/null +++ b/tests/core/cgo/external_includes_test.go @@ -0,0 +1,85 @@ +// Copyright 2020 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 external_includes_test + +import ( + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Main: ` +-- other_repo/WORKSPACE -- +-- other_repo/cc/BUILD.bazel -- +cc_binary( + name = "main", + srcs = ["main.c"], + deps = ["//cgo"], +) +-- other_repo/cc/main.c -- +#include "cgo/cgo.h" + +int main() {} +-- other_repo/cgo/BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") + +go_binary( + name = "cgo", + embed = [":cgo_lib"], + importpath = "example.com/rules_go/cgo", + linkmode = "c-archive", + visibility = ["//visibility:public"], +) + +go_library( + name = "cgo_lib", + srcs = ["cgo.go"], + cgo = True, + importpath = "example.com/rules_go/cgo", + visibility = ["//visibility:private"], +) +-- other_repo/cgo/cgo.go -- +package main + +import "C" + +//export HelloCgo +func HelloCgo() {} + +func main() {} +`, + WorkspaceSuffix: ` +local_repository( + name = "other_repo", + path = "other_repo", +) +`, + }) +} + +func TestExternalIncludes(t *testing.T) { + t.Run("default", func(t *testing.T) { + if err := bazel_testing.RunBazel("build", "@other_repo//cc:main"); err != nil { + t.Fatalf("Did not expect error:\n%+v", err) + } + }) + t.Run("experimental_sibling_repository_layout", func(t *testing.T) { + if err := bazel_testing.RunBazel("build", "--experimental_sibling_repository_layout", "@other_repo//cc:main"); err != nil { + t.Fatalf("Did not expect error:\n%+v", err) + } + }) +} diff --git a/tests/core/cgo/foo.c b/tests/core/cgo/foo.c new file mode 100644 index 00000000..0992a7d1 --- /dev/null +++ b/tests/core/cgo/foo.c @@ -0,0 +1,7 @@ +#include <stdio.h> + +void foo() { + printf("foo\n"); +} + + diff --git a/tests/core/cgo/foo.go b/tests/core/cgo/foo.go new file mode 100644 index 00000000..4403a51c --- /dev/null +++ b/tests/core/cgo/foo.go @@ -0,0 +1,8 @@ +package main + +// void foo(); +import "C" + +func main() { + C.foo() +} diff --git a/tests/core/cgo/generate_imported_dylib.sh b/tests/core/cgo/generate_imported_dylib.sh new file mode 100755 index 00000000..41227b77 --- /dev/null +++ b/tests/core/cgo/generate_imported_dylib.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +set -exo pipefail + +cd "$(dirname "$0")" + +case "$(uname -s)" in + Linux*) + cc -shared -o libimported.so imported.c + cc -shared -o libversioned.so.2 imported.c + ;; + Darwin*) + cc -shared -Wl,-install_name,@rpath/libimported.dylib -o libimported.dylib imported.c + # According to "Mac OS X For Unix Geeks", 4th Edition, Chapter 11, versioned dylib for macOS + # should be libversioned.2.dylib. + cc -shared -Wl,-install_name,@rpath/libversioned.2.dylib -o libversioned.2.dylib imported.c + # However, Oracle Instant Client was distributed as libclntsh.dylib.12.1 with a unversioed + # symlink (https://www.oracle.com/database/technologies/instant-client/macos-intel-x86-downloads.html). + # Let's cover this non-standard case as well. + cc -shared -Wl,-install_name,@rpath/libversioned.dylib.2 -o libversioned.dylib.2 imported.c + ln -fs libversioned.dylib.2 libversioned.dylib + ;; + *) + echo "Unsupported OS: $(uname -s)" >&2 + exit 1 +esac diff --git a/tests/core/cgo/imported.c b/tests/core/cgo/imported.c new file mode 100644 index 00000000..bf7759e1 --- /dev/null +++ b/tests/core/cgo/imported.c @@ -0,0 +1 @@ +int foo() { return 42; } diff --git a/tests/core/cgo/native_dep.c b/tests/core/cgo/native_dep.c new file mode 100644 index 00000000..f6504a85 --- /dev/null +++ b/tests/core/cgo/native_dep.c @@ -0,0 +1,5 @@ +#include <stdio.h> + +void native_greeting(void) { + printf("Hello, world!\n"); +} diff --git a/tests/core/cgo/native_dep.h b/tests/core/cgo/native_dep.h new file mode 100644 index 00000000..5d750beb --- /dev/null +++ b/tests/core/cgo/native_dep.h @@ -0,0 +1 @@ +extern void native_greeting(void); diff --git a/tests/core/cgo/objc/BUILD.bazel b/tests/core/cgo/objc/BUILD.bazel new file mode 100644 index 00000000..030408f1 --- /dev/null +++ b/tests/core/cgo/objc/BUILD.bazel @@ -0,0 +1,32 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_test( + name = "objc_test", + srcs = ["objc_darwin_test.go"], + embed = select({ + "@io_bazel_rules_go//go/platform:darwin": [":objc_lib"], + "//conditions:default": [], + }), +) + +go_library( + name = "objc_lib", + srcs = [ + "add_darwin.go", + "add_darwin.h", + "add_darwin.m", + "sub_darwin.go", + ], + cdeps = [":sub"], + cgo = True, + copts = ["-fmodules"], + importpath = "github.com/bazelbuild/rules_go/tests/core/cgo/objc", + tags = ["manual"], +) + +objc_library( + name = "sub", + srcs = ["sub.m"], + enable_modules = True, + tags = ["manual"], +) diff --git a/tests/core/cgo/objc/README.rst b/tests/core/cgo/objc/README.rst new file mode 100644 index 00000000..86faa182 --- /dev/null +++ b/tests/core/cgo/objc/README.rst @@ -0,0 +1,8 @@ +Objective C / cgo functionality +=============================== + +objc_test +--------- + +Checks that a Go target with Objective C code (both embedded and in an +``objc_library`` ``cdeps`` dependency) compiles, links, and executes. diff --git a/tests/core/cgo/objc/add_darwin.go b/tests/core/cgo/objc/add_darwin.go new file mode 100644 index 00000000..76bdaf97 --- /dev/null +++ b/tests/core/cgo/objc/add_darwin.go @@ -0,0 +1,10 @@ +package objc + +/* +#include "add_darwin.h" +*/ +import "C" + +func Add(a, b int32) int32 { + return int32(C.add(C.int(a), C.int(b))) +} diff --git a/tests/core/cgo/objc/add_darwin.h b/tests/core/cgo/objc/add_darwin.h new file mode 100644 index 00000000..6295ab95 --- /dev/null +++ b/tests/core/cgo/objc/add_darwin.h @@ -0,0 +1 @@ +int add(int a, int b); diff --git a/tests/core/cgo/objc/add_darwin.m b/tests/core/cgo/objc/add_darwin.m new file mode 100644 index 00000000..ffd02430 --- /dev/null +++ b/tests/core/cgo/objc/add_darwin.m @@ -0,0 +1,22 @@ +@import Foundation; + +#include "add_darwin.h" + +@interface Adder : NSObject + +- (int)add:(int)num1 andNum2:(int)num2; + +@end + +@implementation Adder + +- (int)add:(int)num1 andNum2:(int)num2{ + return num1 + num2; +} + +@end + +int add(int a, int b) { + Adder* adder = [[Adder alloc] init]; + return [adder add:a andNum2:b]; +} diff --git a/tests/core/cgo/objc/objc_darwin_test.go b/tests/core/cgo/objc/objc_darwin_test.go new file mode 100644 index 00000000..5d475b4d --- /dev/null +++ b/tests/core/cgo/objc/objc_darwin_test.go @@ -0,0 +1,16 @@ +package objc + +import ( + "fmt" + "math/rand" + "testing" +) + +func TestObjcMethod(t *testing.T) { + a := rand.Int31() + b := rand.Int31() + expected := a + b + if result := Add(a, b); result != expected { + t.Error(fmt.Errorf("wrong result: expected %d, got %d", expected, result)) + } +} diff --git a/tests/core/cgo/objc/sub.m b/tests/core/cgo/objc/sub.m new file mode 100644 index 00000000..3b408ea8 --- /dev/null +++ b/tests/core/cgo/objc/sub.m @@ -0,0 +1,20 @@ +@import Foundation; + +@interface Subber : NSObject + +- (int)sub:(int)num1 andNum2:(int)num2; + +@end + +@implementation Subber + +- (int)sub:(int)num1 andNum2:(int)num2{ + return num1 - num2; +} + +@end + +int sub(int a, int b) { + Subber* subber = [[Subber alloc] init]; + return [subber sub:a andNum2:b]; +} diff --git a/tests/core/cgo/objc/sub_darwin.go b/tests/core/cgo/objc/sub_darwin.go new file mode 100644 index 00000000..5b780307 --- /dev/null +++ b/tests/core/cgo/objc/sub_darwin.go @@ -0,0 +1,10 @@ +package objc + +/* +int sub(int a, int b); +*/ +import "C" + +func Sub(a, b int32) int32 { + return int32(C.sub(C.int(a), C.int(b))) +} diff --git a/tests/core/cgo/provide_external_symbol.go b/tests/core/cgo/provide_external_symbol.go new file mode 100644 index 00000000..68d9fce1 --- /dev/null +++ b/tests/core/cgo/provide_external_symbol.go @@ -0,0 +1,12 @@ +package main + +import "C" + +import "github.com/bazelbuild/rules_go/tests/core/cgo/use_external_symbol" + +//export external_symbol +func external_symbol() {} + +func main() { + use_external_symbol.UseExternalSymbol() +} diff --git a/tests/core/cgo/pure.go b/tests/core/cgo/pure.go new file mode 100644 index 00000000..38dd16da --- /dev/null +++ b/tests/core/cgo/pure.go @@ -0,0 +1,3 @@ +package main + +func main() {} diff --git a/tests/core/cgo/race_off.c b/tests/core/cgo/race_off.c new file mode 100644 index 00000000..f752c63c --- /dev/null +++ b/tests/core/cgo/race_off.c @@ -0,0 +1,3 @@ +// +build !race + +int race_enabled = 0; diff --git a/tests/core/cgo/race_off.go b/tests/core/cgo/race_off.go new file mode 100644 index 00000000..b572268e --- /dev/null +++ b/tests/core/cgo/race_off.go @@ -0,0 +1,5 @@ +// +build !race + +package race + +const goRaceEnabled = false diff --git a/tests/core/cgo/race_on.c b/tests/core/cgo/race_on.c new file mode 100644 index 00000000..9b79ea31 --- /dev/null +++ b/tests/core/cgo/race_on.c @@ -0,0 +1,3 @@ +// +build race + +int race_enabled = 1; diff --git a/tests/core/cgo/race_on.go b/tests/core/cgo/race_on.go new file mode 100644 index 00000000..7f172dc5 --- /dev/null +++ b/tests/core/cgo/race_on.go @@ -0,0 +1,5 @@ +// +build race + +package race + +const goRaceEnabled = true diff --git a/tests/core/cgo/race_test.go b/tests/core/cgo/race_test.go new file mode 100644 index 00000000..c9344bac --- /dev/null +++ b/tests/core/cgo/race_test.go @@ -0,0 +1,31 @@ +// Copyright 2018 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 race + +// extern int race_enabled; +import "C" +import "testing" + +func TestGo(t *testing.T) { + if !goRaceEnabled { + t.Error("goRaceEnabled is false") + } +} + +func TestC(t *testing.T) { + if C.race_enabled == 0 { + t.Error("C.race_enabled is false") + } +} diff --git a/tests/core/cgo/split_import_a.go b/tests/core/cgo/split_import_a.go new file mode 100644 index 00000000..4b0e38a1 --- /dev/null +++ b/tests/core/cgo/split_import_a.go @@ -0,0 +1,3 @@ +package a + +func Answer() int { return 42 } diff --git a/tests/core/cgo/split_import_b.go b/tests/core/cgo/split_import_b.go new file mode 100644 index 00000000..2d8410fa --- /dev/null +++ b/tests/core/cgo/split_import_b.go @@ -0,0 +1,10 @@ +package b + +import ( + "github.com/bazelbuild/rules_go/tests/core/cgo/split_import/a" + "github.com/bazelbuild/rules_go/tests/core/cgo/split_import/cgo" +) + +func HalfAnswer() int { + return cgo.Half(a.Answer()) +} diff --git a/tests/core/cgo/split_import_c.c b/tests/core/cgo/split_import_c.c new file mode 100644 index 00000000..b20e936e --- /dev/null +++ b/tests/core/cgo/split_import_c.c @@ -0,0 +1,2 @@ +int half(int x) { return x/2; } + diff --git a/tests/core/cgo/split_import_c.h b/tests/core/cgo/split_import_c.h new file mode 100644 index 00000000..35731ef1 --- /dev/null +++ b/tests/core/cgo/split_import_c.h @@ -0,0 +1,6 @@ +#ifndef split_import_c +#define split_import_c + +int half(int); + +#endif diff --git a/tests/core/cgo/split_import_cgo.go b/tests/core/cgo/split_import_cgo.go new file mode 100644 index 00000000..b15bc16f --- /dev/null +++ b/tests/core/cgo/split_import_cgo.go @@ -0,0 +1,10 @@ +package cgo + +/* +#include "tests/core/cgo/split_import_c.h" +*/ +import "C" + +func Half(x int) int { + return int(C.half(C.int(x))) +} diff --git a/tests/core/cgo/split_import_i_test.go b/tests/core/cgo/split_import_i_test.go new file mode 100644 index 00000000..77429eff --- /dev/null +++ b/tests/core/cgo/split_import_i_test.go @@ -0,0 +1,9 @@ +package a + +import "testing" + +func TestInternal(t *testing.T) { + if Answer() != 42 { + t.Error("wrong answer") + } +} diff --git a/tests/core/cgo/split_import_x_test.go b/tests/core/cgo/split_import_x_test.go new file mode 100644 index 00000000..9202eaad --- /dev/null +++ b/tests/core/cgo/split_import_x_test.go @@ -0,0 +1,13 @@ +package a_test + +import ( + "testing" + + "github.com/bazelbuild/rules_go/tests/core/cgo/split_import/b" +) + +func TestExternal(t *testing.T) { + if b.HalfAnswer() != 21 { + t.Error("wrong answer") + } +} diff --git a/tests/core/cgo/tag_cgo.go b/tests/core/cgo/tag_cgo.go new file mode 100644 index 00000000..e8185f85 --- /dev/null +++ b/tests/core/cgo/tag_cgo.go @@ -0,0 +1,10 @@ +package main + +// const char *msg = "cgo"; +import "C" + +import "fmt" + +func main() { + fmt.Println(C.GoString(C.msg)) +} diff --git a/tests/core/cgo/tag_cgo_err.go b/tests/core/cgo/tag_cgo_err.go new file mode 100644 index 00000000..4e3a9fa4 --- /dev/null +++ b/tests/core/cgo/tag_cgo_err.go @@ -0,0 +1,6 @@ +// +build !cgo + +package main + +// this file should not be compiled +!!! diff --git a/tests/core/cgo/tag_pure.go b/tests/core/cgo/tag_pure.go new file mode 100644 index 00000000..eca8e6fd --- /dev/null +++ b/tests/core/cgo/tag_pure.go @@ -0,0 +1,9 @@ +// +build !cgo + +package main + +import "fmt" + +func main() { + fmt.Println("pure") +} diff --git a/tests/core/cgo/tag_pure_err.c b/tests/core/cgo/tag_pure_err.c new file mode 100644 index 00000000..c92fa97c --- /dev/null +++ b/tests/core/cgo/tag_pure_err.c @@ -0,0 +1 @@ +#error should not be compiled diff --git a/tests/core/cgo/tag_pure_err.go b/tests/core/cgo/tag_pure_err.go new file mode 100644 index 00000000..32b6e953 --- /dev/null +++ b/tests/core/cgo/tag_pure_err.go @@ -0,0 +1,6 @@ +// +build cgo + +package main + +// this file should not be compiled +!!! diff --git a/tests/core/cgo/tag_test.go b/tests/core/cgo/tag_test.go new file mode 100644 index 00000000..b1ad4c99 --- /dev/null +++ b/tests/core/cgo/tag_test.go @@ -0,0 +1,38 @@ +package tag + +import ( + "os/exec" + "strings" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel" +) + +func Test(t *testing.T) { + for _, tc := range []struct { + name, path, want string + }{ + { + name: "tag_pure_bin", + want: "pure", + }, { + name: "tag_cgo_bin", + want: "cgo", + }, + } { + t.Run(tc.name, func(t *testing.T) { + path, ok := bazel.FindBinary("tests/core/cgo", tc.name) + if !ok { + t.Fatalf("could not find binary: %s", tc.name) + } + out, err := exec.Command(path).Output() + if err != nil { + t.Fatal(err) + } + got := strings.TrimSpace(string(out)) + if got != tc.want { + t.Errorf("got %s; want %s", got, tc.want) + } + }) + } +} diff --git a/tests/core/cgo/transitive_dep.go b/tests/core/cgo/transitive_dep.go new file mode 100644 index 00000000..40632f50 --- /dev/null +++ b/tests/core/cgo/transitive_dep.go @@ -0,0 +1,10 @@ +package transitive_dep + +/* +#include "tests/core/cgo/native_dep.h" + */ +import "C" + +func PrintGreeting() { + C.native_greeting(); +} diff --git a/tests/core/cgo/use_c_symbol_through_go.c b/tests/core/cgo/use_c_symbol_through_go.c new file mode 100644 index 00000000..bd5b7fec --- /dev/null +++ b/tests/core/cgo/use_c_symbol_through_go.c @@ -0,0 +1,5 @@ +#include "tests/core/cgo/use_transitive_symbol.h" + +int main() { + PrintGreeting(); +} diff --git a/tests/core/cgo/use_external_symbol.go b/tests/core/cgo/use_external_symbol.go new file mode 100644 index 00000000..70980ea7 --- /dev/null +++ b/tests/core/cgo/use_external_symbol.go @@ -0,0 +1,10 @@ +package use_external_symbol + +/* +void external_symbol(); +*/ +import "C" + +func UseExternalSymbol() { + C.external_symbol() +} diff --git a/tests/core/cgo/use_transitive_symbol.go b/tests/core/cgo/use_transitive_symbol.go new file mode 100644 index 00000000..c229311e --- /dev/null +++ b/tests/core/cgo/use_transitive_symbol.go @@ -0,0 +1,14 @@ +package main + +import "C" + +import ( + "github.com/bazelbuild/rules_go/tests/core/cgo/direct_dep" +) + +//export PrintGreeting +func PrintGreeting() { + direct_dep.PrintGreeting() +} + +func main() {} diff --git a/tests/core/coverage/BUILD.bazel b/tests/core/coverage/BUILD.bazel new file mode 100644 index 00000000..9a20440f --- /dev/null +++ b/tests/core/coverage/BUILD.bazel @@ -0,0 +1,34 @@ +load("@io_bazel_rules_go//go/tools/bazel_testing:def.bzl", "go_bazel_test") + +go_bazel_test( + name = "coverage_test", + srcs = ["coverage_test.go"], +) + +go_bazel_test( + name = "binary_coverage_test", + srcs = ["binary_coverage_test.go"], +) + +go_bazel_test( + name = "lcov_coverage_test", + srcs = ["lcov_coverage_test.go"], + target_compatible_with = select({ + "@platforms//os:windows": ["@platforms//:incompatible"], + "//conditions:default": [], + }), +) + +go_bazel_test( + name = "lcov_test_main_coverage_test", + srcs = ["lcov_test_main_coverage_test.go"], + target_compatible_with = select({ + "@platforms//os:windows": ["@platforms//:incompatible"], + "//conditions:default": [], + }), +) + +go_bazel_test( + name = "issue3017_test", + srcs = ["issue3017_test.go"], +) diff --git a/tests/core/coverage/README.rst b/tests/core/coverage/README.rst new file mode 100644 index 00000000..aadc5fa9 --- /dev/null +++ b/tests/core/coverage/README.rst @@ -0,0 +1,23 @@ +.. _#2127: https://github.com/bazelbuild/rules_go/issues/2127 + +coverage functionality +====================== + +coverage_test +------------- + +Checks that ``bazel coverage`` on a ``go_test`` produces reasonable output. +Libraries referenced by the test that pass ``--instrumentation_filter`` should +have coverage data. Library excluded with ``--instrumentatiuon_filter`` should +not have coverage data. + +binary_coverage_test +-------------------- + +Checks that ``bazel build --collect_code_coverage`` can instrument a +``go_binary``. ``bazel coverage`` should also work, though it should fail +with status 4 since the binary is not a test. + +This functionality isn't really complete. The generate test main package +gathers and writes coverage data, and that's not present. This is just +a regression test for a link error (`#2127`_). diff --git a/tests/core/coverage/binary_coverage_test.go b/tests/core/coverage/binary_coverage_test.go new file mode 100644 index 00000000..724528be --- /dev/null +++ b/tests/core/coverage/binary_coverage_test.go @@ -0,0 +1,75 @@ +// 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 binary_coverage_test + +import ( + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Main: ` +-- BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_binary") + +go_binary( + name = "hello", + srcs = ["hello.go"], + out = "hello", +) +-- hello.go -- +package main + +import "fmt" + +func main() { + fmt.Println(A()) +} + +func A() int { return 12 } + +func B() int { return 34 } +`, + }) +} + +func Test(t *testing.T) { + // Check that we can build a binary with coverage instrumentation enabled. + args := []string{ + "build", + "--collect_code_coverage", + "--instrumentation_filter=.*", + "//:hello", + } + if err := bazel_testing.RunBazel(args...); err != nil { + t.Fatal(err) + } + + // Check that we can build with `bazel coverage`. It will fail because + // there are no tests. + args = []string{ + "coverage", + "//:hello", + } + if err := bazel_testing.RunBazel(args...); err == nil { + t.Fatal("got success; want failure") + } else if bErr, ok := err.(*bazel_testing.StderrExitError); !ok { + t.Fatalf("got %v; want StderrExitError", err) + } else if code := bErr.Err.ExitCode(); code != 4 { + t.Fatalf("got code %d; want code 4 (no tests found)", code) + } +} diff --git a/tests/core/coverage/coverage_test.go b/tests/core/coverage/coverage_test.go new file mode 100644 index 00000000..52bda780 --- /dev/null +++ b/tests/core/coverage/coverage_test.go @@ -0,0 +1,265 @@ +// 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 coverage_test + +import ( + "bytes" + "fmt" + "io/ioutil" + "path/filepath" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Main: ` +-- BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_test( + name = "a_test", + srcs = ["a_test.go"], + embed = [":a"], +) + +go_test( + name = "a_test_cross", + srcs = ["a_test.go"], + embed = [":a"], + goarch = "386", + goos = "linux", + pure = "on", + tags = ["manual"], +) + +go_library( + name = "a", + srcs = ["a.go"], + importpath = "example.com/coverage/a", + deps = [":b"], +) + +go_library( + name = "b", + srcs = ["b.go"], + importpath = "example.com/coverage/b", + deps = [":c"], +) + +go_library( + name = "c", + srcs = ["c.go"], + importpath = "example.com/coverage/c", +) + +go_library( + name = "d", + srcs = ["d.go"], + importpath = "example.com/coverage/d", +) + +go_test( + name = "d_test", + embed = [":d"], +) + +go_library( + name = "panicking", + srcs = ["panicking.go"], + importpath = "example.com/coverage/panicking", +) + +go_test( + name = "panicking_test", + srcs = ["panicking_test.go"], + embed = [":panicking"], +) +-- a_test.go -- +package a + +import "testing" + +func TestA(t *testing.T) { + ALive() +} +-- a.go -- +package a + +import "example.com/coverage/b" + +func ALive() int { + return b.BLive() +} + +func ADead() int { + return b.BDead() +} + +-- b.go -- +package b + +import "example.com/coverage/c" + +func BLive() int { + return c.CLive() +} + +func BDead() int { + return c.CDead() +} + +-- c.go -- +package c + +func CLive() int { + return 12 +} + +func CDead() int { + return 34 +} + +-- d.go -- +package lzma + +/* Naming conventions follows the CodeReviewComments in the Go Wiki. */ + +// ntz32Const is used by the functions NTZ and NLZ. +const ntz32Const = 0x04d7651f +-- panicking.go -- +package panicking + +func Panic() { + panic("from line 4") +} +-- panicking_test.go -- +package panicking + +import ( + "regexp" + "runtime/debug" + "testing" +) + +func TestPanic(t *testing.T) { + defer func() { + if err := recover(); err != nil { + got := regexp.MustCompile("panicking.go:[0-9]+"). + FindString(string(debug.Stack())) + if want := "panicking.go:4"; want != got { + t.Errorf("want %q; got %q", want, got) + } + } + }() + Panic() +} +`, + }) +} + +func TestCoverage(t *testing.T) { + t.Run("without-race", func(t *testing.T) { + testCoverage(t, "set") + }) + + t.Run("with-race", func(t *testing.T) { + testCoverage(t, "atomic", "--@io_bazel_rules_go//go/config:race") + }) +} + +func testCoverage(t *testing.T, expectedCoverMode string, extraArgs ...string) { + args := append([]string{"coverage"}, append( + extraArgs, + "--instrumentation_filter=-//:b", + "--@io_bazel_rules_go//go/config:cover_format=go_cover", + ":a_test", + )...) + + if err := bazel_testing.RunBazel(args...); err != nil { + t.Fatal(err) + } + + coveragePath := filepath.FromSlash("bazel-testlogs/a_test/coverage.dat") + coverageData, err := ioutil.ReadFile(coveragePath) + if err != nil { + t.Fatal(err) + } + for _, include := range []string{ + fmt.Sprintf("mode: %s", expectedCoverMode), + "example.com/coverage/a/a.go:", + "example.com/coverage/c/c.go:", + } { + if !bytes.Contains(coverageData, []byte(include)) { + t.Errorf("%s: does not contain %q\n", coveragePath, include) + } + } + for _, exclude := range []string{ + "example.com/coverage/b/b.go:", + } { + if bytes.Contains(coverageData, []byte(exclude)) { + t.Errorf("%s: contains %q\n", coveragePath, exclude) + } + } +} + +func TestCrossBuild(t *testing.T) { + t.Run("lcov", func(t *testing.T) { + testCrossBuild(t) + }) + t.Run("cover", func(t *testing.T) { + testCrossBuild(t, "--@io_bazel_rules_go//go/config:cover_format=go_cover") + }) +} + +func testCrossBuild(t *testing.T, extraArgs ...string) { + if err := bazel_testing.RunBazel(append( + []string{"build", "--collect_code_coverage", "--instrumentation_filter=-//:b", "//:a_test_cross"}, + extraArgs..., + )...); err != nil { + t.Fatal(err) + } +} + +func TestCoverageWithComments(t *testing.T) { + t.Run("lcov", func(t *testing.T) { + testCoverageWithComments(t) + }) + t.Run("go_cover", func(t *testing.T) { + testCoverageWithComments(t, "--@io_bazel_rules_go//go/config:cover_format=go_cover") + }) +} + +func testCoverageWithComments(t *testing.T, extraArgs ...string) { + if err := bazel_testing.RunBazel(append([]string{"coverage", ":d_test"}, extraArgs...)...); err != nil { + t.Fatal(err) + } +} + +func TestCoverageWithCorrectLineNumbers(t *testing.T) { + t.Run("lcov", func(t *testing.T) { + testCoverageWithCorrectLineNumbers(t) + }) + t.Run("go_cover", func(t *testing.T) { + testCoverageWithCorrectLineNumbers(t, "--@io_bazel_rules_go//go/config:cover_format=go_cover") + }) +} + +func testCoverageWithCorrectLineNumbers(t *testing.T, extraArgs ...string) { + if err := bazel_testing.RunBazel(append([]string{"coverage", ":panicking_test"}, extraArgs...)...); err != nil { + t.Fatal(err) + } +} diff --git a/tests/core/coverage/issue3017_test.go b/tests/core/coverage/issue3017_test.go new file mode 100644 index 00000000..e2786e1f --- /dev/null +++ b/tests/core/coverage/issue3017_test.go @@ -0,0 +1,80 @@ +// 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 coverage_test + +import ( + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Main: ` +-- fx.go -- +package fx + +import ( + _ "uber.com/internal" +) +-- fx_test.go -- +package fx +-- internal/BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = ["lib.go"], + importpath = "uber.com/internal", + visibility = ["//visibility:public"], + deps = ["@io_bazel_rules_go//go/tools/coverdata"], +) + +go_test( + name = "go_default_test", + srcs = ["lib_test.go"], + embed = [":go_default_library"], +) +-- internal/lib.go -- +package internal + +import _ "github.com/bazelbuild/rules_go/go/tools/coverdata" +-- internal/lib_test.go -- +package internal +-- BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = ["fx.go"], + importpath = "code.uber.internal/devexp/code-coverage/cmd/fx", + visibility = ["//visibility:private"], + deps = ["//internal:go_default_library"], +) + +go_test( + name = "go_default_test", + srcs = ["fx_test.go"], + embed = [":go_default_library"], +) +`, + }) +} + +func TestIssue3017(t *testing.T) { + if err := bazel_testing.RunBazel("coverage", "//:go_default_test"); err != nil { + t.Fatal(err) + } +} diff --git a/tests/core/coverage/lcov_coverage_test.go b/tests/core/coverage/lcov_coverage_test.go new file mode 100644 index 00000000..a46567cc --- /dev/null +++ b/tests/core/coverage/lcov_coverage_test.go @@ -0,0 +1,290 @@ +// Copyright 2022 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 lcov_coverage_test + +import ( + "io/ioutil" + "path/filepath" + "strings" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Main: ` +-- src/BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "lib", + srcs = ["lib.go"], + importpath = "example.com/lib", + deps = [":other_lib"], +) + +go_library( + name = "other_lib", + srcs = ["other_lib.go"], + importpath = "example.com/other_lib", +) + +go_test( + name = "lib_test", + srcs = ["lib_test.go"], + deps = [":lib"], +) + +java_binary( + name = "Tool", + srcs = ["Tool.java"], +) + +go_test( + name = "lib_with_tool_test", + srcs = ["lib_with_tool_test.go"], + data = [":Tool"], + deps = [":lib"], +) +-- src/lib.go -- +package lib + +import ( + "strings" + + "example.com/other_lib" +) + +func HelloFromLib(informal bool) string { + var greetings []string + if informal { + greetings = []string{"Hey there, other_lib!"} + } else { + greetings = []string{"Good morning, other_lib!"} + } + greetings = append(greetings, other_lib.HelloOtherLib(informal)) + return strings.Join(greetings, "\n") +} +-- src/other_lib.go -- +package other_lib + +func HelloOtherLib(informal bool) string { + if informal { + return "Hey there, other_lib!" + } + return "Good morning, other_lib!" +} +-- src/lib_test.go -- +package lib_test + +import ( + "strings" + "testing" + + "example.com/lib" +) + +func TestLib(t *testing.T) { + if !strings.Contains(lib.HelloFromLib(false), "\n") { + t.Error("Expected a newline in the output") + } +} +-- src/Tool.java -- +public class Tool { + public static void main(String[] args) { + if (args.length != 0) { + System.err.println("Expected no arguments"); + System.exit(1); + } + System.err.println("Hello, world!"); + } +} +-- src/lib_with_tool_test.go -- +package lib_test + +import ( + "os/exec" + "path/filepath" + "strings" + "testing" + + "example.com/lib" +) + +func TestLib(t *testing.T) { + if !strings.Contains(lib.HelloFromLib(false), "\n") { + t.Error("Expected a newline in the output") + } +} + +func TestTool(t *testing.T) { + err := exec.Command("." + string(filepath.Separator) + "Tool").Run() + if err != nil { + t.Error(err) + } +} + +`, + }) +} + +func TestLcovCoverage(t *testing.T) { + t.Run("without-race", func(t *testing.T) { + testLcovCoverage(t) + }) + + t.Run("with-race", func(t *testing.T) { + testLcovCoverage(t, "--@io_bazel_rules_go//go/config:race") + }) +} + +func testLcovCoverage(t *testing.T, extraArgs ...string) { + args := append([]string{ + "coverage", + "--combined_report=lcov", + "//src:lib_test", + }, extraArgs...) + + if err := bazel_testing.RunBazel(args...); err != nil { + t.Fatal(err) + } + + individualCoveragePath := filepath.FromSlash("bazel-testlogs/src/lib_test/coverage.dat") + individualCoverageData, err := ioutil.ReadFile(individualCoveragePath) + if err != nil { + t.Fatal(err) + } + for _, expectedIndividualCoverage := range expectedGoCoverage { + if !strings.Contains(string(individualCoverageData), expectedIndividualCoverage) { + t.Errorf( + "%s: does not contain:\n\n%s\nactual content:\n\n%s", + individualCoveragePath, + expectedIndividualCoverage, + string(individualCoverageData), + ) + } + } + + combinedCoveragePath := filepath.FromSlash("bazel-out/_coverage/_coverage_report.dat") + combinedCoverageData, err := ioutil.ReadFile(combinedCoveragePath) + if err != nil { + t.Fatal(err) + } + for _, include := range []string{ + "SF:src/lib.go\n", + "SF:src/other_lib.go\n", + } { + if !strings.Contains(string(combinedCoverageData), include) { + t.Errorf("%s: does not contain %q\n", combinedCoverageData, include) + } + } +} + +func TestLcovCoverageWithTool(t *testing.T) { + args := append([]string{ + "coverage", + "--combined_report=lcov", + "//src:lib_with_tool_test", + }) + + if err := bazel_testing.RunBazel(args...); err != nil { + t.Fatal(err) + } + + individualCoveragePath := filepath.FromSlash("bazel-testlogs/src/lib_with_tool_test/coverage.dat") + individualCoverageData, err := ioutil.ReadFile(individualCoveragePath) + if err != nil { + t.Fatal(err) + } + expectedCoverage := append(expectedGoCoverage, expectedToolCoverage) + for _, expected := range expectedCoverage { + if !strings.Contains(string(individualCoverageData), expected) { + t.Errorf( + "%s: does not contain:\n\n%s\nactual content:\n\n%s", + individualCoveragePath, + expected, + string(individualCoverageData), + ) + } + } + + combinedCoveragePath := filepath.FromSlash("bazel-out/_coverage/_coverage_report.dat") + combinedCoverageData, err := ioutil.ReadFile(combinedCoveragePath) + if err != nil { + t.Fatal(err) + } + for _, include := range []string{ + "SF:src/lib.go\n", + "SF:src/other_lib.go\n", + "SF:src/Tool.java\n", + } { + if !strings.Contains(string(combinedCoverageData), include) { + t.Errorf("%s: does not contain %q\n", combinedCoverageData, include) + } + } +} + +var expectedGoCoverage = []string{ + `SF:src/other_lib.go +FNF:0 +FNH:0 +DA:3,1 +DA:4,1 +DA:5,0 +DA:6,0 +DA:7,1 +LH:3 +LF:5 +end_of_record +`, + `SF:src/lib.go +FNF:0 +FNH:0 +DA:9,1 +DA:10,1 +DA:11,1 +DA:12,0 +DA:13,1 +DA:14,1 +DA:15,1 +DA:16,1 +DA:17,1 +LH:8 +LF:9 +end_of_record +`} + +const expectedToolCoverage = `SF:src/Tool.java +FN:1,Tool::<init> ()V +FN:3,Tool::main ([Ljava/lang/String;)V +FNDA:0,Tool::<init> ()V +FNDA:1,Tool::main ([Ljava/lang/String;)V +FNF:2 +FNH:1 +BRDA:3,0,0,1 +BRDA:3,0,1,0 +BRF:2 +BRH:1 +DA:1,0 +DA:3,1 +DA:4,0 +DA:5,0 +DA:7,1 +DA:8,1 +LH:3 +LF:6 +end_of_record +` diff --git a/tests/core/coverage/lcov_test_main_coverage_test.go b/tests/core/coverage/lcov_test_main_coverage_test.go new file mode 100644 index 00000000..57aafd28 --- /dev/null +++ b/tests/core/coverage/lcov_test_main_coverage_test.go @@ -0,0 +1,122 @@ +// Copyright 2022 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 lcov_test_main_coverage_test + +import ( + "io/ioutil" + "path/filepath" + "strings" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Main: ` +-- src/BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "lib", + srcs = ["lib.go"], + importpath = "example.com/lib", +) + +go_test( + name = "lib_test", + srcs = ["lib_test.go"], + deps = [":lib"], +) +-- src/lib.go -- +package lib + +func HelloFromLib(informal bool) string { + if informal { + return "Hey there, lib!" + } else { + return "Good morning, lib!" + } +} +-- src/lib_test.go -- +package lib_test + +import ( + "strings" + "testing" + "os" + + "example.com/lib" +) + +func TestMain(m *testing.M) { + os.Exit(m.Run()) +} + +func TestLib(t *testing.T) { + if !strings.Contains(lib.HelloFromLib(false), "lib!") { + t.Error("Expected 'lib!' in the output") + } +} +`, + }) +} + +func TestLcovCoverageWithTestMain(t *testing.T) { + if err := bazel_testing.RunBazel("coverage", "--combined_report=lcov", "//src:lib_test"); err != nil { + t.Fatal(err) + } + + individualCoveragePath := filepath.FromSlash("bazel-testlogs/src/lib_test/coverage.dat") + individualCoverageData, err := ioutil.ReadFile(individualCoveragePath) + if err != nil { + t.Fatal(err) + } + if string(individualCoverageData) != string(expectedIndividualCoverage) { + t.Errorf( + "%s: expected content:\n\n%s\nactual content:\n\n%s", + individualCoveragePath, + expectedIndividualCoverage, + string(individualCoverageData), + ) + } + + combinedCoveragePath := filepath.FromSlash("bazel-out/_coverage/_coverage_report.dat") + combinedCoverageData, err := ioutil.ReadFile(combinedCoveragePath) + if err != nil { + t.Fatal(err) + } + for _, include := range []string{ + "SF:src/lib.go\n", + } { + if !strings.Contains(string(combinedCoverageData), include) { + t.Errorf("%s: does not contain %q\n", combinedCoverageData, include) + } + } +} + +const expectedIndividualCoverage = `SF:src/lib.go +FNF:0 +FNH:0 +DA:3,1 +DA:4,1 +DA:5,0 +DA:6,1 +DA:7,1 +DA:8,1 +LH:5 +LF:6 +end_of_record +` diff --git a/tests/core/cross/BUILD.bazel b/tests/core/cross/BUILD.bazel new file mode 100644 index 00000000..ec179984 --- /dev/null +++ b/tests/core/cross/BUILD.bazel @@ -0,0 +1,143 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_cross_binary", "go_library", "go_test") +load("@io_bazel_rules_go//go/tools/bazel_testing:def.bzl", "go_bazel_test") +load(":def.bzl", "no_context_info") + +test_suite( + name = "cross", +) + +go_binary( + name = "windows_cross", + srcs = ["main.go"], + goarch = "amd64", + goos = "windows", + pure = "on", + deps = [":platform_lib"], +) + +go_binary( + name = "linux_cross", + srcs = ["main.go"], + goarch = "amd64", + goos = "linux", + pure = "on", + deps = [":platform_lib"], +) + +go_binary( + name = "darwin_cross", + srcs = ["main.go"], + goarch = "amd64", + goos = "darwin", + pure = "on", + deps = [":platform_lib"], +) + +go_binary( + name = "asm_cross", + srcs = ["asm.s", "main.go"], + goarch = "386", + goos = "linux", + deps = [":platform_lib"], +) + +go_binary( + name = "native_bin", + srcs = ["main.go"], + pure = "on", + deps = [":platform_lib"], +) + +go_cross_binary( + name = "windows_go_cross", + platform = "@io_bazel_rules_go//go/toolchain:windows_amd64", + target = ":native_bin", +) + +go_cross_binary( + name = "linux_go_cross", + platform = "@io_bazel_rules_go//go/toolchain:linux_amd64", + target = ":native_bin", +) + +go_cross_binary( + name = "darwin_go_cross", + platform = "@io_bazel_rules_go//go/toolchain:darwin_amd64", + target = ":native_bin", +) + +go_library( + name = "platform_lib", + srcs = select({ + "//go/platform:darwin": ["lib_darwin.go"], + "//go/platform:linux": ["lib_linux.go"], + "//go/platform:windows": ["lib_windows.go"], + }), + importpath = "github.com/bazelbuild/rules_go/tests/core/cross/platform_lib", +) + +go_test( + name = "cross_test", + size = "small", + srcs = ["cross_test.go"], + args = [ + "-darwin", + "$(location :darwin_cross)", + "-linux", + "$(location :linux_cross)", + "-windows", + "$(location :windows_cross)", + ], + data = [ + ":darwin_cross", + ":linux_cross", + ":windows_cross", + ], + rundir = ".", + deps = ["//go/tools/bazel:go_default_library"], +) + +go_test( + name = "go_cross_binary_test", + size = "small", + srcs = ["cross_test.go"], + args = [ + "-darwin", + "$(location :darwin_go_cross)", + "-linux", + "$(location :linux_go_cross)", + "-windows", + "$(location :windows_go_cross)", + ], + data = [ + ":darwin_go_cross", + ":linux_go_cross", + ":windows_go_cross", + ], + rundir = ".", + deps = ["//go/tools/bazel:go_default_library"], +) + +go_bazel_test( + name = "ios_select_test", + srcs = ["ios_select_test.go"], +) + +go_bazel_test( + name = "proto_test", + srcs = ["proto_test.go"], +) + +go_bazel_test( + name = "sdk_version_test", + srcs = ["sdk_version_test.go"], +) + +go_bazel_test( + name = "non_executable_test", + srcs = ["non_executable_test.go"], +) + +no_context_info( + name = "no_context_info", +) diff --git a/tests/core/cross/README.rst b/tests/core/cross/README.rst new file mode 100644 index 00000000..283eb264 --- /dev/null +++ b/tests/core/cross/README.rst @@ -0,0 +1,62 @@ +Cross compilation +================= + +.. _go_binary: /docs/go/core/rules.md#go_binary +.. _go_library: /docs/go/core/rules.md#go_library +.. _go_cross_binary: /docs/go/core/rules.md#go_cross_binary +.. _#2523: https://github.com/bazelbuild/rules_go/issues/2523 + +Tests to ensure that cross compilation is working as expected. + +.. contents:: + +cross_test +---------- + + +Tests that cross compilation controlled by the ``goos`` and ``goarch`` +attributes on a `go_binary`_ produces executables for the correct platform. + +This builds binaries using `main.go <main.go>`_ in multiple configurations, and +then passes them as data to a test `written in go <cross_test.go>`_. + +The test executes the unix command "file" on the binaries to determine their +type, and checks they were built for the expected architecture. + +The test also checks that `go_library`_ packages imoprted by `go_binary`_ with +``goos`` set are built in the correct configuration, and ``select`` is applied +in that configuration. Each binary depends on ``platform_lib``, which has a +different source file (determined by ``select``) for each platform. The source +files have a ``goos`` suffix, so they will only be built on the right platform. +If the wrong source file is used or if all files are filtered out, the +`go_binary`_ will not build. + +go_cross_test +------------- + +Indentical test to ``cross_test`` except tests using a `go_cross_binary`_ rule wrapping a `go_binary`_ instead of the ``goos`` and ``goarch`` attributes on a `go_binary`_. + +sdk_version_test +---------------- +Tests that a `go_binary`_ wrapped in a `go_cross_binary`_ rule, with the ``sdk_version`` attribute set, produces an executable built with the correct Go SDK version. + +ios_select_test +--------------- + +Tests that we can cross-compile a library for iOS. We should be able to select +a dependency using ``@io_bazel_rules_go//go/platform:darwin``, which is true +when building for iOS (tested by ``ios_select_test``) and macOS +(tested by ``use_ios_lib``). + +proto_test +---------- + +Tests that a ``go_proto_library`` can be cross-compiled, both with +``--platforms`` and with mode attributes. + +no_context_info +--------------- + +Tests that a rule that uses ``@io_bazel_rules_go//go:toolchain`` but does not +depend on any other target can call ``go_context`` without error. Verifies +`#2523`_. diff --git a/tests/core/cross/asm.s b/tests/core/cross/asm.s new file mode 100644 index 00000000..7a5d5219 --- /dev/null +++ b/tests/core/cross/asm.s @@ -0,0 +1,15 @@ +// Example assembly copied from https://github.com/rpccloud/goid + +#include "go_asm.h" +#include "textflag.h" + +#ifdef GOARCH_386 +#define get_tls(r) MOVL TLS, r +#define g(r) 0(r)(TLS*1) +#endif + +TEXT ·getg(SB), NOSPLIT, $0-4 + get_tls(CX) + MOVL g(CX), AX + MOVL AX, ret+0(FP) + RET diff --git a/tests/core/cross/cross_test.go b/tests/core/cross/cross_test.go new file mode 100644 index 00000000..c8ff3b72 --- /dev/null +++ b/tests/core/cross/cross_test.go @@ -0,0 +1,91 @@ +/* Copyright 2017 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 cross_test + +import ( + "flag" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel" +) + +type check struct { + file *string + info []string +} + +var darwin = flag.String("darwin", "", "The darwin binary") +var linux = flag.String("linux", "", "The linux binary") +var windows = flag.String("windows", "", "The windows binary") + +var checks = []check{ + {darwin, []string{ + "Mach-O", + "64-bit", + "executable", + "x86_64", + }}, + {linux, []string{ + "ELF", + "64-bit", + "executable", + "x86-64", + }}, + {windows, []string{ + "PE32+", + "Windows", + "executable", + "console", + "x86-64", + }}, +} + +func TestCross(t *testing.T) { + for _, c := range checks { + path, err := bazel.Runfile(*c.file) + if err != nil { + t.Fatalf("Could not find runfile %s: %q", *c.file, err) + } + + if _, err := os.Stat(path); os.IsNotExist(err) { + t.Fatalf("Missing binary %v", path) + } + file, err := filepath.EvalSymlinks(path) + if err != nil { + t.Fatalf("Invalid filename %v", path) + } + cmd := exec.Command("file", file) + cmd.Stderr = os.Stderr + res, err := cmd.Output() + if err != nil { + t.Fatalf("failed running 'file': %v", err) + } + output := string(res) + if index := strings.Index(output, ":"); index >= 0 { + output = output[index+1:] + } + output = strings.TrimSpace(output) + for _, info := range c.info { + if !strings.Contains(output, info) { + t.Errorf("incorrect type for %v\nExpected %v\nGot %v", file, info, output) + } + } + } +} diff --git a/tests/core/cross/def.bzl b/tests/core/cross/def.bzl new file mode 100644 index 00000000..99ef7c40 --- /dev/null +++ b/tests/core/cross/def.bzl @@ -0,0 +1,24 @@ +# Copyright 2020 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("//go:def.bzl", "go_context") + +def _no_context_info_impl(ctx): + go_context(ctx) + # do nothing and pass if that succeeds + +no_context_info = rule( + implementation = _no_context_info_impl, + toolchains = ["@io_bazel_rules_go//go:toolchain"], +) diff --git a/tests/core/cross/ios_select_test.go b/tests/core/cross/ios_select_test.go new file mode 100644 index 00000000..00bf0032 --- /dev/null +++ b/tests/core/cross/ios_select_test.go @@ -0,0 +1,66 @@ +// 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 ios_select_test + +import ( + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Main: ` +-- BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "use_ios_lib", + importpath = "github.com/bazelbuild/rules_go/tests/core/cross/use_ios_lib", + deps = select({ + ":is_osx": [":ios_lib"], + "//conditions:default": [], + }), +) + +config_setting( + name = "is_osx", + constraint_values = ["@platforms//os:osx"], +) + +go_library( + name = "ios_lib", + srcs = select({ + "@io_bazel_rules_go//go/platform:darwin": ["ios_good.go"], + "@io_bazel_rules_go//go/platform:ios": ["ios_good.go"], + "//conditions:default": ["ios_bad.go"], + }), + importpath = "github.com/bazelbuild/rules_go/tests/core/cross/ios_lib", +) + +-- ios_good.go -- +package ios_lib + +-- ios_bad.go -- +donotbuild +`, + }) +} + +func Test(t *testing.T) { + if err := bazel_testing.RunBazel("build", "--platforms=@io_bazel_rules_go//go/toolchain:ios_amd64", ":ios_lib"); err != nil { + t.Fatal(err) + } +} diff --git a/tests/core/cross/lib_darwin.go b/tests/core/cross/lib_darwin.go new file mode 100644 index 00000000..dfdc059f --- /dev/null +++ b/tests/core/cross/lib_darwin.go @@ -0,0 +1,3 @@ +package platform_lib + +const Platform = "darwin" diff --git a/tests/core/cross/lib_linux.go b/tests/core/cross/lib_linux.go new file mode 100644 index 00000000..0759c9b5 --- /dev/null +++ b/tests/core/cross/lib_linux.go @@ -0,0 +1,3 @@ +package platform_lib + +const Platform = "linux" diff --git a/tests/core/cross/lib_windows.go b/tests/core/cross/lib_windows.go new file mode 100644 index 00000000..6ed0f7ab --- /dev/null +++ b/tests/core/cross/lib_windows.go @@ -0,0 +1,3 @@ +package platform_lib + +const Platform = "windows" diff --git a/tests/core/cross/main.go b/tests/core/cross/main.go new file mode 100644 index 00000000..0d0c6794 --- /dev/null +++ b/tests/core/cross/main.go @@ -0,0 +1,26 @@ +/* Copyright 2016 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 main + +import ( + "fmt" + + "github.com/bazelbuild/rules_go/tests/core/cross/platform_lib" +) + +func main() { + fmt.Println(platform_lib.Platform) +} diff --git a/tests/core/cross/non_executable_test.go b/tests/core/cross/non_executable_test.go new file mode 100644 index 00000000..ed9521c3 --- /dev/null +++ b/tests/core/cross/non_executable_test.go @@ -0,0 +1,106 @@ +// Copyright 2022 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 non_executable_test + +import ( + "regexp" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +var errorRegexp = regexp.MustCompile(`cannot run go_cross target "host_archive": underlying target "@{0,2}//src:archive" is not executable`); + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Main: ` +-- src/BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_cross_binary") +load(":rules.bzl", "no_runfiles_check") + +go_binary( + name = "archive", + srcs = ["archive.go"], + cgo = True, + linkmode = "c-archive", +) + +# We make a new platform here so that we can exercise the go_cross_binary rule. +# However, the test needs to run on all hosts, so the platform needs +# to inherit from the host platform. +platform( + name = "host_cgo", + parents = ["@local_config_platform//:host"], + constraint_values = [ + "@io_bazel_rules_go//go/toolchain:cgo_on", + ], +) + +go_cross_binary( + name = "host_archive", + target = ":archive", + platform = ":host_cgo", +) + +cc_binary( + name = "main", + srcs = ["main.c"], + deps = [":host_archive"], +) + +no_runfiles_check( + name = "no_runfiles", + target = ":main", +) +-- src/archive.go -- +package main + +import "C" + +func main() {} +-- src/main.c -- +int main() {} +-- src/rules.bzl -- +def _no_runfiles_check_impl(ctx): + runfiles = ctx.attr.target[DefaultInfo].default_runfiles.files.to_list() + for runfile in runfiles: + if runfile.short_path not in ["src/main", "src/main.exe"]: + fail("Unexpected runfile: %s" % runfile.short_path) + +no_runfiles_check = rule( + implementation = _no_runfiles_check_impl, + attrs = { + "target": attr.label(), + } +) +`, + }) +} + +func TestNonExecutableGoBinaryCantBeRun(t *testing.T) { + if err := bazel_testing.RunBazel("build", "//src:host_archive"); err != nil { + t.Fatal(err) + } + err := bazel_testing.RunBazel("run", "//src:host_archive") + if err == nil || !errorRegexp.MatchString(err.Error()) { + t.Errorf("Expected bazel run to fail due to //src:host_archive not being executable") + } +} + +func TestNonExecutableGoBinaryNotInRunfiles(t *testing.T) { + if err := bazel_testing.RunBazel("build", "//src:no_runfiles"); err != nil { + t.Fatal(err) + } +} diff --git a/tests/core/cross/proto_test.go b/tests/core/cross/proto_test.go new file mode 100644 index 00000000..cd9e6c60 --- /dev/null +++ b/tests/core/cross/proto_test.go @@ -0,0 +1,126 @@ +// 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 proto_test + +import ( + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +var testArgs = bazel_testing.Args{ + WorkspaceSuffix: ` +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +http_archive( + name = "com_google_protobuf", + sha256 = "75be42bd736f4df6d702a0e4e4d30de9ee40eac024c4b845d17ae4cc831fe4ae", + strip_prefix = "protobuf-21.7", + # latest available in BCR, as of 2022-09-30 + urls = [ + "https://github.com/protocolbuffers/protobuf/archive/v21.7.tar.gz", + "https://mirror.bazel.build/github.com/protocolbuffers/protobuf/archive/v21.7.tar.gz", + ], +) + +load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps") + +protobuf_deps() + +http_archive( + name = "rules_proto", + sha256 = "4d421d51f9ecfe9bf96ab23b55c6f2b809cbaf0eea24952683e397decfbd0dd0", + strip_prefix = "rules_proto-f6b8d89b90a7956f6782a4a3609b2f0eee3ce965", + # master, as of 2020-01-06 + urls = [ + "https://mirror.bazel.build/github.com/bazelbuild/rules_proto/archive/f6b8d89b90a7956f6782a4a3609b2f0eee3ce965.tar.gz", + "https://github.com/bazelbuild/rules_proto/archive/f6b8d89b90a7956f6782a4a3609b2f0eee3ce965.tar.gz", + ], +) +`, + Main: ` +-- BUILD.bazel -- +load("@rules_proto//proto:defs.bzl", "proto_library") +load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") +load("@io_bazel_rules_go//go:def.bzl", "go_binary") + +proto_library( + name = "cross_proto", + srcs = ["cross.proto"], +) + +go_proto_library( + name = "cross_go_proto", + importpath = "github.com/bazelbuild/rules_go/tests/core/cross", + protos = [":cross_proto"], +) + +go_binary( + name = "use_bin", + srcs = ["use.go"], + deps = [":cross_go_proto"], + goos = "linux", + goarch = "386", +) + +go_binary( + name = "use_shared", + srcs = ["use.go"], + deps = [":cross_go_proto"], + linkmode = "c-shared", +) + +-- cross.proto -- +syntax = "proto3"; + +package cross; + +option go_package = "github.com/bazelbuild/rules_go/tests/core/cross"; + +message Foo { + int64 x = 1; +} + +-- use.go -- +package main + +import _ "github.com/bazelbuild/rules_go/tests/core/cross" + +func main() {} +`, +} + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, testArgs) +} + +func TestCmdLine(t *testing.T) { + args := []string{ + "build", + "--platforms=@io_bazel_rules_go//go/toolchain:linux_386", + ":cross_go_proto", + } + if err := bazel_testing.RunBazel(args...); err != nil { + t.Fatal(err) + } +} + +func TestTargets(t *testing.T) { + for _, target := range []string{"//:use_bin", "//:use_shared"} { + if err := bazel_testing.RunBazel("build", target); err != nil { + t.Errorf("building target %s: %v", target, err) + } + } +} diff --git a/tests/core/cross/sdk_version_test.go b/tests/core/cross/sdk_version_test.go new file mode 100644 index 00000000..9cb7ad51 --- /dev/null +++ b/tests/core/cross/sdk_version_test.go @@ -0,0 +1,135 @@ +// Copyright 2022 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 go_download_sdk_test + +import ( + "fmt" + "strings" + "testing" + "text/template" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +type testcase struct { + Name, SDKVersion, expectedVersion string +} + +var testCases = []testcase{ + { + Name: "major_version", + SDKVersion: "1", + expectedVersion: "go1.16", + }, + { + Name: "minor_version", + SDKVersion: "1.16", + expectedVersion: "go1.16", + }, + { + Name: "patch_version", + SDKVersion: "1.16.0", + expectedVersion: "go1.16", + }, + { + Name: "1_17_minor_version", + SDKVersion: "1.17", + expectedVersion: "go1.17", + }, + { + Name: "1_17_patch_version", + SDKVersion: "1.17.1", + expectedVersion: "go1.17.1", + }, +} + +func TestMain(m *testing.M) { + mainFilesTmpl := template.Must(template.New("").Parse(` +-- WORKSPACE -- +local_repository( + name = "io_bazel_rules_go", + path = "../io_bazel_rules_go", +) + +load("@io_bazel_rules_go//go:deps.bzl", "go_download_sdk", "go_rules_dependencies", "go_register_toolchains") + +go_rules_dependencies() + +go_download_sdk( + name = "go_sdk", + version = "1.16", +) +go_download_sdk( + name = "go_sdk_1_17", + version = "1.17", +) +go_download_sdk( + name = "go_sdk_1_17_1", + version = "1.17.1", +) +go_register_toolchains() +-- main.go -- +package main + +import ( + "fmt" + "runtime" +) + +func main() { + fmt.Print(runtime.Version()) +} +-- BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_cross_binary") + +go_binary( + name = "print_version", + srcs = ["main.go"], +) +{{range .TestCases}} +go_cross_binary( + name = "{{.Name}}", + target = ":print_version", + sdk_version = "{{.SDKVersion}}", +) +{{end}} +`)) + tmplValues := struct{ + TestCases []testcase + }{ + TestCases: testCases, + } + mainFilesBuilder := &strings.Builder{} + if err := mainFilesTmpl.Execute(mainFilesBuilder, tmplValues); err != nil { + panic(err) + } + + bazel_testing.TestMain(m, bazel_testing.Args{Main: mainFilesBuilder.String()}) +} + +func Test(t *testing.T) { + for _, test := range testCases { + t.Run(test.Name, func(t *testing.T) { + output, err := bazel_testing.BazelOutput("run", fmt.Sprintf("//:%s", test.Name)) + if err != nil { + t.Fatal(err) + } + actualVersion := string(output) + if actualVersion != test.expectedVersion { + t.Fatal("actual", actualVersion, "vs expected", test.expectedVersion) + } + }) + } +} diff --git a/tests/core/go_bazel_test/BUILD.bazel b/tests/core/go_bazel_test/BUILD.bazel new file mode 100644 index 00000000..5a32ad75 --- /dev/null +++ b/tests/core/go_bazel_test/BUILD.bazel @@ -0,0 +1,9 @@ +load("@io_bazel_rules_go//go/tools/bazel_testing:def.bzl", "go_bazel_test") + +go_bazel_test( + name = "dataargtest_test", + srcs = ["dataargtest_test.go"], + args = ["-binaryPath=$(location //tests/core/go_binary:hello)"], + data = ["//tests/core/go_binary:hello"], + deps = ["//go/tools/bazel:go_default_library"], +) diff --git a/tests/core/go_bazel_test/README.rst b/tests/core/go_bazel_test/README.rst new file mode 100644 index 00000000..073f955f --- /dev/null +++ b/tests/core/go_bazel_test/README.rst @@ -0,0 +1,10 @@ +go_bazel_test macro functionality +================================= + +Tests to ensure the go_bazel_test is functioning correctly. + +dataargtest_test +---------------- + +Tests that `data` and `args` provided to `go_bazel_test` are provided to the go +test framework correctly. diff --git a/tests/core/go_bazel_test/dataargtest_test.go b/tests/core/go_bazel_test/dataargtest_test.go new file mode 100644 index 00000000..9859fe84 --- /dev/null +++ b/tests/core/go_bazel_test/dataargtest_test.go @@ -0,0 +1,69 @@ +// 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 main + +import ( + "flag" + "os" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel" + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +var ( + binaryPath = flag.String("binaryPath", "", "") + setUpRan = false +) + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Main: ` +-- root.txt -- +Hello world! +-- nested/file.txt -- +Hello world!`, + SetUp: func() error { + setUpRan = true + return nil + }, + }) +} + +// Tests that go_bazel_test keeps includes data files correctly and doesn't mess +// up on `args` that include `$(location ...)` calls. +func TestGoldenPath(t *testing.T) { + bp, err := bazel.Runfile(*binaryPath) + if err != nil { + t.Fatalf("unable to get the runfile path %#v: %s", *binaryPath, err) + } + + tests := map[string]string{ + "Go binary file": bp, + "Text file in root": "root.txt", + "Text file in nested dir": "nested/file.txt", + } + + for name, f := range tests { + _, err = os.Stat(f) + if err != nil { + t.Fatalf("unable to stat %s file (%q): %s", name, f, err) + } + } + + if setUpRan == false { + t.Fatal("setUp should have been executed but was not") + } +} diff --git a/tests/core/go_binary/BUILD.bazel b/tests/core/go_binary/BUILD.bazel new file mode 100644 index 00000000..99a2d784 --- /dev/null +++ b/tests/core/go_binary/BUILD.bazel @@ -0,0 +1,193 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") +load("@io_bazel_rules_go//go/tools/bazel_testing:def.bzl", "go_bazel_test") +load(":many_deps.bzl", "many_deps") + +test_suite(name = "go_binary") + +go_bazel_test( + name = "configurable_attribute_bad_test", + srcs = ["configurable_attribute_bad_test.go"], +) + +go_bazel_test( + name = "configurable_attribute_good_test", + srcs = ["configurable_attribute_good_test.go"], +) + +go_binary( + name = "hello", + srcs = ["hello.go"], + visibility = ["//visibility:public"], +) + +go_test( + name = "go_default_test", + srcs = ["out_test.go"], + data = [":custom_bin"], +) + +go_bazel_test( + name = "package_conflict_test", + srcs = ["package_conflict_test.go"], +) + +go_binary( + name = "custom_bin", + srcs = ["custom_bin.go"], + out = "alt_bin", +) + +go_binary( + name = "goos_pure_bin", + srcs = [ + "broken_cgo.go", + "hello.go", + ], + goarch = "amd64", + goos = "plan9", +) + +many_deps(name = "many_deps") + +go_test( + name = "stamp_test", + srcs = ["stamp_test.go"], + data = [":stamp_bin"], + rundir = ".", + deps = ["@io_bazel_rules_go//go/tools/bazel:go_default_library"], +) + +go_binary( + name = "stamp_bin", + srcs = ["stamp_bin.go"], + embed = [":stamp_embed"], + x_defs = { + "Bin": "Bin", + "example.com/stamp_dep.DepBin": "DepBin", + }, + deps = [":stamp_dep"], +) + +go_library( + name = "stamp_embed", + srcs = ["stamp_embed.go"], + importpath = "example.com/stamp_embed", + x_defs = { + "Embed": "Embed", + }, +) + +go_library( + name = "stamp_dep", + srcs = ["stamp_dep.go"], + importpath = "example.com/stamp_dep", + x_defs = { + "DepSelf": "DepSelf", + }, +) + +go_binary( + name = "hello_pie_bin", + srcs = ["hello.go"], + cgo = True, + linkmode = "pie", + tags = ["manual"], +) + +go_binary( + name = "hello_nopie_bin", + srcs = ["hello.go"], + cgo = True, + tags = ["manual"], +) + +go_test( + name = "pie_test", + srcs = [ + "pie_darwin_amd64_test.go", + "pie_darwin_test.go", + "pie_linux_test.go", + ], + data = select({ + "@io_bazel_rules_go//go/platform:darwin": [ + ":hello_nopie_bin", + ":hello_pie_bin", + ], + "@io_bazel_rules_go//go/platform:linux": [ + ":hello_nopie_bin", + ":hello_pie_bin", + ], + "//conditions:default": [], + }), + rundir = ".", + deps = ["@io_bazel_rules_go//go/tools/bazel:go_default_library"], +) + +go_test( + name = "static_test", + srcs = ["static_test.go"], + data = select({ + "@io_bazel_rules_go//go/platform:linux": [ + ":static_bin", + ":static_cgo_bin", + ":static_pure_bin", + ], + "//conditions:default": [], + }), + rundir = ".", + deps = ["//go/tools/bazel:go_default_library"], +) + +go_binary( + name = "static_bin", + srcs = ["static_bin.go"], + static = "on", + tags = ["manual"], + deps = ["@org_golang_x_sys//unix:go_default_library"], +) + +go_binary( + name = "static_cgo_bin", + srcs = ["static_cgo_bin.go"], + cgo = True, + static = "on", + tags = ["manual"], +) + +go_binary( + name = "static_pure_bin", + srcs = ["static_pure_bin.go"], + pure = "on", + static = "on", + tags = ["manual"], +) + +go_binary( + name = "tags_bin", + srcs = [ + "tags_main_bad.go", + "tags_main_good.go", + ], + gotags = ["good"], + deps = [":tags_lib"], +) + +go_library( + name = "tags_lib", + srcs = [ + "tags_lib_bad.go", + "tags_lib_good.go", + ], + importpath = "tags_lib", + tags = ["manual"], +) + +go_binary( + name = "prefix", + embed = ["//tests/core/go_binary/prefix"], +) + +go_bazel_test( + name = "non_executable_test", + srcs = ["non_executable_test.go"], +) diff --git a/tests/core/go_binary/README.rst b/tests/core/go_binary/README.rst new file mode 100644 index 00000000..a01bdf60 --- /dev/null +++ b/tests/core/go_binary/README.rst @@ -0,0 +1,70 @@ +Basic go_binary functionality +============================= + +.. _go_binary: /docs/go/core/rules.md#_go_binary +.. _#2168: https://github.com/bazelbuild/rules_go/issues/2168 +.. _#2463: https://github.com/bazelbuild/rules_go/issues/2463 + +Tests to ensure the basic features of go_binary are working as expected. + +hello +----- + +Hello is a basic "hello world" program that doesn't do anything interesting. +Useful as a primitive smoke test -- if this doesn't build, nothing will. + +out_test +-------- + +Tests that a `go_binary`_ rule can write its executable file with a custom name +in the package directory (not the mode directory). + +package_conflict_test +--------------------- + +Tests that linking multiple packages with the same path (`importmap`) is an +error. + +goos_pure_bin +------------- + +Tests that specifying the `goos` attribute on a `go_binary`_ target to be +different than the host os forces the pure mode to be on. This is achieved +by including a broken cgo file in the sources for the build. + +many_deps +--------- + +Test that a `go_binary`_ with many imports with long names can be linked. This +makes sure we don't exceed command-line length limits with -I and -L flags. +Verifies #1637. + +stamp_test +---------- +Test that the `go_binary`_ ``x_defs`` attribute works correctly, both in a +binary and in an embedded library. Tests regular stamps and stamps that +depend on values from the workspace status script. Verifies #2000. + +pie_test +-------- +Tests that specifying the ``linkmode`` attribute on a `go_binary`_ target to be +pie produces a position-independent executable and that no specifying it produces +a position-dependent binary. + +static_test +----------- +Test that `go_binary`_ rules with ``static = "on"`` with and without cgo +produce static binaries. Verifies `#2168`_. + +This test only runs on Linux. The darwin external linker cannot produce +static binaries since there is no static version of C runtime libraries. + +tags_bin +-------- +Checks that setting ``gotags`` affects source filtering. This binary won't build +without a specific tag being set. + +prefix +------ +This binary has a name that conflicts with a subdirectory. Its output file +name should not have this conflict. Verifies `#2463`_. diff --git a/tests/core/go_binary/broken_cgo.go b/tests/core/go_binary/broken_cgo.go new file mode 100644 index 00000000..0f0b6051 --- /dev/null +++ b/tests/core/go_binary/broken_cgo.go @@ -0,0 +1,7 @@ +// +build cgo + +// This file will not compile and its inclusion in a build is used to ensure +// that a binary was built in pure mode. +package main + +import "non/existent/pkg" diff --git a/tests/core/go_binary/configurable_attribute_bad_test.go b/tests/core/go_binary/configurable_attribute_bad_test.go new file mode 100644 index 00000000..abd35ba2 --- /dev/null +++ b/tests/core/go_binary/configurable_attribute_bad_test.go @@ -0,0 +1,69 @@ +// Copyright 2022 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 configurable_attribute_bad_test + +import ( + "strings" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Main: ` +-- BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_binary") + +go_binary( + name = "main", + srcs = [ + "main.go", + ], + goos = "darwin", + goarch = "amd64", + gotags = select({ + "@io_bazel_rules_go//go/platform:linux": ["penguins"], + "//conditions:default": ["nopenguins"], + }), +) + +-- main.go -- +package main + +import "fmt" + +func main() { + fmt.Println("Howdy") +} +`, + }) +} + +func TestConfigurableGotagsAttribute(t *testing.T) { + _, err := bazel_testing.BazelOutput("build", "//:main") + if err == nil { + t.Fatal("Want error") + } + eErr, ok := err.(*bazel_testing.StderrExitError) + if !ok { + t.Fatalf("Want StderrExitError but got %v", err) + } + stderr := eErr.Error() + want := "Cannot use select for go_binary with goos/goarch set, but gotags was a select" + if !strings.Contains(stderr, want) { + t.Fatalf("Want error message containing %q but got %v", want, stderr) + } +} diff --git a/tests/core/go_binary/configurable_attribute_good_test.go b/tests/core/go_binary/configurable_attribute_good_test.go new file mode 100644 index 00000000..a1fe1954 --- /dev/null +++ b/tests/core/go_binary/configurable_attribute_good_test.go @@ -0,0 +1,92 @@ +// Copyright 2022 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 configurable_attribute_good_test + +import ( + "runtime" + "strings" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Main: ` +-- BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_binary") + +go_binary( + name = "main", + srcs = [ + "main.go", + "lib_nopenguins.go", + "lib_penguins.go", + ], + gotags = select({ + "@io_bazel_rules_go//go/platform:linux": ["penguins"], + "//conditions:default": ["nopenguins"], + }), +) + +-- main.go -- +package main + +import "fmt" + +func main() { + fmt.Println(message()) +} + +-- lib_penguins.go -- +// +build penguins + +package main + +func message() string { + return "Penguins are great" +} + + +-- lib_nopenguins.go -- +// +build !penguins + +package main + +func message() string { + return "Penguins smell fishy'" +} +`, + }) +} + +func TestConfigurableGotagsAttribute(t *testing.T) { + outBytes, err := bazel_testing.BazelOutput("run", "//:main") + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + out := string(outBytes) + os := runtime.GOOS + switch os { + case "linux": + if !strings.Contains(out, "Penguins are great") { + t.Fatalf("Wanted penguin executable, but output was: %s", out) + } + default: + if !strings.Contains(out, "Penguins smell fishy") { + t.Fatalf("Wanted nopenguin executable, but output was: %s", out) + } + } +} diff --git a/tests/core/go_binary/custom_bin.go b/tests/core/go_binary/custom_bin.go new file mode 100644 index 00000000..da29a2ca --- /dev/null +++ b/tests/core/go_binary/custom_bin.go @@ -0,0 +1,4 @@ +package main + +func main() { +} diff --git a/tests/core/go_binary/hello.go b/tests/core/go_binary/hello.go new file mode 100644 index 00000000..f7b60bde --- /dev/null +++ b/tests/core/go_binary/hello.go @@ -0,0 +1,7 @@ +package main + +import "fmt" + +func main() { + fmt.Println("Hello, world!") +} diff --git a/tests/core/go_binary/many_deps.bzl b/tests/core/go_binary/many_deps.bzl new file mode 100644 index 00000000..621e286d --- /dev/null +++ b/tests/core/go_binary/many_deps.bzl @@ -0,0 +1,92 @@ +# Copyright 2018 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_binary", + "go_context", +) + +_PREFIX = "/".join(["abcdefgh"[i] * 100 for i in range(7)]) + "/" + +def _gen_library_impl(ctx): + go = go_context(ctx) + src = go.actions.declare_file(ctx.label.name + ".go") + go.actions.write(src, "package " + ctx.label.name + "\n") + library = go.new_library(go, srcs = [src]) + source = go.library_to_source(go, ctx.attr, library, ctx.coverage_instrumented()) + archive = go.archive(go, source) + return [ + library, + source, + archive, + DefaultInfo(files = depset([archive.data.file])), + ] + +_gen_library = rule( + _gen_library_impl, + attrs = { + "importpath": attr.string(mandatory = True), + "_go_context_data": attr.label( + default = "//:go_context_data", + ), + }, + toolchains = ["@io_bazel_rules_go//go:toolchain"], +) + +def _gen_main_src_impl(ctx): + src = ctx.actions.declare_file(ctx.label.name + ".go") + lines = [ + "package main", + "", + "import (", + ] + for i in range(ctx.attr.n): + lines.append('\t_ "{}many_deps{}"'.format(_PREFIX, i)) + lines.extend([ + ")", + "", + "func main() {}", + ]) + ctx.actions.write(src, "\n".join(lines)) + return [DefaultInfo(files = depset([src]))] + +_gen_main_src = rule( + _gen_main_src_impl, + attrs = { + "n": attr.int(mandatory = True), + }, +) + +def many_deps(name, **kwargs): + deps = [] + n = 200 + for i in range(n): + lib_name = "many_deps" + str(i) + _gen_library( + name = lib_name, + importpath = _PREFIX + lib_name, + visibility = ["//visibility:private"], + ) + deps.append(lib_name) + _gen_main_src( + name = "many_deps_src", + n = n, + ) + go_binary( + name = name, + srcs = [":many_deps_src"], + deps = deps, + **kwargs + ) diff --git a/tests/core/go_binary/non_executable_test.go b/tests/core/go_binary/non_executable_test.go new file mode 100644 index 00000000..b2eb987c --- /dev/null +++ b/tests/core/go_binary/non_executable_test.go @@ -0,0 +1,87 @@ +// Copyright 2022 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 non_executable_test + +import ( + "strings" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Main: ` +-- src/BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_binary") +load(":rules.bzl", "no_runfiles_check") + +go_binary( + name = "archive", + srcs = ["archive.go"], + cgo = True, + linkmode = "c-archive", +) + +cc_binary( + name = "main", + srcs = ["main.c"], + deps = [":archive"], +) + +no_runfiles_check( + name = "no_runfiles", + target = ":main", +) +-- src/archive.go -- +package main + +import "C" + +func main() {} +-- src/main.c -- +int main() {} +-- src/rules.bzl -- +def _no_runfiles_check_impl(ctx): + runfiles = ctx.attr.target[DefaultInfo].default_runfiles.files.to_list() + for runfile in runfiles: + if runfile.short_path not in ["src/main", "src/main.exe"]: + fail("Unexpected runfile: %s" % runfile.short_path) + +no_runfiles_check = rule( + implementation = _no_runfiles_check_impl, + attrs = { + "target": attr.label(), + } +) +`, + }) +} + +func TestNonExecutableGoBinaryCantBeRun(t *testing.T) { + if err := bazel_testing.RunBazel("build", "//src:archive"); err != nil { + t.Fatal(err) + } + err := bazel_testing.RunBazel("run", "//src:archive") + if err == nil || !strings.Contains(err.Error(), "ERROR: Cannot run target //src:archive: Not executable") { + t.Errorf("Expected bazel run to fail due to //src:archive not being executable") + } +} + +func TestNonExecutableGoBinaryNotInRunfiles(t *testing.T) { + if err := bazel_testing.RunBazel("build", "//src:no_runfiles"); err != nil { + t.Fatal(err) + } +} diff --git a/tests/core/go_binary/out_test.go b/tests/core/go_binary/out_test.go new file mode 100644 index 00000000..7c6ba7f3 --- /dev/null +++ b/tests/core/go_binary/out_test.go @@ -0,0 +1,13 @@ +package main + +import ( + "os" + "testing" +) + +func TestCustomBinaryName(t *testing.T) { + _, err := os.Stat("alt_bin") + if err != nil { + t.Error(err) + } +} diff --git a/tests/core/go_binary/package_conflict_test.go b/tests/core/go_binary/package_conflict_test.go new file mode 100644 index 00000000..9ec6340a --- /dev/null +++ b/tests/core/go_binary/package_conflict_test.go @@ -0,0 +1,120 @@ +// Copyright 2020 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 package_conflict_test + +import ( + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Main: ` +-- BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") + +go_library( + name = "foo_de", + importpath = "github.com/bazelbuild/rules_go/tests/core/package_conflict/foo", + srcs = ["foo_de.go"], +) + +go_library( + name = "de", + importpath = "github.com/bazelbuild/rules_go/tests/core/package_conflict/de", + srcs = ["de.go"], + deps = [":foo_de"], +) + +go_library( + name = "foo_en", + importpath = "github.com/bazelbuild/rules_go/tests/core/package_conflict/foo", + srcs = ["foo_en.go"], +) + +go_library( + name = "en", + importpath = "github.com/bazelbuild/rules_go/tests/core/package_conflict/en", + srcs = ["en.go"], + deps = [":foo_en"], +) + +go_binary( + name = "main", + srcs = ["main.go"], + deps = [ + ":de", + ":en", + ], +) + +-- foo_en.go -- +package foo + +import "fmt" + +func SayHello() { + fmt.Println("Hello, World!") +} + +-- en.go -- +package en + +import "github.com/bazelbuild/rules_go/tests/core/package_conflict/foo" + +func SayHello() { + foo.SayHello() +} + +-- foo_de.go -- +package foo + +import "fmt" + +func SayHello() { + fmt.Println("Hallo, Welt!") +} + +-- de.go -- +package de + +import "github.com/bazelbuild/rules_go/tests/core/package_conflict/foo" + +func SayHello() { + foo.SayHello() +} + +-- main.go -- +package main + +import ( + "github.com/bazelbuild/rules_go/tests/core/package_conflict/de" + "github.com/bazelbuild/rules_go/tests/core/package_conflict/en" +) + +func main() { + de.SayHello() + en.SayHello() +} +`, + }) +} + +func TestPackageConflict(t *testing.T) { + if err := bazel_testing.RunBazel("build", "//:main"); err == nil { + t.Fatal("Expected error") + } +} diff --git a/tests/core/go_binary/pie_darwin_amd64_test.go b/tests/core/go_binary/pie_darwin_amd64_test.go new file mode 100644 index 00000000..9a07f955 --- /dev/null +++ b/tests/core/go_binary/pie_darwin_amd64_test.go @@ -0,0 +1,31 @@ +// Copyright 2021 Google LLC +// +// 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 +// +// https://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 test + +import ( + "debug/macho" + "testing" +) + +func TestNoPIE(t *testing.T) { + m, err := openMachO("tests/core/go_binary", "hello_nopie_bin") + if err != nil { + t.Fatal(err) + } + + if m.Flags&macho.FlagPIE != 0 { + t.Error("ELF binary is not position-dependent.") + } +} diff --git a/tests/core/go_binary/pie_darwin_test.go b/tests/core/go_binary/pie_darwin_test.go new file mode 100644 index 00000000..2f45b878 --- /dev/null +++ b/tests/core/go_binary/pie_darwin_test.go @@ -0,0 +1,35 @@ +package test + +import ( + "debug/macho" + "fmt" + "os" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel" +) + +func openMachO(dir, bin string) (*macho.File, error) { + bin, ok := bazel.FindBinary(dir, bin) + if !ok { + return nil, fmt.Errorf("could not find binary: %s", bin) + } + + f, err := os.Open(bin) + if err != nil { + return nil, err + } + + return macho.NewFile(f) +} + +func TestPIE(t *testing.T) { + m, err := openMachO("tests/core/go_binary", "hello_pie_bin") + if err != nil { + t.Fatal(err) + } + + if m.Flags&macho.FlagPIE == 0 { + t.Error("ELF binary is not position-independent.") + } +} diff --git a/tests/core/go_binary/pie_linux_test.go b/tests/core/go_binary/pie_linux_test.go new file mode 100644 index 00000000..8b135500 --- /dev/null +++ b/tests/core/go_binary/pie_linux_test.go @@ -0,0 +1,48 @@ +package test + +import ( + "debug/elf" + "fmt" + "os" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel" +) + +func openELF(dir, bin string) (*elf.File, error) { + bin, ok := bazel.FindBinary(dir, bin) + if !ok { + return nil, fmt.Errorf("could not find binary: %s", bin) + } + + f, err := os.Open(bin) + if err != nil { + return nil, err + } + + return elf.NewFile(f) +} + +func TestPIE(t *testing.T) { + e, err := openELF("tests/core/go_binary", "hello_pie_bin") + if err != nil { + t.Fatal(err) + } + + // PIE binaries are implemented as shared libraries. + if e.Type != elf.ET_DYN { + t.Error("ELF binary is not position-independent.") + } +} + +func TestNoPIE(t *testing.T) { + e, err := openELF("tests/core/go_binary", "hello_nopie_bin") + if err != nil { + t.Fatal(err) + } + + // PIE binaries are implemented as shared libraries. + if e.Type != elf.ET_EXEC { + t.Error("ELF binary is not position-dependent.") + } +} diff --git a/tests/core/go_binary/prefix/BUILD.bazel b/tests/core/go_binary/prefix/BUILD.bazel new file mode 100644 index 00000000..aff7406b --- /dev/null +++ b/tests/core/go_binary/prefix/BUILD.bazel @@ -0,0 +1,8 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "prefix", + srcs = ["prefix.go"], + importpath = "github.com/bazelbuild/rules_go/tests/core/go_binary/prefix", + visibility = ["//tests/core/go_binary:__pkg__"], +) diff --git a/tests/core/go_binary/prefix/prefix.go b/tests/core/go_binary/prefix/prefix.go new file mode 100644 index 00000000..38dd16da --- /dev/null +++ b/tests/core/go_binary/prefix/prefix.go @@ -0,0 +1,3 @@ +package main + +func main() {} diff --git a/tests/core/go_binary/stamp_bin.go b/tests/core/go_binary/stamp_bin.go new file mode 100644 index 00000000..e227be4c --- /dev/null +++ b/tests/core/go_binary/stamp_bin.go @@ -0,0 +1,16 @@ +package main + +import ( + "fmt" + + "example.com/stamp_dep" +) + +var Bin = "redacted" + +func main() { + fmt.Printf("Bin=%s\n", Bin) + fmt.Printf("Embed=%s\n", Embed) + fmt.Printf("DepSelf=%s\n", stamp_dep.DepSelf) + fmt.Printf("DepBin=%s\n", stamp_dep.DepBin) +} diff --git a/tests/core/go_binary/stamp_dep.go b/tests/core/go_binary/stamp_dep.go new file mode 100644 index 00000000..37aa4635 --- /dev/null +++ b/tests/core/go_binary/stamp_dep.go @@ -0,0 +1,6 @@ +package stamp_dep + +var ( + DepSelf = "redacted" + DepBin = "redacted" +) diff --git a/tests/core/go_binary/stamp_embed.go b/tests/core/go_binary/stamp_embed.go new file mode 100644 index 00000000..8989e914 --- /dev/null +++ b/tests/core/go_binary/stamp_embed.go @@ -0,0 +1,3 @@ +package main + +var Embed = "redacted" diff --git a/tests/core/go_binary/stamp_test.go b/tests/core/go_binary/stamp_test.go new file mode 100644 index 00000000..506cce5c --- /dev/null +++ b/tests/core/go_binary/stamp_test.go @@ -0,0 +1,29 @@ +package main + +import ( + "os/exec" + "strings" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel" +) + +func TestStamp(t *testing.T) { + bin, ok := bazel.FindBinary("tests/core/go_binary", "stamp_bin") + if !ok { + t.Error("could not find stamp_bin") + } + out, err := exec.Command(bin).Output() + if err != nil { + t.Fatal(err) + } + + got := strings.TrimSpace(string(out)) + want := `Bin=Bin +Embed=Embed +DepSelf=DepSelf +DepBin=DepBin` + if got != want { + t.Errorf("got:\n%s\nwant:\n%s", got, want) + } +} diff --git a/tests/core/go_binary/static_bin.go b/tests/core/go_binary/static_bin.go new file mode 100644 index 00000000..7e0ab83b --- /dev/null +++ b/tests/core/go_binary/static_bin.go @@ -0,0 +1,7 @@ +package main + +import ( + _ "golang.org/x/sys/unix" // See https://github.com/bazelbuild/rules_go/issues/2168 +) + +func main() {} diff --git a/tests/core/go_binary/static_cgo_bin.go b/tests/core/go_binary/static_cgo_bin.go new file mode 100644 index 00000000..65676fd9 --- /dev/null +++ b/tests/core/go_binary/static_cgo_bin.go @@ -0,0 +1,14 @@ +package main + +/* +#include <stdio.h> + +void say_hello() { + printf("hello\n"); +} +*/ +import "C" + +func main() { + C.say_hello() +} diff --git a/tests/core/go_binary/static_pure_bin.go b/tests/core/go_binary/static_pure_bin.go new file mode 100644 index 00000000..1dbaa12e --- /dev/null +++ b/tests/core/go_binary/static_pure_bin.go @@ -0,0 +1,8 @@ +package main + +import ( + _ "net" + _ "os" +) + +func main() {} diff --git a/tests/core/go_binary/static_test.go b/tests/core/go_binary/static_test.go new file mode 100644 index 00000000..7c23cdae --- /dev/null +++ b/tests/core/go_binary/static_test.go @@ -0,0 +1,45 @@ +// 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. + +// +build linux + +package static_cgo_test + +import ( + "debug/elf" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel" +) + +func TestStatic(t *testing.T) { + for _, name := range []string{"static_bin", "static_cgo_bin", "static_pure_bin"} { + t.Run(name, func(t *testing.T) { + path, ok := bazel.FindBinary("tests/core/go_binary", name) + if !ok { + t.Fatal("could not find static_cgo_bin") + } + f, err := elf.Open(path) + if err != nil { + t.Fatal(err) + } + defer f.Close() + for _, prog := range f.Progs { + if prog.Type == elf.PT_INTERP { + t.Fatalf("binary %s has PT_INTERP segment, indicating dynamic linkage", path) + } + } + }) + } +} diff --git a/tests/core/go_binary/tags_lib_bad.go b/tests/core/go_binary/tags_lib_bad.go new file mode 100644 index 00000000..30deb5ed --- /dev/null +++ b/tests/core/go_binary/tags_lib_bad.go @@ -0,0 +1,5 @@ +// +build !good + +package lib + +var Does Not = Compile diff --git a/tests/core/go_binary/tags_lib_good.go b/tests/core/go_binary/tags_lib_good.go new file mode 100644 index 00000000..55c21f80 --- /dev/null +++ b/tests/core/go_binary/tags_lib_good.go @@ -0,0 +1 @@ +package lib diff --git a/tests/core/go_binary/tags_main_bad.go b/tests/core/go_binary/tags_main_bad.go new file mode 100644 index 00000000..444667ba --- /dev/null +++ b/tests/core/go_binary/tags_main_bad.go @@ -0,0 +1,5 @@ +// +build !good + +package main + +var Does Not = Compile diff --git a/tests/core/go_binary/tags_main_good.go b/tests/core/go_binary/tags_main_good.go new file mode 100644 index 00000000..ee15bee7 --- /dev/null +++ b/tests/core/go_binary/tags_main_good.go @@ -0,0 +1,8 @@ +// +build good + +package main + +import _ "tags_lib" + +func main() { +} diff --git a/tests/core/go_download_sdk/BUILD.bazel b/tests/core/go_download_sdk/BUILD.bazel new file mode 100644 index 00000000..f294f987 --- /dev/null +++ b/tests/core/go_download_sdk/BUILD.bazel @@ -0,0 +1,6 @@ +load("@io_bazel_rules_go//go/tools/bazel_testing:def.bzl", "go_bazel_test") + +go_bazel_test( + name = "go_download_sdk_test", + srcs = ["go_download_sdk_test.go"], +) diff --git a/tests/core/go_download_sdk/README.rst b/tests/core/go_download_sdk/README.rst new file mode 100644 index 00000000..78e93118 --- /dev/null +++ b/tests/core/go_download_sdk/README.rst @@ -0,0 +1,7 @@ +go_download_sdk +=============== + +go_download_sdk_test +-------------------- +Verifies that ``go_downlaod_sdk`` can be used to download a specific version +or a set of archives for various platforms. diff --git a/tests/core/go_download_sdk/go_download_sdk_test.go b/tests/core/go_download_sdk/go_download_sdk_test.go new file mode 100644 index 00000000..61bd05b8 --- /dev/null +++ b/tests/core/go_download_sdk/go_download_sdk_test.go @@ -0,0 +1,187 @@ +// 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 go_download_sdk_test + +import ( + "bytes" + "io/ioutil" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Main: ` +-- BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "version_test", + srcs = ["version_test.go"], +) + +-- version_test.go -- +package version_test + +import ( + "flag" + "runtime" + "testing" +) + +var want = flag.String("version", "", "") + +func Test(t *testing.T) { + if v := runtime.Version(); v != *want { + t.Errorf("got version %q; want %q", v, *want) + } +} +`, + }) +} + +func Test(t *testing.T) { + for _, test := range []struct { + desc, rule string + optToWantVersion map[string]string + fetchOnly string + }{ + { + desc: "version", + rule: ` +load("@io_bazel_rules_go//go:deps.bzl", "go_download_sdk") + +go_download_sdk( + name = "go_sdk", + version = "1.16", +) + +`, + optToWantVersion: map[string]string{"": "go1.16"}, + }, + { + desc: "custom_archives", + rule: ` +load("@io_bazel_rules_go//go:deps.bzl", "go_download_sdk") + +go_download_sdk( + name = "go_sdk", + sdks = { + "darwin_amd64": ("go1.16.darwin-amd64.tar.gz", "6000a9522975d116bf76044967d7e69e04e982e9625330d9a539a8b45395f9a8"), + "darwin_arm64": ("go1.16.darwin-arm64.tar.gz", "4dac57c00168d30bbd02d95131d5de9ca88e04f2c5a29a404576f30ae9b54810"), + "linux_amd64": ("go1.16.linux-amd64.tar.gz", "013a489ebb3e24ef3d915abe5b94c3286c070dfe0818d5bca8108f1d6e8440d2"), + "windows_amd64": ("go1.16.windows-amd64.zip", "5cc88fa506b3d5c453c54c3ea218fc8dd05d7362ae1de15bb67986b72089ce93"), + }, +) +`, + optToWantVersion: map[string]string{"": "go1.16"}, + }, + { + desc: "multiple_sdks", + rule: ` +load("@io_bazel_rules_go//go:deps.bzl", "go_download_sdk", "go_host_sdk") + +go_download_sdk( + name = "go_sdk", + version = "1.16", +) +go_download_sdk( + name = "go_sdk_1_17", + version = "1.17", +) +go_download_sdk( + name = "go_sdk_1_17_1", + version = "1.17.1", +) +`, + optToWantVersion: map[string]string{ + "": "go1.16", + "--@io_bazel_rules_go//go/toolchain:sdk_version=remote": "go1.16", + "--@io_bazel_rules_go//go/toolchain:sdk_version=1": "go1.16", + "--@io_bazel_rules_go//go/toolchain:sdk_version=1.17": "go1.17", + "--@io_bazel_rules_go//go/toolchain:sdk_version=1.17.0": "go1.17", + "--@io_bazel_rules_go//go/toolchain:sdk_version=1.17.1": "go1.17.1", + }, + }, + { + // Cover workaround for #2771. + desc: "windows_zip", + rule: ` +load("@io_bazel_rules_go//go:deps.bzl", "go_download_sdk") + +go_download_sdk( + name = "go_sdk", + goarch = "amd64", + goos = "windows", + version = "1.20.4", +) +`, + fetchOnly: "@go_sdk//:BUILD.bazel", + }, + } { + t.Run(test.desc, func(t *testing.T) { + origWorkspaceData, err := ioutil.ReadFile("WORKSPACE") + if err != nil { + t.Fatal(err) + } + + i := bytes.Index(origWorkspaceData, []byte("go_rules_dependencies()")) + if i < 0 { + t.Fatal("could not find call to go_rules_dependencies()") + } + + buf := &bytes.Buffer{} + buf.Write(origWorkspaceData[:i]) + buf.WriteString(test.rule) + buf.WriteString(` +go_rules_dependencies() + +go_register_toolchains() +`) + if err := ioutil.WriteFile("WORKSPACE", buf.Bytes(), 0666); err != nil { + t.Fatal(err) + } + defer func() { + if err := ioutil.WriteFile("WORKSPACE", origWorkspaceData, 0666); err != nil { + t.Errorf("error restoring WORKSPACE: %v", err) + } + }() + + if test.fetchOnly != "" { + if err := bazel_testing.RunBazel("fetch", test.fetchOnly); err != nil { + t.Fatal(err) + } + return + } + + for opt, wantVersion := range test.optToWantVersion { + t.Run(wantVersion, func(t *testing.T) { + args := []string{ + "test", + "//:version_test", + "--test_arg=-version=" + wantVersion, + } + if opt != "" { + args = append(args, opt) + } + if err := bazel_testing.RunBazel(args...); err != nil { + t.Fatal(err) + } + }) + } + }) + } +} diff --git a/tests/core/go_library/BUILD.bazel b/tests/core/go_library/BUILD.bazel new file mode 100644 index 00000000..25293cbe --- /dev/null +++ b/tests/core/go_library/BUILD.bazel @@ -0,0 +1,168 @@ +load("//go:def.bzl", "go_binary", "go_library", "go_test") +load("//go/tools/bazel_testing:def.bzl", "go_bazel_test") +load(":def.bzl", "embedsrcs_files") + +go_library( + name = "empty", + srcs = [ + # foo and bar have different package names + "empty_foo.go", + "empty_bar.go", + "empty_baz.s", + ], + importpath = "empty", +) + +go_library( + name = "asm_include", + srcs = [ + "inc.go", + "inc_bar.s", + "inc_foo.s", + ], + importpath = "asm_include", +) + +go_library( + name = "asm_header", + srcs = [ + "inc.go", + "inc_asmhdr.s", + ], + importpath = "asm_header", +) + +go_library( + name = "package_height", + srcs = ["package_height.go"], + importpath = "package_height", + deps = [ + ":package_height_dep_deep", + ":package_height_embedder", + ], +) + +go_library( + name = "package_height_embedder", + srcs = ["package_height_embedder.go"], + embed = [":package_height_embeddee"], + importpath = "package_height/embed", + deps = [":package_height_dep_deep"], +) + +go_library( + name = "package_height_embeddee", + srcs = ["package_height_embeddee.go"], + importpath = "package_height/embed", + deps = [":package_height_dep_shallow"], +) + +go_library( + name = "package_height_dep_deep", + srcs = ["package_height_dep_deep.go"], + importpath = "package_height/dep", +) + +go_library( + name = "package_height_dep_shallow", + srcs = ["package_height_dep_shallow.go"], + importpath = "package_height/dep", +) + +go_test( + name = "import_alias_test", + srcs = ["import_alias_test.go"], + deps = [ + ":import_alias_a_v2", + ":import_alias_b", + ":import_alias_b_v2", + ], +) + +go_library( + name = "import_alias_a_v2", + srcs = ["import_alias_a_v2.go"], + importpath = "import_alias/a/v2", + importpath_aliases = ["import_alias/a"], +) + +go_library( + name = "import_alias_b", + srcs = ["import_alias_b.go"], + importpath = "import_alias/b", +) + +go_library( + name = "import_alias_b_v2", + importpath = "import_alias/b/v2", + importpath_aliases = ["import_alias/b"], +) + +go_test( + name = "embedsrcs_test", + srcs = [ + "embedsrcs_gen_test.go", + "embedsrcs_test.go", + ], + embedsrcs = [ + ":embedsrcs_transitioned", + ":embedsrcs_dynamic", + "embedsrcs_test.go", + ] + glob(["embedsrcs_static/**"]), +) + +genrule( + name = "embedsrcs_gen", + srcs = ["embedsrcs_gen_test.go.in"], + outs = ["embedsrcs_gen_test.go"], + cmd = "cp $< $@", +) + +embedsrcs_files( + name = "embedsrcs_dynamic", + files = [ + "dir/_no", + "dir/f", + "empty/", + "file", + "glob/_hidden", + "glob/f", + "no", + ], +) + +go_binary( + name = "embedsrcs_transitioned", + srcs = ["empty_main.go"], + out = "embedsrcs_transitioned", + # Causes a transition on the incoming dependency edge. + race = "on", +) + +go_binary( + name = "gen_embedsrcs_files", + srcs = ["gen_embedsrcs_files.go"], +) + +go_bazel_test( + name = "embedsrcs_error_test", + size = "medium", + srcs = ["embedsrcs_error_test.go"], +) + +go_test( + name = "embedsrcs_simple_test", + srcs = ["embedsrcs_simple_test.go"], + embedsrcs = ["embedsrcs_static/no"], +) + +go_bazel_test( + name = "no_srcs_test", + size = "medium", + srcs = ["no_srcs_test.go"], +) + +go_library( + name = "no_srcs_lib", + importpath = "github.com/bazelbuild/rules_go/tests/core/no_srcs_lib", +) diff --git a/tests/core/go_library/README.rst b/tests/core/go_library/README.rst new file mode 100644 index 00000000..4410902f --- /dev/null +++ b/tests/core/go_library/README.rst @@ -0,0 +1,57 @@ +Basic go_library functionality +============================== + +.. _go_library: /docs/go/core/rules.md#_go_library +.. _#1262: https://github.com/bazelbuild/rules_go/issues/1262 +.. _#1520: https://github.com/bazelbuild/rules_go/issues/1520 +.. _#1772: https://github.com/bazelbuild/rules_go/issues/1772 +.. _#2058: https://github.com/bazelbuild/rules_go/issues/2058 +.. _#3558: https://github.com/bazelbuild/rules_go/issues/3558 + +empty +----- + +Checks that a `go_library`_ will compile and link even if all the sources +(including assembly sources) are filtered out by build constraints. + +asm_include +----------- + +Checks that assembly files in a `go_library`_ may include other assembly +files in the same library. Verifies `#1520`_. + +asm_header +---------- + +Checks that assembly files in a `go_library`_ may include ``"go_asm.h"``, +generated by the compiler. Verifies `#1262`_. + +package_height +-------------- + +Checks that when a library embeds another library, the embedder's dependencies +may override the embeddee's dependencies. Verifies `#1772`_. + +import_alias_test +----------------- + +Checks that a library may import another library using one of the strings +listed in ``importpath_aliases``. This is the basic mechanism for minimal +module compatibility. Verifies `#2058`_. + +embedsrcs_test +-------------- + +Checks that `go_library`_ can match ``//go:embed`` directives to files listed +in the ``embedsrcs`` attribute and can pass those files to the compiler. + +embedsrcs_error_test +-------------------- + +Verifies common errors with ``//go:embed`` directives are correctly reported. + +no_srcs_test +------------ + +Verifies that `go_library`_ targets without Go source files build concurrently, +even unsandboxed, and reproducibly. Verifies `#3558`_.
\ No newline at end of file diff --git a/tests/core/go_library/def.bzl b/tests/core/go_library/def.bzl new file mode 100644 index 00000000..613ea84d --- /dev/null +++ b/tests/core/go_library/def.bzl @@ -0,0 +1,22 @@ +def _embedsrcs_files_impl(ctx): + name = ctx.attr.name + dir = ctx.actions.declare_directory(name) + args = [dir.path] + ctx.attr.files + ctx.actions.run( + outputs = [dir], + executable = ctx.executable._gen, + arguments = args, + ) + return [DefaultInfo(files = depset([dir]))] + +embedsrcs_files = rule( + implementation = _embedsrcs_files_impl, + attrs = { + "files": attr.string_list(), + "_gen": attr.label( + default = ":gen_embedsrcs_files", + executable = True, + cfg = "exec", + ), + }, +) diff --git a/tests/core/go_library/embedsrcs_error_test.go b/tests/core/go_library/embedsrcs_error_test.go new file mode 100644 index 00000000..73629ddf --- /dev/null +++ b/tests/core/go_library/embedsrcs_error_test.go @@ -0,0 +1,138 @@ +// Copyright 2021 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 embedsrcs_errors + +import ( + "strings" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Main: ` +-- BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "invalid", + srcs = ["invalid.go"], + importpath = "invalid", +) + +go_library( + name = "none", + srcs = ["none.go"], + importpath = "none", +) + +go_library( + name = "multi_dir", + srcs = [ + "a.go", + "b/b.go", + ], + embedsrcs = [ + "a.txt", + "b/b.txt", + ], + importpath = "multi_dir", +) + +go_library( + name = "embeds_vcs_dir", + srcs = ["c/c.go"], + embedsrcs = ["c/.bzr/c.txt"], + importpath = "embeds_vcs_dir", +) +-- invalid.go -- +package invalid + +import _ "embed" + +//go:embed .. +var x string +-- none.go -- +package none + +import _ "embed" + +//go:embed none +var x string +-- a.go -- +package a + +import _ "embed" + +//go:embed a.txt +var x string +-- a.txt -- +-- b/b.go -- +package a + +import _ "embed" + +//go:embed b.txt +var y string +-- b/b.txt -- +-- c/c.go -- +package a + +import _ "embed" + +//go:embed .bzr +var z string +-- c/.bzr/c.txt -- +`, + }) +} + +func Test(t *testing.T) { + for _, test := range []struct { + desc, target, want string + }{ + { + desc: "invalid", + target: "//:invalid", + want: "invalid pattern syntax", + }, + { + desc: "none", + target: "//:none", + want: "could not embed none: no matching files found", + }, + { + desc: "multi_dir", + target: "//:multi_dir", + want: "source files with //go:embed should be in same directory", + }, + { + desc: "embeds_vcs_dir", + target: "//:embeds_vcs_dir", + want: "could not embed .bzr: cannot embed directory .bzr: invalid name .bzr", + }, + } { + t.Run(test.desc, func(t *testing.T) { + err := bazel_testing.RunBazel("build", test.target) + if err == nil { + t.Fatalf("expected error matching %q", test.want) + } + if errMsg := err.Error(); !strings.Contains(errMsg, test.want) { + t.Fatalf("expected error matching %q; got %v", test.want, errMsg) + } + }) + } +} diff --git a/tests/core/go_library/embedsrcs_gen_test.go.in b/tests/core/go_library/embedsrcs_gen_test.go.in new file mode 100644 index 00000000..a845b48e --- /dev/null +++ b/tests/core/go_library/embedsrcs_gen_test.go.in @@ -0,0 +1,20 @@ +// Copyright 2021 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 embedsrcs + +import "embed" + +//go:embed embedsrcs_test.go +var gen embed.FS diff --git a/tests/core/go_library/embedsrcs_simple_test.go b/tests/core/go_library/embedsrcs_simple_test.go new file mode 100644 index 00000000..32b3fdee --- /dev/null +++ b/tests/core/go_library/embedsrcs_simple_test.go @@ -0,0 +1,6 @@ +package embedsrcs_simple_test + +import _ "embed" + +//go:embed embedsrcs_static/no +var no []byte diff --git a/tests/core/go_library/embedsrcs_static/contains_hidden/.hidden b/tests/core/go_library/embedsrcs_static/contains_hidden/.hidden new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/core/go_library/embedsrcs_static/contains_hidden/.hidden diff --git a/tests/core/go_library/embedsrcs_static/contains_hidden/.hidden_dir/.env b/tests/core/go_library/embedsrcs_static/contains_hidden/.hidden_dir/.env new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/core/go_library/embedsrcs_static/contains_hidden/.hidden_dir/.env diff --git a/tests/core/go_library/embedsrcs_static/contains_hidden/.hidden_dir/visible_file b/tests/core/go_library/embedsrcs_static/contains_hidden/.hidden_dir/visible_file new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/core/go_library/embedsrcs_static/contains_hidden/.hidden_dir/visible_file diff --git a/tests/core/go_library/embedsrcs_static/contains_hidden/_hidden_dir/.bashrc b/tests/core/go_library/embedsrcs_static/contains_hidden/_hidden_dir/.bashrc new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/core/go_library/embedsrcs_static/contains_hidden/_hidden_dir/.bashrc diff --git a/tests/core/go_library/embedsrcs_static/contains_hidden/_hidden_dir/_hidden_file b/tests/core/go_library/embedsrcs_static/contains_hidden/_hidden_dir/_hidden_file new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/core/go_library/embedsrcs_static/contains_hidden/_hidden_dir/_hidden_file diff --git a/tests/core/go_library/embedsrcs_static/contains_hidden/_hidden_dir/visible_file b/tests/core/go_library/embedsrcs_static/contains_hidden/_hidden_dir/visible_file new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/core/go_library/embedsrcs_static/contains_hidden/_hidden_dir/visible_file diff --git a/tests/core/go_library/embedsrcs_static/contains_hidden/visible/.bzr/file-under-version-control b/tests/core/go_library/embedsrcs_static/contains_hidden/visible/.bzr/file-under-version-control new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/core/go_library/embedsrcs_static/contains_hidden/visible/.bzr/file-under-version-control diff --git a/tests/core/go_library/embedsrcs_static/contains_hidden/visible/visible_file b/tests/core/go_library/embedsrcs_static/contains_hidden/visible/visible_file new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/core/go_library/embedsrcs_static/contains_hidden/visible/visible_file diff --git a/tests/core/go_library/embedsrcs_static/dir/_no b/tests/core/go_library/embedsrcs_static/dir/_no new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/core/go_library/embedsrcs_static/dir/_no diff --git a/tests/core/go_library/embedsrcs_static/dir/f b/tests/core/go_library/embedsrcs_static/dir/f new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/core/go_library/embedsrcs_static/dir/f diff --git a/tests/core/go_library/embedsrcs_static/file b/tests/core/go_library/embedsrcs_static/file new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/core/go_library/embedsrcs_static/file diff --git a/tests/core/go_library/embedsrcs_static/glob/_hidden b/tests/core/go_library/embedsrcs_static/glob/_hidden new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/core/go_library/embedsrcs_static/glob/_hidden diff --git a/tests/core/go_library/embedsrcs_static/glob/f b/tests/core/go_library/embedsrcs_static/glob/f new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/core/go_library/embedsrcs_static/glob/f diff --git a/tests/core/go_library/embedsrcs_static/no b/tests/core/go_library/embedsrcs_static/no new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/core/go_library/embedsrcs_static/no diff --git a/tests/core/go_library/embedsrcs_test.go b/tests/core/go_library/embedsrcs_test.go new file mode 100644 index 00000000..0a4506e6 --- /dev/null +++ b/tests/core/go_library/embedsrcs_test.go @@ -0,0 +1,199 @@ +// Copyright 2021 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 embedsrcs + +import ( + "bytes" + "embed" + "io/fs" + "strings" + "testing" +) + +//go:embed embedsrcs_test.go +var self embed.FS + +//go:embed embedsrcs_static/file embedsrcs_static/dir embedsrcs_static/glob/* +var static embed.FS + +//go:embed embedsrcs_dynamic/file embedsrcs_dynamic/dir embedsrcs_dynamic/glob/* +var dynamic embed.FS + +//go:embed embedsrcs_transitioned +var transitioned embed.FS + +//go:embed * +var star embed.FS + +//go:embed all:embedsrcs_static/contains_hidden +var all embed.FS + +//go:embed embedsrcs_static/contains_hidden +var allButHidden embed.FS + +func TestFiles(t *testing.T) { + for _, test := range []struct { + desc string + fsys fs.FS + want []string + }{ + { + desc: "self", + fsys: self, + want: []string{ + ".", + "embedsrcs_test.go", + }, + }, + { + desc: "gen", + fsys: gen, + want: []string{ + ".", + "embedsrcs_test.go", + }, + }, + { + desc: "static", + fsys: static, + want: []string{ + ".", + "embedsrcs_static", + "embedsrcs_static/dir", + "embedsrcs_static/dir/f", + "embedsrcs_static/file", + "embedsrcs_static/glob", + "embedsrcs_static/glob/_hidden", + "embedsrcs_static/glob/f", + }, + }, + { + desc: "dynamic", + fsys: dynamic, + want: []string{ + ".", + "embedsrcs_dynamic", + "embedsrcs_dynamic/dir", + "embedsrcs_dynamic/dir/f", + "embedsrcs_dynamic/file", + "embedsrcs_dynamic/glob", + "embedsrcs_dynamic/glob/_hidden", + "embedsrcs_dynamic/glob/f", + }, + }, + { + desc: "transitioned", + fsys: transitioned, + want: []string{ + ".", + "embedsrcs_transitioned", + }, + }, + { + desc: "star", + fsys: star, + want: []string{ + ".", + "embedsrcs_dynamic", + "embedsrcs_dynamic/dir", + "embedsrcs_dynamic/dir/f", + "embedsrcs_dynamic/empty", + "embedsrcs_dynamic/file", + "embedsrcs_dynamic/glob", + "embedsrcs_dynamic/glob/f", + "embedsrcs_dynamic/no", + "embedsrcs_static", + "embedsrcs_static/contains_hidden", + "embedsrcs_static/contains_hidden/visible", + "embedsrcs_static/contains_hidden/visible/visible_file", + "embedsrcs_static/dir", + "embedsrcs_static/dir/f", + "embedsrcs_static/file", + "embedsrcs_static/glob", + "embedsrcs_static/glob/f", + "embedsrcs_static/no", + "embedsrcs_test.go", + "embedsrcs_transitioned", + }, + }, + { + desc: "all", + fsys: all, + want: []string{ + ".", + "embedsrcs_static", + "embedsrcs_static/contains_hidden", + "embedsrcs_static/contains_hidden/.hidden", + "embedsrcs_static/contains_hidden/.hidden_dir", + "embedsrcs_static/contains_hidden/.hidden_dir/.env", + "embedsrcs_static/contains_hidden/.hidden_dir/visible_file", + "embedsrcs_static/contains_hidden/_hidden_dir", + "embedsrcs_static/contains_hidden/_hidden_dir/.bashrc", + "embedsrcs_static/contains_hidden/_hidden_dir/_hidden_file", + "embedsrcs_static/contains_hidden/_hidden_dir/visible_file", + "embedsrcs_static/contains_hidden/visible", + "embedsrcs_static/contains_hidden/visible/visible_file", + }, + }, + { + desc: "allButHidden", + fsys: allButHidden, + want: []string{ + ".", + "embedsrcs_static", + "embedsrcs_static/contains_hidden", + "embedsrcs_static/contains_hidden/visible", + "embedsrcs_static/contains_hidden/visible/visible_file", + }, + }, + } { + t.Run(test.desc, func(t *testing.T) { + got, err := listFiles(test.fsys) + if err != nil { + t.Fatal(err) + } + gotStr := strings.Join(got, "\n") + wantStr := strings.Join(test.want, "\n") + if gotStr != wantStr { + t.Errorf("got:\n%s\nwant:\n%s", gotStr, wantStr) + } + }) + } +} + +func listFiles(fsys fs.FS) ([]string, error) { + var files []string + err := fs.WalkDir(fsys, ".", func(path string, _ fs.DirEntry, err error) error { + if err != nil { + return err + } + files = append(files, path) + return nil + }) + if err != nil { + return nil, err + } + return files, nil +} + +func TestContent(t *testing.T) { + data, err := fs.ReadFile(self, "embedsrcs_test.go") + if err != nil { + t.Fatal(err) + } + if !bytes.Contains(data, []byte("package embedsrcs")) { + t.Error("embedded content did not contain package declaration") + } +} diff --git a/tests/core/go_library/empty_bar.go b/tests/core/go_library/empty_bar.go new file mode 100644 index 00000000..d73206c4 --- /dev/null +++ b/tests/core/go_library/empty_bar.go @@ -0,0 +1,3 @@ +// +build ignore + +package bar diff --git a/tests/core/go_library/empty_baz.s b/tests/core/go_library/empty_baz.s new file mode 100644 index 00000000..15934b3b --- /dev/null +++ b/tests/core/go_library/empty_baz.s @@ -0,0 +1 @@ +// +build ignore diff --git a/tests/core/go_library/empty_foo.go b/tests/core/go_library/empty_foo.go new file mode 100644 index 00000000..ce414ed6 --- /dev/null +++ b/tests/core/go_library/empty_foo.go @@ -0,0 +1,3 @@ +// +build ignore + +package foo diff --git a/tests/core/go_library/empty_main.go b/tests/core/go_library/empty_main.go new file mode 100644 index 00000000..38dd16da --- /dev/null +++ b/tests/core/go_library/empty_main.go @@ -0,0 +1,3 @@ +package main + +func main() {} diff --git a/tests/core/go_library/gen_embedsrcs_files.go b/tests/core/go_library/gen_embedsrcs_files.go new file mode 100644 index 00000000..68ff2fef --- /dev/null +++ b/tests/core/go_library/gen_embedsrcs_files.go @@ -0,0 +1,51 @@ +// Copyright 2021 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 main + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" +) + +func main() { + dir := os.Args[1] + files := os.Args[2:] + if err := run(dir, files); err != nil { + fmt.Fprintf(os.Stderr, "%v\n", err) + os.Exit(1) + } +} + +func run(dir string, files []string) error { + for _, file := range files { + path := filepath.Join(dir, file) + if strings.HasSuffix(path, "/") { + if err := os.MkdirAll(path, 0777); err != nil { + return err + } + } else { + if err := os.MkdirAll(filepath.Dir(path), 0777); err != nil { + return err + } + if err := ioutil.WriteFile(path, nil, 0666); err != nil { + return err + } + } + } + return nil +} diff --git a/tests/core/go_library/import_alias_a_v2.go b/tests/core/go_library/import_alias_a_v2.go new file mode 100644 index 00000000..66326f94 --- /dev/null +++ b/tests/core/go_library/import_alias_a_v2.go @@ -0,0 +1,17 @@ +// 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 a + +const A = "import_alias/a/v2" diff --git a/tests/core/go_library/import_alias_b.go b/tests/core/go_library/import_alias_b.go new file mode 100644 index 00000000..1fa79977 --- /dev/null +++ b/tests/core/go_library/import_alias_b.go @@ -0,0 +1,17 @@ +// 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 b + +const B = "import_alias/b" diff --git a/tests/core/go_library/import_alias_test.go b/tests/core/go_library/import_alias_test.go new file mode 100644 index 00000000..aabec973 --- /dev/null +++ b/tests/core/go_library/import_alias_test.go @@ -0,0 +1,33 @@ +// 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 import_alias + +import ( + "import_alias/a" + "import_alias/b" + "testing" +) + +func TestA(t *testing.T) { + if a.A != "import_alias/a/v2" { + t.Errorf("got %q; want %q", a.A, "import_alias/a/v2") + } +} + +func TestB(t *testing.T) { + if b.B != "import_alias/b" { + t.Errorf("got %q; want %q", b.B, "import_alias/b") + } +} diff --git a/tests/core/go_library/inc.go b/tests/core/go_library/inc.go new file mode 100644 index 00000000..bb5e9272 --- /dev/null +++ b/tests/core/go_library/inc.go @@ -0,0 +1 @@ +package inc diff --git a/tests/core/go_library/inc_asmhdr.s b/tests/core/go_library/inc_asmhdr.s new file mode 100644 index 00000000..230bbe2f --- /dev/null +++ b/tests/core/go_library/inc_asmhdr.s @@ -0,0 +1 @@ +#include "go_asm.h" diff --git a/tests/core/go_library/inc_bar.s b/tests/core/go_library/inc_bar.s new file mode 100644 index 00000000..47eaec6b --- /dev/null +++ b/tests/core/go_library/inc_bar.s @@ -0,0 +1 @@ +#include "inc_foo.s" diff --git a/tests/core/go_library/inc_foo.s b/tests/core/go_library/inc_foo.s new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/core/go_library/inc_foo.s diff --git a/tests/core/go_library/no_srcs_test.go b/tests/core/go_library/no_srcs_test.go new file mode 100644 index 00000000..8e6bebc0 --- /dev/null +++ b/tests/core/go_library/no_srcs_test.go @@ -0,0 +1,78 @@ +// Copyright 2021 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_srcs + +import ( + "bytes" + "os" + "strings" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Main: ` +-- BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +[ + go_library( + name = "lib_" + str(i), + srcs = [], + importpath = "example.com/some/path", + ) + for i in range(1000) +] +`, + }) +} + +func Test(t *testing.T) { + commonArgs := []string{ + "--spawn_strategy=local", + "--compilation_mode=dbg", + } + + if err := bazel_testing.RunBazel(append([]string{"build", "//..."}, commonArgs...)...); err != nil { + t.Fatal(err) + } + + out, err := bazel_testing.BazelOutput(append([]string{"cquery", "--output=files", "//..."}, commonArgs...)...) + if err != nil { + t.Fatal(err) + } + archives := strings.Split(strings.TrimSpace(string(out)), "\n") + + if len(archives) != 1000 { + t.Fatalf("expected 1000 archives, got %d", len(archives)) + } + + referenceContent, err := os.ReadFile(archives[0]) + if err != nil { + t.Fatal(err) + } + + for _, archive := range archives { + content, err := os.ReadFile(archive) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(content, referenceContent) { + t.Fatalf("expected all archives to be identical, got:\n\n%s\n\n%s\n", string(content), string(referenceContent)) + } + } +} diff --git a/tests/core/go_library/package_height.go b/tests/core/go_library/package_height.go new file mode 100644 index 00000000..cafb5faf --- /dev/null +++ b/tests/core/go_library/package_height.go @@ -0,0 +1,6 @@ +package height + +import "package_height/embed" +import "package_height/dep" + +var X = embed.T{F: dep.T{}} diff --git a/tests/core/go_library/package_height_dep_deep.go b/tests/core/go_library/package_height_dep_deep.go new file mode 100644 index 00000000..139a805d --- /dev/null +++ b/tests/core/go_library/package_height_dep_deep.go @@ -0,0 +1,7 @@ +package dep + +import "os" + +type T struct { + F *os.File +} diff --git a/tests/core/go_library/package_height_dep_shallow.go b/tests/core/go_library/package_height_dep_shallow.go new file mode 100644 index 00000000..1902c035 --- /dev/null +++ b/tests/core/go_library/package_height_dep_shallow.go @@ -0,0 +1,3 @@ +package dep + +type T struct{} diff --git a/tests/core/go_library/package_height_embeddee.go b/tests/core/go_library/package_height_embeddee.go new file mode 100644 index 00000000..0c36a32a --- /dev/null +++ b/tests/core/go_library/package_height_embeddee.go @@ -0,0 +1,7 @@ +package embed + +import "package_height/dep" + +type T struct { + F dep.T +} diff --git a/tests/core/go_library/package_height_embedder.go b/tests/core/go_library/package_height_embedder.go new file mode 100644 index 00000000..81a00369 --- /dev/null +++ b/tests/core/go_library/package_height_embedder.go @@ -0,0 +1,3 @@ +package embed + +var X = T{} diff --git a/tests/core/go_path/BUILD.bazel b/tests/core/go_path/BUILD.bazel new file mode 100644 index 00000000..59aea97c --- /dev/null +++ b/tests/core/go_path/BUILD.bazel @@ -0,0 +1,81 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_path", "go_test") + +test_suite(name = "go_path") + +[go_path( + name = mode + "_path", + testonly = True, + data = ["extra.txt"], + include_pkg = True, + mode = mode, + deps = [ + "//tests/core/go_path/cmd/bin", + "//tests/core/go_path/cmd/bin:cross", + "//tests/core/go_path/pkg/lib:embed_test", + "//tests/core/go_path/pkg/lib:go_default_library", + "//tests/core/go_path/pkg/lib:go_default_test", + "//tests/core/go_path/pkg/lib:vendored", + ], +) for mode in ("archive", "copy")] + +go_path( + name = "transition_path", + testonly = True, + data = ["extra.txt"], + include_pkg = True, + mode = "copy", + deps = ["//tests/core/go_path/cmd/bin:pie"], +) + +go_path( + name = "nodata_path", + testonly = True, + data = ["extra.txt"], + include_data = False, + mode = "copy", + deps = ["//tests/core/go_path/pkg/lib:go_default_library"], +) + +go_path( + name = "notransitive_path", + testonly = True, + include_transitive = False, + mode = "copy", + deps = ["//tests/core/go_path/pkg/lib:go_default_library"], +) + +go_path( + name = "embed_path", + mode = "copy", + deps = ["//tests/core/go_path/pkg/lib:generated_embeded"], +) + +go_path( + name = "embed_no_srcs_path", + mode = "copy", + deps = ["//tests/core/go_path/pkg/lib:generated_embeded_no_srcs"], +) + +go_test( + name = "go_path_test", + srcs = ["go_path_test.go"], + args = [ + "-archive_path=$(location :archive_path)", + "-copy_path=$(location :copy_path)", + "-nodata_path=$(location :nodata_path)", + "-embed_path=$(location :embed_path)", + "-embed_no_srcs_path=$(location :embed_no_srcs_path)", + "-notransitive_path=$(location :notransitive_path)", + ], + data = [ + ":archive_path", + ":copy_path", + ":embed_no_srcs_path", + ":embed_path", + ":nodata_path", + ":notransitive_path", + ":transition_path", + ], + rundir = ".", + deps = ["//go/tools/bazel:go_default_library"], +) diff --git a/tests/core/go_path/README.rst b/tests/core/go_path/README.rst new file mode 100644 index 00000000..6d086086 --- /dev/null +++ b/tests/core/go_path/README.rst @@ -0,0 +1,12 @@ +Basic go_path functionality +=========================== + +.. _go_path: /docs/go/core/rules.md#_go_path + +Tests to ensure the basic features of `go_path`_ are working as expected. + +go_path_test +------------ + +Consumes `go_path`_ rules built for the same set of packages in archive, copy, +and link modes and verifies that expected files are present in each mode. diff --git a/tests/core/go_path/cmd/bin/BUILD.bazel b/tests/core/go_path/cmd/bin/BUILD.bazel new file mode 100644 index 00000000..08f4e6dd --- /dev/null +++ b/tests/core/go_path/cmd/bin/BUILD.bazel @@ -0,0 +1,30 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary") + +go_binary( + name = "bin", + srcs = ["bin.go"], + data = ["bin.go"], # test duplicate + importpath = "example.com/repo/cmd/bin", + visibility = ["//visibility:public"], +) + +go_binary( + name = "pie", + srcs = ["bin.go"], + importpath = "example.com/repo/cmd/bin", + linkmode = "pie", + visibility = ["//visibility:public"], + deps = [ + "//tests/core/go_path/pkg/lib:go_default_library", + ], +) + +go_binary( + name = "cross", + srcs = ["bin.go"], + goarch = "arm", + goos = "plan9", + importpath = "example.com/repo/cmd/bin", + pure = "on", + visibility = ["//visibility:public"], +) diff --git a/tests/core/go_path/cmd/bin/bin.go b/tests/core/go_path/cmd/bin/bin.go new file mode 100644 index 00000000..da29a2ca --- /dev/null +++ b/tests/core/go_path/cmd/bin/bin.go @@ -0,0 +1,4 @@ +package main + +func main() { +} diff --git a/tests/core/go_path/extra.txt b/tests/core/go_path/extra.txt new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/core/go_path/extra.txt diff --git a/tests/core/go_path/go_path_test.go b/tests/core/go_path/go_path_test.go new file mode 100644 index 00000000..23e27cb8 --- /dev/null +++ b/tests/core/go_path/go_path_test.go @@ -0,0 +1,205 @@ +/* Copyright 2018 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 go_path + +import ( + "archive/zip" + "flag" + "io" + "io/ioutil" + "os" + "path/filepath" + "runtime" + "strings" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel" +) + +var copyPath, embedPath, embedNoSrcsPath, archivePath, nodataPath, notransitivePath string + +var defaultMode = runtime.GOOS + "_" + runtime.GOARCH + +var files = []string{ + "extra.txt", + "src/", + "-src/example.com/repo/cmd/bin/bin", + "-src/testmain/testmain.go", + "src/example.com/repo/cmd/bin/bin.go", + "src/example.com/repo/pkg/lib/lib.go", + "src/example.com/repo/pkg/lib/embedded_src.txt", + "src/example.com/repo/pkg/lib/template/index.html.tmpl", + "src/example.com/repo/pkg/lib/embed_test.go", + "src/example.com/repo/pkg/lib/internal_test.go", + "src/example.com/repo/pkg/lib/external_test.go", + "-src/example.com/repo/pkg/lib_test/embed_test.go", + "src/example.com/repo/pkg/lib/data.txt", + "src/example.com/repo/pkg/lib/testdata/testdata.txt", + "src/example.com/repo/vendor/example.com/repo2/vendored.go", + "pkg/" + defaultMode + "/example.com/repo/cmd/bin.a", + "pkg/" + defaultMode + "/example.com/repo/pkg/lib.a", + "pkg/" + defaultMode + "/example.com/repo/vendor/example.com/repo2.a", + "pkg/plan9_arm/example.com/repo/cmd/bin.a", +} + +func TestMain(m *testing.M) { + flag.StringVar(©Path, "copy_path", "", "path to copied go_path") + flag.StringVar(&archivePath, "archive_path", "", "path to archive go_path") + flag.StringVar(&nodataPath, "nodata_path", "", "path to go_path without data") + flag.StringVar(&embedPath, "embed_path", "", "path to go_path with embedsrcs") + flag.StringVar(&embedNoSrcsPath, "embed_no_srcs_path", "", "path to go_path with embedsrcs") + flag.StringVar(¬ransitivePath, "notransitive_path", "", "path to go_path without transitive dependencies") + flag.Parse() + os.Exit(m.Run()) +} + +func TestCopyPath(t *testing.T) { + if copyPath == "" { + t.Fatal("-copy_path not set") + } + checkPath(t, copyPath, files) +} + +func TestEmbedPath(t *testing.T) { + if embedPath == "" { + t.Fatal("-embed_path not set") + } + files := []string{ + "src/lib/embed_test.go", + "src/lib/embedded_src.txt", + "src/lib/generated_embeded.go", + } + checkPath(t, embedPath, files) +} + +func TestEmbedNoSrcsPath(t *testing.T) { + if embedNoSrcsPath == "" { + t.Fatal("-embed_no_srcs_path not set") + } + files := []string{ + "src/lib/embedded_src.txt", + "src/lib/generated_embeded_no_srcs.go", + } + checkPath(t, embedNoSrcsPath, files) +} + +func TestArchivePath(t *testing.T) { + if archivePath == "" { + t.Fatal("-archive_path not set") + } + dir, err := ioutil.TempDir(os.Getenv("TEST_TEMPDIR"), "TestArchivePath") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + path, err := bazel.Runfile(archivePath) + if err != nil { + t.Fatalf("Could not find runfile %s: %q", archivePath, err) + } + + z, err := zip.OpenReader(path) + if err != nil { + t.Fatalf("error opening zip: %v", err) + } + defer z.Close() + for _, f := range z.File { + r, err := f.Open() + if err != nil { + t.Fatalf("error reading file %s: %v", f.Name, err) + } + dstPath := filepath.Join(dir, filepath.FromSlash(f.Name)) + if err := os.MkdirAll(filepath.Dir(dstPath), 0777); err != nil { + t.Fatalf("error creating directory %s: %v", filepath.Dir(dstPath), err) + } + w, err := os.Create(dstPath) + if err != nil { + t.Fatalf("error creating file %s: %v", dstPath, err) + } + if _, err := io.Copy(w, r); err != nil { + w.Close() + t.Fatalf("error writing file %s: %v", dstPath, err) + } + if err := w.Close(); err != nil { + t.Fatalf("error closing file %s: %v", dstPath, err) + } + } + + checkPath(t, dir, files) +} + +func TestNoDataPath(t *testing.T) { + if nodataPath == "" { + t.Fatal("-nodata_path not set") + } + files := []string{ + "extra.txt", + "src/example.com/repo/pkg/lib/lib.go", + "-src/example.com/repo/pkg/lib/data.txt", + } + checkPath(t, nodataPath, files) +} + +func TestNoTransitivePath(t *testing.T) { + if notransitivePath == "" { + t.Fatal("-notransitive_path not set") + } + files := []string{ + "-src/example.com/repo/pkg/lib/transitive/transitive.go", + } + checkPath(t, notransitivePath, files) +} + +// checkPath checks that dir contains a list of files. files is a list of +// slash-separated paths relative to dir. Files that start with "-" should be +// absent. Files that end with "/" should be directories. +func checkPath(t *testing.T, dir string, files []string) { + if strings.HasPrefix(dir, "external") { + dir = filepath.Join(os.Getenv("TEST_SRCDIR"), strings.TrimPrefix(dir, "external/")) + } + + for _, f := range files { + wantDir := strings.HasSuffix(f, "/") + wantAbsent := false + if strings.HasPrefix(f, "-") { + f = f[1:] + wantAbsent = true + } + path := filepath.Join(dir, filepath.FromSlash(f)) + st, err := os.Stat(path) + if wantAbsent { + if err == nil { + t.Errorf("found %s: should not be present", path) + } else if !os.IsNotExist(err) { + t.Error(err) + } + } else { + if err != nil { + if os.IsNotExist(err) { + t.Errorf("%s is missing", path) + } else { + t.Error(err) + } + continue + } + if st.IsDir() && !wantDir { + t.Errorf("%s: got directory; wanted file", path) + } else if !st.IsDir() && wantDir { + t.Errorf("%s: got file; wanted directory", path) + } + } + } +} diff --git a/tests/core/go_path/pkg/lib/BUILD.bazel b/tests/core/go_path/pkg/lib/BUILD.bazel new file mode 100644 index 00000000..68f7c0b4 --- /dev/null +++ b/tests/core/go_path/pkg/lib/BUILD.bazel @@ -0,0 +1,80 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") +load("@bazel_skylib//rules:copy_file.bzl", "copy_file") +load(":generated_embeded.bzl", "generated_embeded") + +go_library( + name = "go_default_library", + srcs = ["lib.go"], + cgo = True, + data = [ + "data.txt", + "testdata/testdata.txt", + ], + embedsrcs = [ + "embedded_src.txt", + "renamed_embedded_src.txt", + "template/index.html.tmpl", + ], + importpath = "example.com/repo/pkg/lib", + visibility = ["//visibility:public"], + deps = [":transitive_lib"], +) + +copy_file( + name = "rename_embedded_src.txt", + src = ":embedded_src.txt", + out = "renamed_embedded_src.txt", +) + +generated_embeded( + name = "generated_embeded", + srcs = ["embed_test.go"], + embedsrcs = ["embedded_src.txt"], + importpath = "lib", + visibility = ["//visibility:public"], +) + +generated_embeded( + name = "generated_embeded_no_srcs", + embedsrcs = ["embedded_src.txt"], + importpath = "lib", + visibility = ["//visibility:public"], +) + +go_test( + name = "go_default_test", + srcs = [ + "external_test.go", + "internal_test.go", + ], + importpath = "example.com/repo/pkg/lib", + visibility = ["//visibility:public"], +) + +go_test( + name = "embed_test", + embed = [":embed_lib"], + visibility = ["//visibility:public"], +) + +go_library( + name = "embed_lib", + srcs = ["embed_test.go"], + importpath = "example.com/repo/pkg/lib", + visibility = ["//visibility:public"], +) + +go_library( + name = "vendored", + srcs = ["vendored.go"], + importmap = "example.com/repo/vendor/example.com/repo2", + importpath = "example.com/repo2", + visibility = ["//visibility:public"], +) + +go_library( + name = "transitive_lib", + srcs = ["transitive.go"], + importpath = "example.com/repo/pkg/lib/transitive", + visibility = ["//visibility:public"], +) diff --git a/tests/core/go_path/pkg/lib/data.txt b/tests/core/go_path/pkg/lib/data.txt new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/core/go_path/pkg/lib/data.txt diff --git a/tests/core/go_path/pkg/lib/embed_test.go b/tests/core/go_path/pkg/lib/embed_test.go new file mode 100644 index 00000000..55c21f80 --- /dev/null +++ b/tests/core/go_path/pkg/lib/embed_test.go @@ -0,0 +1 @@ +package lib diff --git a/tests/core/go_path/pkg/lib/embedded_src.txt b/tests/core/go_path/pkg/lib/embedded_src.txt new file mode 100644 index 00000000..257cc564 --- /dev/null +++ b/tests/core/go_path/pkg/lib/embedded_src.txt @@ -0,0 +1 @@ +foo diff --git a/tests/core/go_path/pkg/lib/external_test.go b/tests/core/go_path/pkg/lib/external_test.go new file mode 100644 index 00000000..df64f213 --- /dev/null +++ b/tests/core/go_path/pkg/lib/external_test.go @@ -0,0 +1 @@ +package lib_test diff --git a/tests/core/go_path/pkg/lib/generated_embeded.bzl b/tests/core/go_path/pkg/lib/generated_embeded.bzl new file mode 100644 index 00000000..42bcf595 --- /dev/null +++ b/tests/core/go_path/pkg/lib/generated_embeded.bzl @@ -0,0 +1,71 @@ +# Copyright 2018 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_context", +) + +def _gen_library_impl(ctx): + go = go_context(ctx) + libname = getattr(ctx.attr, "libname") + src = go.actions.declare_file(ctx.label.name + ".go") + + embedsrcs = getattr(ctx.attr, "embedsrcs", []) + + lines = [ + "package " + libname, + "", + 'import _ "embed"', + "", + ] + + i = 0 + for e in embedsrcs: + for f in e.files.to_list(): + lines.extend([ + "//go:embed {}".format(f.basename), + "var embeddedSource{} string".format(i), + ]) + i += 1 + + ctx.actions.write(src, "\n".join(lines)) + + library = go.new_library(go, srcs = [src]) + source = go.library_to_source(go, ctx.attr, library, ctx.coverage_instrumented()) + archive = go.archive(go, source) + return [ + library, + source, + archive, + DefaultInfo(files = depset([archive.data.file])), + ] + +generated_embeded = rule( + _gen_library_impl, + attrs = { + "importpath": attr.string(mandatory = True), + "_go_context_data": attr.label( + default = "//:go_context_data", + ), + "srcs": attr.label_list( + allow_files = True, + ), + "embedsrcs": attr.label_list( + allow_files = True, + ), + "libname": attr.string(default = "lib"), + }, + toolchains = ["@io_bazel_rules_go//go:toolchain"], +) diff --git a/tests/core/go_path/pkg/lib/internal_test.go b/tests/core/go_path/pkg/lib/internal_test.go new file mode 100644 index 00000000..55c21f80 --- /dev/null +++ b/tests/core/go_path/pkg/lib/internal_test.go @@ -0,0 +1 @@ +package lib diff --git a/tests/core/go_path/pkg/lib/lib.go b/tests/core/go_path/pkg/lib/lib.go new file mode 100644 index 00000000..1434db9f --- /dev/null +++ b/tests/core/go_path/pkg/lib/lib.go @@ -0,0 +1,15 @@ +package lib + +import ( + "C" + _ "embed" // for go:embed +) + +//go:embed embedded_src.txt +var embeddedSource string + +//go:embed renamed_embedded_src.txt +var renamedEmbeddedSource string + +//go:embed template/index.html.tmpl +var indexTmpl string diff --git a/tests/core/go_path/pkg/lib/template/index.html.tmpl b/tests/core/go_path/pkg/lib/template/index.html.tmpl new file mode 100644 index 00000000..68a09ae6 --- /dev/null +++ b/tests/core/go_path/pkg/lib/template/index.html.tmpl @@ -0,0 +1 @@ +{{ .Content }} diff --git a/tests/core/go_path/pkg/lib/testdata/testdata.txt b/tests/core/go_path/pkg/lib/testdata/testdata.txt new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/core/go_path/pkg/lib/testdata/testdata.txt diff --git a/tests/core/go_path/pkg/lib/transitive.go b/tests/core/go_path/pkg/lib/transitive.go new file mode 100644 index 00000000..a505a296 --- /dev/null +++ b/tests/core/go_path/pkg/lib/transitive.go @@ -0,0 +1,5 @@ +package transitive + +var ( + _ = "" +) diff --git a/tests/core/go_path/pkg/lib/vendored.go b/tests/core/go_path/pkg/lib/vendored.go new file mode 100644 index 00000000..2355022f --- /dev/null +++ b/tests/core/go_path/pkg/lib/vendored.go @@ -0,0 +1 @@ +package vendored diff --git a/tests/core/go_plugin/BUILD.bazel b/tests/core/go_plugin/BUILD.bazel new file mode 100644 index 00000000..ce07189d --- /dev/null +++ b/tests/core/go_plugin/BUILD.bazel @@ -0,0 +1,16 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_test") + +test_suite(name = "go_plugin") + +go_test( + name = "go_default_test", + srcs = ["all_test.go"], + data = [":plugin"], +) + +go_binary( + name = "plugin", + srcs = ["plugin.go"], + out = "plugin.so", + linkmode = "plugin", +) diff --git a/tests/core/go_plugin/README.rst b/tests/core/go_plugin/README.rst new file mode 100644 index 00000000..55b0e295 --- /dev/null +++ b/tests/core/go_plugin/README.rst @@ -0,0 +1,16 @@ +Basic -buildmode=plugin functionality +===================================== + +.. _go_binary: /docs/go/core/rules.md#_go_binary + +Tests to ensure the basic features of go_binary with linkmode="plugin" are +working as expected. + +all_test +-------- + +1. Test that a go_binary_ rule can write its shared object file with a custom + name in the package directory (not the mode directory). + +2. Test that a plugin built using a go_binary_ rule can be loaded by a Go + program and that its symbols are working as expected. diff --git a/tests/core/go_plugin/all_test.go b/tests/core/go_plugin/all_test.go new file mode 100644 index 00000000..d74738b5 --- /dev/null +++ b/tests/core/go_plugin/all_test.go @@ -0,0 +1,33 @@ +package main_test + +import ( + "os" + "plugin" + "testing" +) + +const HelloWorld = "Hello, world!" + +func TestPluginCreated(t *testing.T) { + _, err := os.Stat("plugin.so") + if err != nil { + t.Error(err) + } +} + +func TestPluginWorks(t *testing.T) { + p, err := plugin.Open("plugin.so") + if err != nil { + t.Error(err) + } + + f, err := p.Lookup("Hi") + if err != nil { + t.Error(err) + } + + helloWorld := f.(func() string)() + if helloWorld != HelloWorld { + t.Errorf("expected %#v, got %#v", HelloWorld, helloWorld) + } +} diff --git a/tests/core/go_plugin/plugin.go b/tests/core/go_plugin/plugin.go new file mode 100644 index 00000000..b7db8d2a --- /dev/null +++ b/tests/core/go_plugin/plugin.go @@ -0,0 +1,7 @@ +package main + +const HelloWorld = "Hello, world!" + +func Hi() string { return HelloWorld } + +func main() {} diff --git a/tests/core/go_plugin_with_proto_library/BUILD.bazel b/tests/core/go_plugin_with_proto_library/BUILD.bazel new file mode 100644 index 00000000..468d8b35 --- /dev/null +++ b/tests/core/go_plugin_with_proto_library/BUILD.bazel @@ -0,0 +1,31 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_test") +load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") + +test_suite(name = "go_plugin_with_proto_library") + +go_test( + name = "go_default_test", + srcs = ["all_test.go"], + data = [":plugin"], + deps = [":validate"], +) + +go_binary( + name = "plugin", + srcs = ["plugin.go"], + out = "plugin.so", + linkmode = "plugin", + deps = [":validate"], +) + +proto_library( + name = "validate_proto", + srcs = ["validate.proto"], +) + +go_proto_library( + name = "validate", + gc_goopts = ["-trimpath=$(BINDIR)=>."], + importpath = "go_plugin_with_proto_library/validate", + proto = ":validate_proto", +) diff --git a/tests/core/go_plugin_with_proto_library/README.rst b/tests/core/go_plugin_with_proto_library/README.rst new file mode 100644 index 00000000..437a2cd9 --- /dev/null +++ b/tests/core/go_plugin_with_proto_library/README.rst @@ -0,0 +1,16 @@ +Go Plugin supporting protobufs +============================== + +.. _go_binary: /go/core.rst#_go_binary + +Tests to ensure a protobuf can be included into a plugin and host. + +all_test +-------- + +1. Test that a go_binary_ rule can write its shared object file with a custom + name in the package directory (not the mode directory) when the plugin + depends on a protobuf. + +2. Test that a plugin with a protobuf dependency built using a go_binary_ rule + can be loaded by a Go program and that its symbols are working as expected. diff --git a/tests/core/go_plugin_with_proto_library/all_test.go b/tests/core/go_plugin_with_proto_library/all_test.go new file mode 100644 index 00000000..c09c6ef7 --- /dev/null +++ b/tests/core/go_plugin_with_proto_library/all_test.go @@ -0,0 +1,36 @@ +package main_test + +import ( + "go_plugin_with_proto_library/validate" + "os" + "plugin" + "testing" +) + +const RuleName = "test" + +func TestPluginCreated(t *testing.T) { + _, err := os.Stat("plugin.so") + if err != nil { + t.Error(err) + } +} + +func TestPluginWorks(t *testing.T) { + p, err := plugin.Open("plugin.so") + if err != nil { + t.Error(err) + return + } + + symProto, err := p.Lookup("SomeProto") + if err != nil { + t.Error(err) + return + } + + proto := symProto.(*validate.MessageRules) + if *proto.Name != RuleName { + t.Errorf("expected %#v, got %#v", RuleName, proto.Name) + } +} diff --git a/tests/core/go_plugin_with_proto_library/plugin.go b/tests/core/go_plugin_with_proto_library/plugin.go new file mode 100644 index 00000000..7197a140 --- /dev/null +++ b/tests/core/go_plugin_with_proto_library/plugin.go @@ -0,0 +1,7 @@ +package main + +import "go_plugin_with_proto_library/validate" + +var testValue = "test" + +var SomeProto = validate.MessageRules{Name: &testValue} diff --git a/tests/core/go_plugin_with_proto_library/validate.proto b/tests/core/go_plugin_with_proto_library/validate.proto new file mode 100644 index 00000000..759e8726 --- /dev/null +++ b/tests/core/go_plugin_with_proto_library/validate.proto @@ -0,0 +1,6 @@ +syntax = "proto2"; +package validate; + +message MessageRules { + required string name = 1; +} diff --git a/tests/core/go_proto_library/BUILD.bazel b/tests/core/go_proto_library/BUILD.bazel new file mode 100644 index 00000000..0c1f8e23 --- /dev/null +++ b/tests/core/go_proto_library/BUILD.bazel @@ -0,0 +1,314 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") +load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") +load("@rules_proto//proto:defs.bzl", "proto_library") + +# Common rules +proto_library( + name = "foo_proto", + srcs = ["foo.proto"], +) + +go_proto_library( + name = "foo_go_proto", + importpath = "github.com/bazelbuild/rules_go/tests/core/go_proto_library/foo", + proto = ":foo_proto", +) + +proto_library( + name = "bar_proto", + srcs = ["bar.proto"], + deps = [":foo_proto"], +) + +go_proto_library( + name = "bar_go_proto", + importpath = "github.com/bazelbuild/rules_go/tests/core/go_proto_library/bar", + proto = ":bar_proto", + deps = [":foo_go_proto"], +) + +proto_library( + name = "grpc_proto", + srcs = ["grpc.proto"], +) + +# embed_test +go_proto_library( + name = "embed_go_proto", + embed = [":extra_lib"], + importpath = "github.com/bazelbuild/rules_go/tests/core/go_proto_library/foo", + proto = ":foo_proto", +) + +go_library( + name = "extra_lib", + srcs = ["extra.go"], + importpath = "github.com/bazelbuild/rules_go/tests/core/go_proto_library/foo", +) + +go_test( + name = "embed_test", + srcs = ["embed_test.go"], + deps = [ + ":embed_go_proto", + "@com_github_golang_protobuf//proto:go_default_library", + ], +) + +# transitive_test +go_proto_library( + name = "transitive_go_proto", + importpath = "github.com/bazelbuild/rules_go/tests/core/go_proto_library/bar", + proto = ":bar_proto", + deps = [":wrap_lib"], +) + +go_library( + name = "wrap_lib", + srcs = ["extra.go"], + embed = [":foo_go_proto"], + importpath = "github.com/bazelbuild/rules_go/tests/core/go_proto_library/foo", +) + +go_test( + name = "transitive_test", + srcs = ["transitive_test.go"], + deps = [ + ":transitive_go_proto", + ":wrap_lib", + ], +) + +# proxy_test +go_test( + name = "proxy_test", + srcs = ["proxy_test.go"], + deps = [":proxy_go_proto"], +) + +go_proto_library( + name = "proxy_go_proto", + importpath = "github.com/bazelbuild/rules_go/tests/core/go_proto_library/proxy", + proto = ":proxy_proto", +) + +proto_library( + name = "proxy_proto", + deps = [ + ":proxy_a_proto", + ":proxy_b_proto", + ], +) + +proto_library( + name = "proxy_a_proto", + srcs = ["proxy_a.proto"], +) + +proto_library( + name = "proxy_b_proto", + srcs = ["proxy_b.proto"], +) + +# protos_test (multiple entries in protos argument) +go_test( + name = "protos_test", + srcs = ["protos_test.go"], + deps = [":protos_go_proto"], +) + +go_test( + name = "protos_alias_test", + srcs = ["protos_alias_test.go"], + deps = [":protos_go_proto"], +) + +go_proto_library( + name = "protos_go_proto", + importpath = "github.com/bazelbuild/rules_go/tests/core/go_proto_library/protos", + importpath_aliases = ["myalias/protos"], + protos = [ + ":protos_a_proto", + ":protos_b_proto", + ], +) + +proto_library( + name = "protos_a_proto", + srcs = ["protos_a.proto"], +) + +proto_library( + name = "protos_b_proto", + srcs = ["protos_b.proto"], +) + +# gofast test +go_test( + name = "gofast_test", + srcs = ["gofast_test.go"], + deps = [":gofast_proto"], +) + +go_proto_library( + name = "gofast_proto", + compilers = ["@io_bazel_rules_go//proto:gofast_proto"], + importpath = "github.com/bazelbuild/rules_go/tests/core/go_proto_library/foo", + protos = [":foo_proto"], +) + +# gofast gRPC test +go_test( + name = "gofast_grpc_test", + srcs = ["gofast_grpc_test.go"], + deps = [":gofast_grpc"], +) + +go_proto_library( + name = "gofast_grpc", + compilers = ["@io_bazel_rules_go//proto:gofast_grpc"], + importpath = "github.com/bazelbuild/rules_go/tests/core/go_proto_library/grpc", + protos = [":grpc_proto"], +) + +# gogofast test +go_test( + name = "gogofast_test", + srcs = ["gogofast_test.go"], + deps = [":gogofast_proto"], +) + +go_proto_library( + name = "gogofast_proto", + compilers = ["@io_bazel_rules_go//proto:gogofast_proto"], + importpath = "github.com/bazelbuild/rules_go/tests/core/go_proto_library/foo", + protos = [":foo_proto"], +) + +# gogofast gRPC test +go_test( + name = "gogofast_grpc_test", + srcs = ["gogofast_grpc_test.go"], + deps = [":gogofast_grpc"], +) + +go_proto_library( + name = "gogofast_grpc", + compilers = ["@io_bazel_rules_go//proto:gogofast_grpc"], + importpath = "github.com/bazelbuild/rules_go/tests/core/go_proto_library/grpc", + protos = [":grpc_proto"], +) + +# adjusted_import_test +# TODO(#1851): uncomment when Bazel 0.22.0 is the minimum version. +# go_test( +# name = "adjusted_import_test", +# srcs = ["adjusted_import_test.go"], +# deps = [ +# ":adjusted_a_go_proto", +# ":adjusted_b_go_library", +# ":adjusted_c_go_proto", +# ], +# ) + +# go_proto_library( +# name = "adjusted_a_go_proto", +# importpath = "adjusted/a", +# proto = "adjusted_a_proto", +# deps = [ +# ":adjusted_b_go_library", +# ":adjusted_c_go_proto", +# ], +# ) + +# proto_library( +# name = "adjusted_a_proto", +# srcs = ["adjusted_a.proto"], +# import_prefix = "adjusted", +# strip_import_prefix = "", +# deps = [ +# ":adjusted_b_proto", +# ":adjusted_c_proto", +# ], +# ) + +# go_library( +# name = "adjusted_b_go_library", +# embed = [":adjusted_b_go_proto"], +# importpath = "adjusted/b", +# ) + +# go_proto_library( +# name = "adjusted_b_go_proto", +# importpath = "adjusted/b", +# proto = "adjusted_b_proto", +# deps = [":adjusted_c_go_proto"], +# ) + +# proto_library( +# name = "adjusted_b_proto", +# srcs = ["adjusted_b.proto"], +# import_prefix = "adjusted", +# strip_import_prefix = "", +# deps = [":adjusted_c_proto"], +# ) + +# go_proto_library( +# name = "adjusted_c_go_proto", +# importpath = "adjusted/c", +# proto = ":adjusted_c_proto", +# ) + +# proto_library( +# name = "adjusted_c_proto", +# srcs = ["adjusted_c.proto"], +# import_prefix = "adjusted", +# strip_import_prefix = "", +# ) + +# proto_package_test +proto_library( + name = "no_go_package_proto", + srcs = ["no_go_package.proto"], +) + +go_proto_library( + name = "no_go_package_go_proto", + importpath = "github.com/bazelbuild/rules_go/tests/core/go_proto_library/package_name_derived_from_importpath", + protos = [":no_go_package_proto"], +) + +go_test( + name = "proto_package_test", + srcs = ["proto_package_test.go"], + deps = [":no_go_package_go_proto"], +) + +go_test( + name = "wkt_wrapper_test", + srcs = ["wkt_wrapper_test.go"], + deps = [ + "//proto/wkt:any_go_proto", + "//proto/wkt:compiler_plugin_go_proto", + "//proto/wkt:descriptor_go_proto", + "//proto/wkt:duration_go_proto", + "//proto/wkt:empty_go_proto", + "//proto/wkt:field_mask_go_proto", + "//proto/wkt:source_context_go_proto", + "//proto/wkt:struct_go_proto", + "//proto/wkt:timestamp_go_proto", + "//proto/wkt:type_go_proto", + "//proto/wkt:wrappers_go_proto", + "@org_golang_google_protobuf//types/descriptorpb:go_default_library", + "@org_golang_google_protobuf//types/known/anypb:go_default_library", + "@org_golang_google_protobuf//types/known/durationpb:go_default_library", + "@org_golang_google_protobuf//types/known/emptypb:go_default_library", + "@org_golang_google_protobuf//types/known/fieldmaskpb:go_default_library", + "@org_golang_google_protobuf//types/known/sourcecontextpb:go_default_library", + "@org_golang_google_protobuf//types/known/structpb:go_default_library", + "@org_golang_google_protobuf//types/known/timestamppb:go_default_library", + "@org_golang_google_protobuf//types/known/typepb:go_default_library", + "@org_golang_google_protobuf//types/known/wrapperspb:go_default_library", + "@org_golang_google_protobuf//types/pluginpb:go_default_library", + ], +) diff --git a/tests/core/go_proto_library/README.rst b/tests/core/go_proto_library/README.rst new file mode 100644 index 00000000..7696102a --- /dev/null +++ b/tests/core/go_proto_library/README.rst @@ -0,0 +1,65 @@ +Basic go_proto_library functionality +==================================== + +.. _go_proto_library: /proto/core.rst#_go_proto_library +.. _go_library: /docs/go/core/rules.md#_go_library +.. _protobuf v1.26.0: https://github.com/protocolbuffers/protobuf-go/releases/tag/v1.26.0 +.. _#1422: https://github.com/bazelbuild/rules_go/issues/1422 +.. _#1596: https://github.com/bazelbuild/rules_go/issues/1596 + +Tests to ensure the basic features of `go_proto_library`_ are working. + +.. contents:: + +embed_test +---------- + +Checks that `go_proto_library`_ can embed rules that provide `GoLibrary`_. + +transitive_test +--------------- + +Checks that `go_proto_library`_ can import a proto dependency that is +embedded in a `go_library`_. Verifies `#1422`_. + +adjusted_import_test +-------------------- + +Checks that `go_proto_library`_ can build ``proto_library`` with +``import_prefix`` and ``strip_import_prefix``. + +gofast_test and gofast_grpc_test +-------------------------------- + +Checks that the gogo `gofast` compiler plugins build and link. In +particular, these plugins only depoend on `github.com/golang/protobuf`. + +gogofast_test and gogofast_grpc_test +------------------------------------ + +Checks that the `gogofast` compiler plugins build and link. In +particular, these plugins depend on both `github.com/gogo/protobuf` +and `github.com/golang/protobuf`. + +proto_package_test +------------------ + +Checks that `go_proto_library`_ generates files with a package name based on +``importpath``, not the proto package, when ``option go_package`` is not given. +This changed in `protobuf v1.26.0`_: the import path must either be specified +with an ``option go_package`` in each proto file or with an ``M`` flag passed +in from ``go_proto_library``. Previously, the Go package name was derived from +the proto package name. Previously verified `#1596`_. + +wkt_wrapper_test +---------------- + +Checks that most of the well known types in ``//proto/wkt`` are wrappers +for packages in ``@org_golang_google_protobuf``. The proto types should be +type aliases. + +protos_alias_test +----------------- + +Checks that packages generated by `go_proto_library` can be imported using one of the strings +listed in ``importpath_aliases``. diff --git a/tests/core/go_proto_library/adjusted_a.proto b/tests/core/go_proto_library/adjusted_a.proto new file mode 100644 index 00000000..d5538346 --- /dev/null +++ b/tests/core/go_proto_library/adjusted_a.proto @@ -0,0 +1,11 @@ +syntax = "proto3"; + +import "adjusted/adjusted_b.proto"; +import "adjusted/adjusted_c.proto"; + +package adjusted.a; + +message A { + adjusted.b.B x = 1; + adjusted.c.C y = 2; +} diff --git a/tests/core/go_proto_library/adjusted_b.proto b/tests/core/go_proto_library/adjusted_b.proto new file mode 100644 index 00000000..e7828b84 --- /dev/null +++ b/tests/core/go_proto_library/adjusted_b.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; + +import "adjusted/adjusted_c.proto"; + +package adjusted.b; + +message B { + adjusted.c.C b = 1; +} diff --git a/tests/core/go_proto_library/adjusted_c.proto b/tests/core/go_proto_library/adjusted_c.proto new file mode 100644 index 00000000..500144c1 --- /dev/null +++ b/tests/core/go_proto_library/adjusted_c.proto @@ -0,0 +1,7 @@ +syntax = "proto3"; + +package adjusted.c; + +message C { + int64 c = 1; +} diff --git a/tests/core/go_proto_library/adjusted_import_test.go b/tests/core/go_proto_library/adjusted_import_test.go new file mode 100644 index 00000000..6c2856c5 --- /dev/null +++ b/tests/core/go_proto_library/adjusted_import_test.go @@ -0,0 +1,31 @@ +/* Copyright 2018 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 adjusted_import_test + +import ( + "testing" + + a "adjusted/a" + b "adjusted/b" + c "adjusted/c" +) + +func use(interface{}) {} + +func TestAdjusted(t *testing.T) { + // just make sure types exist + use(a.A{X: &b.B{B: &c.C{C: 1}}, Y: &c.C{C: 1}}) +} diff --git a/tests/core/go_proto_library/bar.proto b/tests/core/go_proto_library/bar.proto new file mode 100644 index 00000000..6d945edf --- /dev/null +++ b/tests/core/go_proto_library/bar.proto @@ -0,0 +1,10 @@ +syntax = "proto3"; + +package tests.core.go_proto_library.bar; +option go_package = "github.com/bazelbuild/rules_go/tests/core/go_proto_library/bar"; + +import "tests/core/go_proto_library/foo.proto"; + +message Bar { + tests.core.go_proto_library.foo.Foo value = 1; +} diff --git a/tests/core/go_proto_library/embed_test.go b/tests/core/go_proto_library/embed_test.go new file mode 100644 index 00000000..2ba1dad4 --- /dev/null +++ b/tests/core/go_proto_library/embed_test.go @@ -0,0 +1,45 @@ +/* Copyright 2018 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 embed_test + +import ( + "testing" + + "github.com/bazelbuild/rules_go/tests/core/go_proto_library/foo" + "github.com/golang/protobuf/proto" +) + +func TestProto(t *testing.T) { + x := foo.Foo{Value: 42} + data, err := proto.Marshal(&x) + if err != nil { + t.Fatal(err) + } + var y foo.Foo + err = proto.Unmarshal(data, &y) + if err != nil { + t.Fatal(err) + } + if y.Value != x.Value { + t.Errorf("got {x = %d}; want {x = %d}", y.Value, x.Value) + } +} + +func TestExtra(t *testing.T) { + if got, want := foo.Extra(), 42; got != want { + t.Errorf("got %d; want %d", got, want) + } +} diff --git a/tests/core/go_proto_library/extra.go b/tests/core/go_proto_library/extra.go new file mode 100644 index 00000000..d27cddc8 --- /dev/null +++ b/tests/core/go_proto_library/extra.go @@ -0,0 +1,5 @@ +package foo + +func Extra() int { + return 42 +} diff --git a/tests/core/go_proto_library/foo.proto b/tests/core/go_proto_library/foo.proto new file mode 100644 index 00000000..1f164661 --- /dev/null +++ b/tests/core/go_proto_library/foo.proto @@ -0,0 +1,8 @@ +syntax = "proto3"; + +package tests.core.go_proto_library.foo; +option go_package = "github.com/bazelbuild/rules_go/tests/core/go_proto_library/foo"; + +message Foo { + int64 value = 1; +} diff --git a/tests/core/go_proto_library/gofast_grpc_test.go b/tests/core/go_proto_library/gofast_grpc_test.go new file mode 100644 index 00000000..aeb4d5e7 --- /dev/null +++ b/tests/core/go_proto_library/gofast_grpc_test.go @@ -0,0 +1,32 @@ +/* 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 gofast_grpc_test + +import ( + "testing" + + "github.com/bazelbuild/rules_go/tests/core/go_proto_library/grpc" +) + +func use(interface{}) {} + +func TestGoFastGrpc(t *testing.T) { + // just make sure types and generated functions exist + use(grpc.RPCServer(nil)) + use(grpc.RPCClient(nil)) + (&grpc.HelloRequest{}).Marshal() + (&grpc.HelloReply{}).Marshal() +} diff --git a/tests/core/go_proto_library/gofast_test.go b/tests/core/go_proto_library/gofast_test.go new file mode 100644 index 00000000..3fbfc54b --- /dev/null +++ b/tests/core/go_proto_library/gofast_test.go @@ -0,0 +1,30 @@ +/* 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 gofast_test + +import ( + "testing" + + "github.com/bazelbuild/rules_go/tests/core/go_proto_library/foo" +) + +func use(interface{}) {} + +func TestGoFast(t *testing.T) { + // just make sure types and generated functions exist + use(foo.Foo{Value: 42}) + (&foo.Foo{}).Marshal() +} diff --git a/tests/core/go_proto_library/gogofast_grpc_test.go b/tests/core/go_proto_library/gogofast_grpc_test.go new file mode 100644 index 00000000..0cf08e44 --- /dev/null +++ b/tests/core/go_proto_library/gogofast_grpc_test.go @@ -0,0 +1,32 @@ +/* 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 gogofast_grpc_test + +import ( + "testing" + + "github.com/bazelbuild/rules_go/tests/core/go_proto_library/grpc" +) + +func use(interface{}) {} + +func TestGoGoFastGrpc(t *testing.T) { + // just make sure types and generated functions exist + use(grpc.RPCServer(nil)) + use(grpc.RPCClient(nil)) + (&grpc.HelloRequest{}).Marshal() + (&grpc.HelloReply{}).Marshal() +} diff --git a/tests/core/go_proto_library/gogofast_test.go b/tests/core/go_proto_library/gogofast_test.go new file mode 100644 index 00000000..ee1d9fdf --- /dev/null +++ b/tests/core/go_proto_library/gogofast_test.go @@ -0,0 +1,30 @@ +/* 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 gogofast_test + +import ( + "testing" + + "github.com/bazelbuild/rules_go/tests/core/go_proto_library/foo" +) + +func use(interface{}) {} + +func TestGoGoFast(t *testing.T) { + // just make sure types and generated functions exist + use(foo.Foo{Value: 42}) + (&foo.Foo{}).Marshal() +} diff --git a/tests/core/go_proto_library/grpc.proto b/tests/core/go_proto_library/grpc.proto new file mode 100644 index 00000000..fd50d217 --- /dev/null +++ b/tests/core/go_proto_library/grpc.proto @@ -0,0 +1,14 @@ +syntax = "proto3"; + +package tests.core.go_proto_library.grpc; +option go_package = "github.com/bazelbuild/rules_go/tests/core/go_proto_library/grpc"; + +message HelloRequest { +} + +message HelloReply { +} + +service RPC { + rpc Hello (HelloRequest) returns (HelloReply) {} +} diff --git a/tests/core/go_proto_library/no_go_package.proto b/tests/core/go_proto_library/no_go_package.proto new file mode 100644 index 00000000..e3112c56 --- /dev/null +++ b/tests/core/go_proto_library/no_go_package.proto @@ -0,0 +1,7 @@ +syntax = "proto3"; + +package protopkg; + +message Foo { + int64 x = 1; +} diff --git a/tests/core/go_proto_library/proto_package_test.go b/tests/core/go_proto_library/proto_package_test.go new file mode 100644 index 00000000..81d66174 --- /dev/null +++ b/tests/core/go_proto_library/proto_package_test.go @@ -0,0 +1,28 @@ +/* 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 proto_package_test + +import ( + "testing" + + "github.com/bazelbuild/rules_go/tests/core/go_proto_library/package_name_derived_from_importpath" +) + +func use(interface{}) {} + +func TestNoGoPackage(t *testing.T) { + use(&package_name_derived_from_importpath.Foo{}) +} diff --git a/tests/core/go_proto_library/protos_a.proto b/tests/core/go_proto_library/protos_a.proto new file mode 100644 index 00000000..7b64d52b --- /dev/null +++ b/tests/core/go_proto_library/protos_a.proto @@ -0,0 +1,7 @@ +syntax = "proto3"; + +option go_package = "github.com/bazelbuild/rules_go/tests/core/go_proto_library/protos"; + +message A { + int64 a = 1; +} diff --git a/tests/core/go_proto_library/protos_alias_test.go b/tests/core/go_proto_library/protos_alias_test.go new file mode 100644 index 00000000..17612765 --- /dev/null +++ b/tests/core/go_proto_library/protos_alias_test.go @@ -0,0 +1,29 @@ +/* Copyright 2020 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 protos_alias_test + +import ( + "myalias/protos" // importing based on the alias import path + "testing" +) + +func use(interface{}) {} + +func TestProtos(t *testing.T) { + // just make sure both types exist + use(protos.A{}) + use(protos.B{}) +} diff --git a/tests/core/go_proto_library/protos_b.proto b/tests/core/go_proto_library/protos_b.proto new file mode 100644 index 00000000..21f81711 --- /dev/null +++ b/tests/core/go_proto_library/protos_b.proto @@ -0,0 +1,7 @@ +syntax = "proto3"; + +option go_package = "github.com/bazelbuild/rules_go/tests/core/go_proto_library/protos"; + +message B { + int64 b = 1; +} diff --git a/tests/core/go_proto_library/protos_test.go b/tests/core/go_proto_library/protos_test.go new file mode 100644 index 00000000..68083c27 --- /dev/null +++ b/tests/core/go_proto_library/protos_test.go @@ -0,0 +1,30 @@ +/* Copyright 2018 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 protos_test + +import ( + "testing" + + "github.com/bazelbuild/rules_go/tests/core/go_proto_library/protos" +) + +func use(interface{}) {} + +func TestProtos(t *testing.T) { + // just make sure both types exist + use(protos.A{}) + use(protos.B{}) +} diff --git a/tests/core/go_proto_library/proxy_a.proto b/tests/core/go_proto_library/proxy_a.proto new file mode 100644 index 00000000..a3c88444 --- /dev/null +++ b/tests/core/go_proto_library/proxy_a.proto @@ -0,0 +1,7 @@ +syntax = "proto3"; + +option go_package = "github.com/bazelbuild/rules_go/tests/core/go_proto_library/proxy"; + +message A { + int64 a = 1; +} diff --git a/tests/core/go_proto_library/proxy_b.proto b/tests/core/go_proto_library/proxy_b.proto new file mode 100644 index 00000000..d65a239f --- /dev/null +++ b/tests/core/go_proto_library/proxy_b.proto @@ -0,0 +1,7 @@ +syntax = "proto3"; + +option go_package = "github.com/bazelbuild/rules_go/tests/core/go_proto_library/proxy"; + +message B { + int64 b = 1; +} diff --git a/tests/core/go_proto_library/proxy_test.go b/tests/core/go_proto_library/proxy_test.go new file mode 100644 index 00000000..2209462f --- /dev/null +++ b/tests/core/go_proto_library/proxy_test.go @@ -0,0 +1,30 @@ +/* Copyright 2018 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 proxy_test + +import ( + "testing" + + "github.com/bazelbuild/rules_go/tests/core/go_proto_library/proxy" +) + +func use(interface{}) {} + +func TestProxy(t *testing.T) { + // just make sure both types exist + use(proxy.A{}) + use(proxy.B{}) +} diff --git a/tests/core/go_proto_library/transitive_test.go b/tests/core/go_proto_library/transitive_test.go new file mode 100644 index 00000000..1b733afe --- /dev/null +++ b/tests/core/go_proto_library/transitive_test.go @@ -0,0 +1,30 @@ +/* Copyright 2018 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 transitive_compiler_test + +import ( + "testing" + + bar "github.com/bazelbuild/rules_go/tests/core/go_proto_library/bar" + foo "github.com/bazelbuild/rules_go/tests/core/go_proto_library/foo" +) + +func use(interface{}) {} + +func TestServices(t *testing.T) { + // just make sure both types exist + use(bar.Bar{Value: &foo.Foo{Value: 42}}) +} diff --git a/tests/core/go_proto_library/wkt_wrapper_test.go b/tests/core/go_proto_library/wkt_wrapper_test.go new file mode 100644 index 00000000..16228b04 --- /dev/null +++ b/tests/core/go_proto_library/wkt_wrapper_test.go @@ -0,0 +1,67 @@ +// Copyright 2020 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 wkt_wrapper_test + +import ( + "testing" + + descriptorpb1 "github.com/golang/protobuf/protoc-gen-go/descriptor" + pluginpb1 "github.com/golang/protobuf/protoc-gen-go/plugin" + anypb1 "github.com/golang/protobuf/ptypes/any" + durationpb1 "github.com/golang/protobuf/ptypes/duration" + emptypb1 "github.com/golang/protobuf/ptypes/empty" + structpb1 "github.com/golang/protobuf/ptypes/struct" + timestamppb1 "github.com/golang/protobuf/ptypes/timestamp" + wrapperspb1 "github.com/golang/protobuf/ptypes/wrappers" + field_mask1 "google.golang.org/genproto/protobuf/field_mask" + type1 "google.golang.org/genproto/protobuf/ptype" + source_context1 "google.golang.org/genproto/protobuf/source_context" + descriptorpb2 "google.golang.org/protobuf/types/descriptorpb" + anypb2 "google.golang.org/protobuf/types/known/anypb" + durationpb2 "google.golang.org/protobuf/types/known/durationpb" + emptypb2 "google.golang.org/protobuf/types/known/emptypb" + field_mask2 "google.golang.org/protobuf/types/known/fieldmaskpb" + source_context2 "google.golang.org/protobuf/types/known/sourcecontextpb" + structpb2 "google.golang.org/protobuf/types/known/structpb" + timestamppb2 "google.golang.org/protobuf/types/known/timestamppb" + type2 "google.golang.org/protobuf/types/known/typepb" + wrapperspb2 "google.golang.org/protobuf/types/known/wrapperspb" + pluginpb2 "google.golang.org/protobuf/types/pluginpb" +) + +func Test(t *testing.T) { + var _ *anypb2.Any = (*anypb1.Any)(nil) + var _ *anypb1.Any = (*anypb2.Any)(nil) + var _ *pluginpb2.Version = (*pluginpb1.Version)(nil) + var _ *pluginpb1.Version = (*pluginpb2.Version)(nil) + var _ *descriptorpb2.DescriptorProto = (*descriptorpb1.DescriptorProto)(nil) + var _ *descriptorpb1.DescriptorProto = (*descriptorpb2.DescriptorProto)(nil) + var _ *durationpb2.Duration = (*durationpb1.Duration)(nil) + var _ *durationpb1.Duration = (*durationpb2.Duration)(nil) + var _ *emptypb2.Empty = (*emptypb1.Empty)(nil) + var _ *emptypb1.Empty = (*emptypb2.Empty)(nil) + var _ *field_mask1.FieldMask = (*field_mask2.FieldMask)(nil) + var _ *field_mask2.FieldMask = (*field_mask1.FieldMask)(nil) + var _ *source_context1.SourceContext = (*source_context2.SourceContext)(nil) + var _ *source_context2.SourceContext = (*source_context1.SourceContext)(nil) + var _ *structpb2.Struct = (*structpb1.Struct)(nil) + var _ *structpb1.Struct = (*structpb2.Struct)(nil) + var _ *timestamppb2.Timestamp = (*timestamppb1.Timestamp)(nil) + var _ *timestamppb1.Timestamp = (*timestamppb2.Timestamp)(nil) + var _ *type1.Type = (*type2.Type)(nil) + var _ *type2.Type = (*type1.Type)(nil) + var _ *wrapperspb2.BoolValue = (*wrapperspb1.BoolValue)(nil) + var _ *wrapperspb1.BoolValue = (*wrapperspb2.BoolValue)(nil) +} diff --git a/tests/core/go_proto_library_importmap/BUILD.bazel b/tests/core/go_proto_library_importmap/BUILD.bazel new file mode 100644 index 00000000..e1695b9f --- /dev/null +++ b/tests/core/go_proto_library_importmap/BUILD.bazel @@ -0,0 +1,21 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") +load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") +load("@rules_proto//proto:defs.bzl", "proto_library") + +proto_library( + name = "foo_proto", + srcs = ["foo.proto"], +) + +go_proto_library( + name = "foo_go_proto", + importmap = "never/gonna/give/you/up", + importpath = "github.com/bazelbuild/rules_go/tests/core/go_proto_library_importmap", + proto = ":foo_proto", +) + +go_test( + name = "importmap_test", + srcs = ["importmap_test.go"], + deps = [":foo_go_proto"], +) diff --git a/tests/core/go_proto_library_importmap/README.rst b/tests/core/go_proto_library_importmap/README.rst new file mode 100644 index 00000000..991f52f8 --- /dev/null +++ b/tests/core/go_proto_library_importmap/README.rst @@ -0,0 +1,8 @@ +go_proto_library importmap +========================== + +importmap_test +-------------- + +Checks that the ``importmap`` attribute of ``go_proto_library`` works correctly. +The test uses reflection information for a type in the generated file. diff --git a/tests/core/go_proto_library_importmap/foo.proto b/tests/core/go_proto_library_importmap/foo.proto new file mode 100644 index 00000000..b2bbe414 --- /dev/null +++ b/tests/core/go_proto_library_importmap/foo.proto @@ -0,0 +1,6 @@ +syntax = "proto3"; + +option go_package = "github.com/bazelbuild/rules_go/tests/core/go_proto_library_importmap;foo"; + +message Foo { +}; diff --git a/tests/core/go_proto_library_importmap/importmap_test.go b/tests/core/go_proto_library_importmap/importmap_test.go new file mode 100644 index 00000000..2ebbd815 --- /dev/null +++ b/tests/core/go_proto_library_importmap/importmap_test.go @@ -0,0 +1,16 @@ +package importmap_test + +import ( + "reflect" + "testing" + + foo "github.com/bazelbuild/rules_go/tests/core/go_proto_library_importmap" +) + +func TestImportMap(t *testing.T) { + got := reflect.TypeOf(foo.Foo{}).PkgPath() + want := "never/gonna/give/you/up" + if got != want { + t.Errorf("got %q; want %q", got, want) + } +} diff --git a/tests/core/go_test/BUILD.bazel b/tests/core/go_test/BUILD.bazel new file mode 100644 index 00000000..b77c851b --- /dev/null +++ b/tests/core/go_test/BUILD.bazel @@ -0,0 +1,261 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") +load("@io_bazel_rules_go//go/tools/bazel_testing:def.bzl", "go_bazel_test") + +test_suite( + name = "go_test", +) + +go_library( + name = "lib", + srcs = ["lib.go"], + cgo = True, + importpath = "lib", +) + +go_test( + name = "internal_test", + size = "small", + srcs = ["internal_test.go"], + embed = [":lib"], + x_defs = { + "Got": "Internal", + "Expect": "Internal", + }, +) + +go_test( + name = "external_test", + size = "small", + srcs = ["external_test.go"], + embed = [":lib"], + x_defs = { + "Got": "External", + "Expect": "External", + }, +) + +go_test( + name = "combined_test", + size = "small", + srcs = [ + "external_test.go", + "internal_test.go", + ], + embed = [":lib"], + x_defs = { + "Got": "Combined", + "Expect": "Combined", + }, +) + +go_test( + name = "flag_test", + size = "small", + srcs = [ + "flag_test.go", + ], + args = [ + "-aflag", + "somevalue", + ], +) + +go_test( + name = "example_test", + size = "small", + srcs = ["example_test.go"], + embed = [":lib"], + x_defs = { + "Got": "Example", + "Expected": "Example", + }, +) + +go_test( + name = "only_testmain_test", + size = "small", + srcs = ["only_testmain_test.go"], +) + +go_test( + name = "external_importmap_test", + size = "small", + srcs = ["external_importmap_test.go"], + embed = [":external_importmap_lib"], + deps = [":external_importmap_dep"], +) + +go_library( + name = "external_importmap_lib", + srcs = ["external_importmap_lib.go"], + importmap = "x/github.com/bazelbuild/rules_go/tests/core/go_test/external_importmap", + importpath = "github.com/bazelbuild/rules_go/tests/core/go_test/external_importmap", +) + +go_library( + name = "external_importmap_dep", + srcs = ["external_importmap_dep.go"], + importpath = "github.com/bazelbuild/rules_go/tests/core/go_test/external_importmap_dep", + deps = [":external_importmap_lib"], +) + +go_test( + name = "pwd_test", + size = "small", + srcs = ["pwd_test.go"], +) + +go_test( + name = "data_test", + size = "small", + embed = [":data_test_lib"], +) + +go_library( + name = "data_test_lib", + srcs = [":data_test_srcs"], + data = ["x"], + importpath = "github.com/bazelbuild/rules_go/tests/core/go_test/data_test", + deps = [":data_test_dep"], +) + +filegroup( + name = "data_test_srcs", + srcs = ["data_test.go"], +) + +go_library( + name = "data_test_dep", + srcs = ["data_test_dep.go"], + data = ["y"], + embed = [":data_test_embed"], + importpath = "github.com/bazelbuild/rules_go/tests/core/go_test/data_test_dep", +) + +go_library( + name = "data_test_embed", + srcs = ["data_test_embed.go"], + data = ["z"], + importpath = "github.com/bazelbuild/rules_go/tests/core/go_test/data_test_dep", +) + +go_bazel_test( + name = "test_fail_fast_test", + srcs = ["test_fail_fast_test.go"], +) + +go_bazel_test( + name = "test_filter_test", + srcs = ["test_filter_test.go"], +) + +go_bazel_test( + name = "xmlreport_test", + srcs = ["xmlreport_test.go"], +) + +go_test( + name = "testmain_import_test", + srcs = [ + "testmain_import_indirect_test.go", + "testmain_import_main_test.go", + ], + importpath = "example.com/imports/test_main", +) + +go_test( + name = "tags_test", + srcs = [ + "tags_bad_test.go", + "tags_good_test.go", + ], + gotags = ["good"], +) + +go_test( + name = "indirect_import_test", + srcs = [ + "indirect_import_i_test.go", + "indirect_import_x_test.go", + ], + embed = [":indirect_import_lib"], + deps = [":indirect_import_dep"], +) + +go_library( + name = "indirect_import_lib", + srcs = ["indirect_import_lib.go"], + importpath = "github.com/bazelbuild/rules_go/tests/core/go_test/indirect_import", +) + +go_library( + name = "indirect_import_dep", + srcs = ["indirect_import_dep.go"], + importpath = "github.com/bazelbuild/rules_go/tests/core/go_test/indirect_import_dep", + deps = [":indirect_import_lib"], +) + +[ + go_test( + name = "same_package_{}_test".format(i), + srcs = ["same_package_test.go"], + ) + for i in range(1, 80) +] + +test_suite( + name = "same_package_test", + tests = ["same_package_{}_test".format(i) for i in range(1, 80)], +) + +go_bazel_test( + name = "testmain_without_exit_test", + srcs = ["testmain_without_exit_test.go"], +) + +go_test( + name = "wrapper_test", + srcs = ["wrapper_test.go"], + args = ["$(location :wrapped_test)"], + data = [":wrapped_test"], + rundir = ".", +) + +go_test( + name = "wrapped_test", + srcs = ["wrapped_test.go"], + tags = ["manual"], +) + +go_test( + name = "fuzz_test", + srcs = ["fuzz_test.go"], +) + +go_test( + name = "env_test", + srcs = ["env_test.go"], + data = ["@go_sdk//:lib/time/zoneinfo.zip"], + env = { + "ZONEINFO": "$(execpath @go_sdk//:lib/time/zoneinfo.zip)", + }, + deps = [ + "@io_bazel_rules_go//go/tools/bazel", + ], +) + +go_test( + name = "sharding_test", + srcs = ["sharding_test.go"], + shard_count = 2, +) + +go_bazel_test( + name = "env_inherit_test", + srcs = ["env_inherit_test.go"], +) + +go_bazel_test( + name = "binary_env_test", + srcs = ["binary_env_test.go"], +) diff --git a/tests/core/go_test/README.rst b/tests/core/go_test/README.rst new file mode 100644 index 00000000..176cf196 --- /dev/null +++ b/tests/core/go_test/README.rst @@ -0,0 +1,126 @@ +Basic go_test functionality +=========================== + +.. _go_test: /docs/go/core/rules.md#_go_test +.. _#1877: https://github.com/bazelbuild/rules_go/issues/1877 +.. _#34129: https:////github.com/golang/go/issues/34129 +.. _#2749: https://github.com/bazelbuild/rules_go/issues/2749 + +Tests to ensure that basic features of `go_test`_ are working as expected. + +.. contents:: + +internal_test +------------- + +Test that a `go_test`_ rule that adds white box tests to an embedded package works. +This builds a library with `lib.go <lib.go>`_ and then a package with an +`internal test <internal_test.go>`_ that contains the test case. +It uses x_def stamped values to verify the library names are correct. + +external_test +------------- + +Test that a `go_test`_ rule that adds black box tests for a dependant package works. +This builds a library with `lib.go <lib.go>`_ and then a package with an +`external test <external_test.go>`_ that contains the test case. +It uses x_def stamped values to verify the library names are correct. + +combined_test +------------- +Test that a `go_test`_ rule that adds both white and black box tests for a +package works. +This builds a library with `lib.go <lib.go>`_ and then a one merged with the +`internal test <internal_test.go>`_, and then another one that depends on it +with the `external test <external_test.go>`_. +It uses x_def stamped values to verify that all library names are correct. +Verifies #413 + +flag_test +--------- +Test that a `go_test`_ rule that adds flags, even in the main package, can read +the flag. +This does not even build a library, it's a test in the main package with no +dependancies that checks it can declare and then read a flag. +Verifies #838 + +only_testmain_test +------------------ +Test that an `go_test`_ that contains a ``TestMain`` function but no tests +still builds and passes. + +external_importmap_test +---------------------- + +Test that an external test package in `go_test`_ is compiled with the correct +``importmap`` for the library under test. This is verified by defining an +interface in the library under test and implementing it in a separate +dependency. + +Verifies #1538. + +pwd_test +-------- + +Checks that the ``PWD`` environment variable is set to the current directory +in the generated test main before running a test. This matches functionality +in ``go test``. + +Verifies #1561. + +data_test +--------- + +Checks that data dependencies, including those inherited from ``deps`` and +``embed``, are visible to tests at run-time. Source files should not be +visible at run-time. + +test_fail_fast_test +---------------- + +Checks that ``--test_runner_fail_fast`` actually enables stopping test execution after +the first failure. + +Verifies #3055. + +test_filter_test +---------------- + +Checks that ``--test_filter`` actually filters out test cases. + +testmain_import_test +---------------- + +Check if all packages in all source files are imported to test main, to ensure +a consistent test behaviour. This ensures a consistent behaviour when thinking +about global indirect depencencies. + +tags_test +--------- + +Checks that setting ``gotags`` affects source filtering. The test will fail +unless a specific tag is set. + +indirect_import_test +-------------------- + +Checks that an external test can import another package that imports the library +under test. The other package should be compiled against the internal test +package, not the library under test. Verifies `#1877`_. + +testmain_without_exit +--------------------- + +Checks that TestMain without calling os.Exit directly works. +Verifies `#34129`_ from Go 1.15. + +wrapper_test +------------ + +Checks that a ``go_test`` can be executed by another test in a subdirectory. +Verifies `#2749`_. + +fuzz_test +--------- + +Checks that a ``go_test`` with a fuzz target builds correctly. diff --git a/tests/core/go_test/binary_env_test.go b/tests/core/go_test/binary_env_test.go new file mode 100644 index 00000000..25089034 --- /dev/null +++ b/tests/core/go_test/binary_env_test.go @@ -0,0 +1,55 @@ +// Copyright 2023 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 binary_env_test + +import ( + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Main: ` +-- src/BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_binary") +go_binary( + name = "main", + srcs = ["env.go"], + env = {"FOO": "bar"}, +) +-- src/env.go -- +package main + +import ( + "log" + "os" +) + +func main() { + v := os.Getenv("FOO") + if v != "bar" { + log.Fatalf("FOO was not equal to bar") + } +} +`, + }) +} + +func TestBinaryEnv(t *testing.T) { + if err := bazel_testing.RunBazel("run", "//src:main"); err != nil { + t.Fatal(err) + } +} diff --git a/tests/core/go_test/data_test.go b/tests/core/go_test/data_test.go new file mode 100644 index 00000000..fd8a3930 --- /dev/null +++ b/tests/core/go_test/data_test.go @@ -0,0 +1,45 @@ +// Copyright 2018 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 data_test + +import ( + "os" + "path/filepath" + "strings" + "testing" +) + +func TestVisibleRunfiles(t *testing.T) { + var got []string + err := filepath.Walk(".", func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if base := filepath.Base(path); info.IsDir() || base == "data_test" || base == "data_test.exe" { + return nil + } + got = append(got, filepath.ToSlash(path)) + return nil + }) + if err != nil { + t.Fatal(err) + } + + gotStr := strings.Join(got, "\n") + wantStr := "x\ny\nz" + if gotStr != wantStr { + t.Errorf("got:\n%s\nwant:\n%s\n", gotStr, wantStr) + } +} diff --git a/tests/core/go_test/data_test_dep.go b/tests/core/go_test/data_test_dep.go new file mode 100644 index 00000000..d8eb2672 --- /dev/null +++ b/tests/core/go_test/data_test_dep.go @@ -0,0 +1,15 @@ +// Copyright 2018 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 data_test_dep diff --git a/tests/core/go_test/data_test_embed.go b/tests/core/go_test/data_test_embed.go new file mode 100644 index 00000000..d8eb2672 --- /dev/null +++ b/tests/core/go_test/data_test_embed.go @@ -0,0 +1,15 @@ +// Copyright 2018 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 data_test_dep diff --git a/tests/core/go_test/env_inherit_test.go b/tests/core/go_test/env_inherit_test.go new file mode 100644 index 00000000..b425abe2 --- /dev/null +++ b/tests/core/go_test/env_inherit_test.go @@ -0,0 +1,61 @@ +// Copyright 2021 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 env_inherit_test + +import ( + "os" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Main: ` +-- src/BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_test") +go_test( + name = "main", + srcs = ["env_inherit.go"], + env_inherit = ["INHERITEDVAR"], +) +-- src/env_inherit.go -- +package env_inherit_test + +import ( + "os" + "testing" +) + +func TestInherit(t *testing.T) { + v := os.Getenv("INHERITEDVAR") + if v != "b" { + t.Fatalf("INHERITEDVAR was not equal to b") + } +} +`, + + SetUp: func() error { + os.Setenv("INHERITEDVAR", "b") + return nil + }, + }) +} + +func TestInheritedEnvVar(t *testing.T) { + if err := bazel_testing.RunBazel("test", "//src:main"); err != nil { + t.Fatal(err) + } +} diff --git a/tests/core/go_test/env_test.go b/tests/core/go_test/env_test.go new file mode 100644 index 00000000..89dae3ca --- /dev/null +++ b/tests/core/go_test/env_test.go @@ -0,0 +1,39 @@ +// Copyright 2021 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 env_test + +import ( + "os" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel" +) + +func TestEnv(t *testing.T) { + v := os.Getenv("ZONEINFO") + if v == "" { + t.Fatalf("ZONEINFO env var was empty") + } + + path, err := bazel.Runfile(v) + if err != nil { + t.Fatalf("Could not find runfile %v: %v", v, err) + } + + if _, err := os.Stat(path); err != nil { + t.Fatalf("Could not find file at env var $ZONEINFO (value: %v) at path %v: %v", v, path, err) + } +} + diff --git a/tests/core/go_test/example_test.go b/tests/core/go_test/example_test.go new file mode 100644 index 00000000..6314e69c --- /dev/null +++ b/tests/core/go_test/example_test.go @@ -0,0 +1,32 @@ +package lib + +import "fmt" + +var Expected = "Expected" + +func ExampleHelloWorld() { + fmt.Println("Hello Example!") + fmt.Println("expected: " + Expected) + fmt.Println("got: " + Got) + // Output: + // Hello Example! + // expected: Example + // got: Example +} + +func ExampleDontTestMe() { + panic("Dont Test Me!") +} + +func ExampleTestEmptyOutput() { + if false { + fmt.Println("Say something!") + } + // Output: +} + +func ExampleTestQuoting() { + fmt.Printf(`"quotes are handled"`) + // Output: + // "quotes are handled" +} diff --git a/tests/core/go_test/external_importmap_dep.go b/tests/core/go_test/external_importmap_dep.go new file mode 100644 index 00000000..c9990774 --- /dev/null +++ b/tests/core/go_test/external_importmap_dep.go @@ -0,0 +1,11 @@ +package external_importmap_dep + +import ( + "github.com/bazelbuild/rules_go/tests/core/go_test/external_importmap" +) + +type Impl struct{} + +func (_ *Impl) DeepCopyObject() external_importmap.Object { + return nil +} diff --git a/tests/core/go_test/external_importmap_lib.go b/tests/core/go_test/external_importmap_lib.go new file mode 100644 index 00000000..e9800a1c --- /dev/null +++ b/tests/core/go_test/external_importmap_lib.go @@ -0,0 +1,5 @@ +package external_importmap + +type Object interface { + DeepCopyObject() Object +} diff --git a/tests/core/go_test/external_importmap_test.go b/tests/core/go_test/external_importmap_test.go new file mode 100644 index 00000000..fbcc55ef --- /dev/null +++ b/tests/core/go_test/external_importmap_test.go @@ -0,0 +1,10 @@ +package external_importmap_test + +import ( + "github.com/bazelbuild/rules_go/tests/core/go_test/external_importmap" + "github.com/bazelbuild/rules_go/tests/core/go_test/external_importmap_dep" +) + +var _ external_importmap.Object = &external_importmap_dep.Impl{} + +// Test passes if it compiles. diff --git a/tests/core/go_test/external_test.go b/tests/core/go_test/external_test.go new file mode 100644 index 00000000..957cce9e --- /dev/null +++ b/tests/core/go_test/external_test.go @@ -0,0 +1,29 @@ +/* Copyright 2017 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 lib_test + +import ( + "lib" + "testing" +) + +var Expect = "Expect" + +func TestExternal(t *testing.T) { + if lib.Got != Expect { + t.Errorf("Got %q expected %q", lib.Got, Expect) + } +} diff --git a/tests/core/go_test/flag_test.go b/tests/core/go_test/flag_test.go new file mode 100644 index 00000000..ec7c3576 --- /dev/null +++ b/tests/core/go_test/flag_test.go @@ -0,0 +1,30 @@ +/* Copyright 2018 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 main + +import ( + "flag" + "testing" +) + +const flagDefault = "flag default" + +var aflag = flag.String("aflag", flagDefault, "A test flag") + +func TestAFlag(t *testing.T) { + if *aflag == flagDefault { + t.Error("aflag is still the default") + } +} diff --git a/tests/core/go_test/fuzz_test.go b/tests/core/go_test/fuzz_test.go new file mode 100644 index 00000000..48c88194 --- /dev/null +++ b/tests/core/go_test/fuzz_test.go @@ -0,0 +1,29 @@ +// 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. + +//go:build go1.18 +// +build go1.18 + +package fuzz_test + +import "testing" + +func Fuzz(f *testing.F) { + f.Add("seed") + f.Fuzz(func(t *testing.T, s string) { + if s != "seed" { + t.Fail() + } + }) +} diff --git a/tests/core/go_test/indirect_import_dep.go b/tests/core/go_test/indirect_import_dep.go new file mode 100644 index 00000000..c47d28b5 --- /dev/null +++ b/tests/core/go_test/indirect_import_dep.go @@ -0,0 +1,7 @@ +package indirect_import_dep + +import "github.com/bazelbuild/rules_go/tests/core/go_test/indirect_import" + +func GetX() string { + return indirect_import.X +} diff --git a/tests/core/go_test/indirect_import_i_test.go b/tests/core/go_test/indirect_import_i_test.go new file mode 100644 index 00000000..e6814143 --- /dev/null +++ b/tests/core/go_test/indirect_import_i_test.go @@ -0,0 +1,7 @@ +package indirect_import + +import "testing" + +func TestMain(m *testing.M) { + X = "set by TestMain" +} diff --git a/tests/core/go_test/indirect_import_lib.go b/tests/core/go_test/indirect_import_lib.go new file mode 100644 index 00000000..915c0dfe --- /dev/null +++ b/tests/core/go_test/indirect_import_lib.go @@ -0,0 +1,3 @@ +package indirect_import + +var X string = "not set" diff --git a/tests/core/go_test/indirect_import_x_test.go b/tests/core/go_test/indirect_import_x_test.go new file mode 100644 index 00000000..008063ce --- /dev/null +++ b/tests/core/go_test/indirect_import_x_test.go @@ -0,0 +1,15 @@ +package indirect_import_test + +import ( + "testing" + + "github.com/bazelbuild/rules_go/tests/core/go_test/indirect_import_dep" +) + +func Test(t *testing.T) { + got := indirect_import_dep.GetX() + want := "set by TestMain" + if got != want { + t.Errorf("got %q; want %q", got, want) + } +} diff --git a/tests/core/go_test/internal_test.go b/tests/core/go_test/internal_test.go new file mode 100644 index 00000000..fdb112e6 --- /dev/null +++ b/tests/core/go_test/internal_test.go @@ -0,0 +1,28 @@ +/* Copyright 2017 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 lib + +import ( + "testing" +) + +var Expect = "Expect" + +func TestInternal(t *testing.T) { + if Got != Expect { + t.Errorf("Got %q expected %q", Got, Expect) + } +} diff --git a/tests/core/go_test/lib.go b/tests/core/go_test/lib.go new file mode 100644 index 00000000..f34c854f --- /dev/null +++ b/tests/core/go_test/lib.go @@ -0,0 +1,20 @@ +/* Copyright 2017 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 lib + +import "C" + +var Got = "Got" diff --git a/tests/core/go_test/only_testmain_test.go b/tests/core/go_test/only_testmain_test.go new file mode 100644 index 00000000..474a3d6c --- /dev/null +++ b/tests/core/go_test/only_testmain_test.go @@ -0,0 +1,10 @@ +package only_testmain + +import ( + "os" + "testing" +) + +func TestMain(m *testing.M) { + os.Exit(m.Run()) +} diff --git a/tests/core/go_test/pwd_test.go b/tests/core/go_test/pwd_test.go new file mode 100644 index 00000000..9de4abf0 --- /dev/null +++ b/tests/core/go_test/pwd_test.go @@ -0,0 +1,16 @@ +package pwd + +import ( + "os" + "path/filepath" + "strings" + "testing" +) + +func TestPwd(t *testing.T) { + pwd := os.Getenv("PWD") + suffix := filepath.FromSlash("tests/core/go_test") + if !strings.HasSuffix(pwd, filepath.FromSlash(suffix)) { + t.Errorf("PWD not set. got %q; want something ending with %q", pwd, suffix) + } +} diff --git a/tests/core/go_test/same_package_test.go b/tests/core/go_test/same_package_test.go new file mode 100644 index 00000000..cf991492 --- /dev/null +++ b/tests/core/go_test/same_package_test.go @@ -0,0 +1,7 @@ +package same_package + +import ( + "testing" +) + +func OkTest(t *testing.T) {} diff --git a/tests/core/go_test/sharding_test.go b/tests/core/go_test/sharding_test.go new file mode 100644 index 00000000..a83bb85c --- /dev/null +++ b/tests/core/go_test/sharding_test.go @@ -0,0 +1,13 @@ +package sharding_test + +import ( + "log" + "os" + "testing" +) + +func TestShardStatusFile(t *testing.T) { + if _, err := os.Stat(os.Getenv("TEST_SHARD_STATUS_FILE")); err != nil { + log.Fatalf("Expected Go test runner to create TEST_SHARD_STATUS_FILE: %v", err) + } +} diff --git a/tests/core/go_test/tags_bad_test.go b/tests/core/go_test/tags_bad_test.go new file mode 100644 index 00000000..76d32cd5 --- /dev/null +++ b/tests/core/go_test/tags_bad_test.go @@ -0,0 +1,9 @@ +// +build !good + +package tags_test + +import "testing" + +func Test(t *testing.T) { + t.Fail() +} diff --git a/tests/core/go_test/tags_good_test.go b/tests/core/go_test/tags_good_test.go new file mode 100644 index 00000000..60071805 --- /dev/null +++ b/tests/core/go_test/tags_good_test.go @@ -0,0 +1,7 @@ +// +build good + +package tags_test + +import "testing" + +func Test(t *testing.T) {} diff --git a/tests/core/go_test/test_fail_fast_test.go b/tests/core/go_test/test_fail_fast_test.go new file mode 100644 index 00000000..b949b489 --- /dev/null +++ b/tests/core/go_test/test_fail_fast_test.go @@ -0,0 +1,75 @@ +// Copyright 2022 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 test_fail_fast_test + +import ( + "bytes" + "io/ioutil" + "path/filepath" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Main: ` +-- BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "fail_fast_test", + srcs = ["fail_fast_test.go"], +) + +-- fail_fast_test.go -- +package test_fail_fast + +import "testing" + +func TestShouldFail(t *testing.T) { + t.Fail() +} + +func TestShouldNotRun(t *testing.T) { + t.Fail() +} +`, + }) +} + +func Test(t *testing.T) { + if err := bazel_testing.RunBazel("test", "//:fail_fast_test", "--test_runner_fail_fast"); err == nil { + t.Fatal("got success; want failure") + } else if bErr, ok := err.(*bazel_testing.StderrExitError); !ok { + t.Fatalf("got %v; want StderrExitError", err) + } else if code := bErr.Err.ExitCode(); code != 3 { + t.Fatalf("got code %d; want code 3", code) + } + + logPath := filepath.FromSlash("bazel-testlogs/fail_fast_test/test.log") + logData, err := ioutil.ReadFile(logPath) + if err != nil { + t.Fatal(err) + } + + if !bytes.Contains(logData, []byte("TestShouldFail")) { + t.Fatalf("test log does not contain 'TestShouldFail': %q", logData) + } + + if bytes.Contains(logData, []byte("TestShouldNotRun")) { + t.Fatalf("test log contains 'TestShouldNotRun' but should not: %q", logData) + } +} diff --git a/tests/core/go_test/test_filter_test.go b/tests/core/go_test/test_filter_test.go new file mode 100644 index 00000000..49b1ce73 --- /dev/null +++ b/tests/core/go_test/test_filter_test.go @@ -0,0 +1,54 @@ +// 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 test_filter_test + +import ( + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Main: ` +-- BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "filter_test", + srcs = ["filter_test.go"], +) + +-- filter_test.go -- +package test_filter + +import "testing" + +func TestShouldPass(t *testing.T) { +} + +func TestShouldFail(t *testing.T) { + t.Fail() +} + +`, + }) +} + +func Test(t *testing.T) { + if err := bazel_testing.RunBazel("test", "//:filter_test", "--test_filter=Pass"); err != nil { + t.Fatal(err) + } +} diff --git a/tests/core/go_test/testmain_import_indirect_test.go b/tests/core/go_test/testmain_import_indirect_test.go new file mode 100644 index 00000000..bcb6fac8 --- /dev/null +++ b/tests/core/go_test/testmain_import_indirect_test.go @@ -0,0 +1,7 @@ +package test_main_test + +import "example.com/imports/test_main" + +func init() { + test_main.Updated = true +} diff --git a/tests/core/go_test/testmain_import_main_test.go b/tests/core/go_test/testmain_import_main_test.go new file mode 100644 index 00000000..b723bf06 --- /dev/null +++ b/tests/core/go_test/testmain_import_main_test.go @@ -0,0 +1,11 @@ +package test_main + +import "testing" + +var Updated bool + +func TestShouldPass(t *testing.T) { + if !Updated { + t.Fail() + } +} diff --git a/tests/core/go_test/testmain_without_exit_test.go b/tests/core/go_test/testmain_without_exit_test.go new file mode 100644 index 00000000..6944747d --- /dev/null +++ b/tests/core/go_test/testmain_without_exit_test.go @@ -0,0 +1,59 @@ +// Copyright 2020 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 testmain_without_exit + +import ( + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Main: ` +-- BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "main_without_exit_test", + srcs = ["main_without_exit_test.go"], +) + +-- main_without_exit_test.go -- +package test_main_without_exit + +import "testing" + +func TestMain(m *testing.M) { + m.Run() +} + +func TestShouldFail(t *testing.T) { + t.Fail() +} +`, + }) +} + +func Test(t *testing.T) { + err := bazel_testing.RunBazel("test", "//:main_without_exit_test") + if err == nil { + t.Fatal("expected bazel test to have failed") + } + + if xerr, ok := err.(*bazel_testing.StderrExitError); !ok || xerr.Err.ExitCode() != 3 { + t.Fatalf("expected bazel tests to fail with exit code 3 (TESTS_FAILED), got: %s", err) + } +} diff --git a/tests/core/go_test/wrapped_test.go b/tests/core/go_test/wrapped_test.go new file mode 100644 index 00000000..ce8c1933 --- /dev/null +++ b/tests/core/go_test/wrapped_test.go @@ -0,0 +1,19 @@ +// Copyright 2020 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 wrapped_test + +import "testing" + +func Test(t *testing.T) {} diff --git a/tests/core/go_test/wrapper_test.go b/tests/core/go_test/wrapper_test.go new file mode 100644 index 00000000..c889ccac --- /dev/null +++ b/tests/core/go_test/wrapper_test.go @@ -0,0 +1,46 @@ +// Copyright 2020 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 wrapper_test + +import ( + "io/ioutil" + "os" + "os/exec" + "testing" +) + +func Test(t *testing.T) { + if _, ok := os.LookupEnv("RUNFILES_MANIFEST_FILE"); ok { + t.Skipf("test only applicable with a runfiles directory") + } + + tmpLog, err := ioutil.TempFile("", "tmp.xml") + if err != nil { + t.Fatal(err) + } + defer func() { + tmpLog.Close() + os.Remove(tmpLog.Name()) + }() + + arg := os.Args[1] + cmd := exec.Command(arg) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Env = append(os.Environ(), "GO_TEST_WRAP=1", "XML_OUTPUT_FILE="+tmpLog.Name()) + if err := cmd.Run(); err != nil { + t.Fatalf("running wrapped_test: %v", err) + } +} diff --git a/tests/core/go_test/x b/tests/core/go_test/x new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/core/go_test/x diff --git a/tests/core/go_test/x_defs/BUILD.bazel b/tests/core/go_test/x_defs/BUILD.bazel new file mode 100644 index 00000000..7edc4885 --- /dev/null +++ b/tests/core/go_test/x_defs/BUILD.bazel @@ -0,0 +1,73 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +test_suite( + name = "x_defs", +) + +go_test( + name = "foo_test", + srcs = ["foo_test.go"], + embed = [":foo"], + importpath = "github.com/bazelbuild/rules_go/tests/core/go_test/x_defs/foo", + pure = "on", + deps = [":bar"], +) + +go_library( + name = "bar", + srcs = ["bar.go"], + importpath = "github.com/bazelbuild/rules_go/tests/core/go_test/x_defs/bar", + visibility = ["//visibility:public"], + x_defs = {"Bar": "Bar"}, + deps = [ + ":baz", + ":foo", + ], +) + +go_library( + name = "baz", + srcs = ["baz.go"], + importpath = "github.com/bazelbuild/rules_go/tests/core/go_test/x_defs/baz", + visibility = ["//visibility:public"], + x_defs = {"Baz": "Baz"}, + deps = [":qux"], +) + +go_library( + name = "foo", + srcs = ["foo.go"], + importpath = "github.com/bazelbuild/rules_go/tests/core/go_test/x_defs/foo", + visibility = ["//visibility:public"], +) + +go_library( + name = "qux", + srcs = ["qux.go"], + importpath = "github.com/bazelbuild/rules_go/tests/core/go_test/x_defs/qux", + visibility = ["//visibility:public"], + x_defs = {"Qux": "Qux"}, +) + +go_library( + name = "x_defs_lib", + srcs = ["x_defs_lib.go"], + data = ["x_defs_lib.go"], + importpath = "github.com/bazelbuild/rules_go/tests/core/go_test/x_defs/x_defs_lib", + x_defs = { + "LibGo": "$(rlocationpath x_defs_lib.go)", + }, +) + +go_test( + name = "x_defs_test", + srcs = ["x_defs_test.go"], + data = ["x_defs_test.go"], + x_defs = { + "BinGo": "$(rlocationpath x_defs_test.go)", + }, + deps = [ + ":x_defs_lib", + "//go/runfiles", + ], +) diff --git a/tests/core/go_test/x_defs/bar.go b/tests/core/go_test/x_defs/bar.go new file mode 100644 index 00000000..1d0081a4 --- /dev/null +++ b/tests/core/go_test/x_defs/bar.go @@ -0,0 +1,10 @@ +package bar + +import ( + "github.com/bazelbuild/rules_go/tests/core/go_test/x_defs/baz" + _ "github.com/bazelbuild/rules_go/tests/core/go_test/x_defs/foo" +) + +var Bar string +var Qux = baz.Qux +var Baz = baz.Baz diff --git a/tests/core/go_test/x_defs/baz.go b/tests/core/go_test/x_defs/baz.go new file mode 100644 index 00000000..9d4334f7 --- /dev/null +++ b/tests/core/go_test/x_defs/baz.go @@ -0,0 +1,6 @@ +package baz + +import "github.com/bazelbuild/rules_go/tests/core/go_test/x_defs/qux" + +var Baz string +var Qux = qux.Qux diff --git a/tests/core/go_test/x_defs/foo.go b/tests/core/go_test/x_defs/foo.go new file mode 100644 index 00000000..f52652b1 --- /dev/null +++ b/tests/core/go_test/x_defs/foo.go @@ -0,0 +1 @@ +package foo diff --git a/tests/core/go_test/x_defs/foo_test.go b/tests/core/go_test/x_defs/foo_test.go new file mode 100644 index 00000000..dc940509 --- /dev/null +++ b/tests/core/go_test/x_defs/foo_test.go @@ -0,0 +1,28 @@ +package foo_test + +import ( + "testing" + + "github.com/bazelbuild/rules_go/tests/core/go_test/x_defs/bar" +) + +func TestBar(t *testing.T) { + Equal(t, "Bar", bar.Bar) +} + +func TestBaz(t *testing.T) { + Equal(t, "Baz", bar.Baz) +} + +func TestQux(t *testing.T) { + Equal(t, "Qux", bar.Qux) +} + +func Equal(t *testing.T, expected string, actual string) bool { + if expected != actual { + t.Errorf("Not equal: \n"+ + "expected: %s\n"+ + "actual : %s", expected, actual) + } + return true +} diff --git a/tests/core/go_test/x_defs/qux.go b/tests/core/go_test/x_defs/qux.go new file mode 100644 index 00000000..4983170c --- /dev/null +++ b/tests/core/go_test/x_defs/qux.go @@ -0,0 +1,3 @@ +package qux + +var Qux string diff --git a/tests/core/go_test/x_defs/x_defs_lib.go b/tests/core/go_test/x_defs/x_defs_lib.go new file mode 100644 index 00000000..e7794b33 --- /dev/null +++ b/tests/core/go_test/x_defs/x_defs_lib.go @@ -0,0 +1,3 @@ +package x_defs_lib + +var LibGo = "not set" diff --git a/tests/core/go_test/x_defs/x_defs_test.go b/tests/core/go_test/x_defs/x_defs_test.go new file mode 100644 index 00000000..0832c247 --- /dev/null +++ b/tests/core/go_test/x_defs/x_defs_test.go @@ -0,0 +1,34 @@ +package x_defs_lib_test + +import ( + "os" + "testing" + + "github.com/bazelbuild/rules_go/go/runfiles" + + "github.com/bazelbuild/rules_go/tests/core/go_test/x_defs/x_defs_lib" +) + +var BinGo = "not set" + +func TestLibGoPath(t *testing.T) { + libGoPath, err := runfiles.Rlocation(x_defs_lib.LibGo) + if err != nil { + t.Fatal(err) + } + _, err = os.Stat(libGoPath) + if err != nil { + t.Fatal(err) + } +} + +func TestBinGoPath(t *testing.T) { + binGoPath, err := runfiles.Rlocation(BinGo) + if err != nil { + t.Fatal(err) + } + _, err = os.Stat(binGoPath) + if err != nil { + t.Fatal(err) + } +} diff --git a/tests/core/go_test/xmlreport_test.go b/tests/core/go_test/xmlreport_test.go new file mode 100644 index 00000000..09198356 --- /dev/null +++ b/tests/core/go_test/xmlreport_test.go @@ -0,0 +1,163 @@ +// Copyright 2020 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 test_filter_test + +import ( + "encoding/xml" + "io/ioutil" + "path/filepath" + "reflect" + "strings" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Main: ` +-- BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "xml_test", + importpath = "github.com/bazelbuild/rules_go/tests/core/go_test/xml_test", + srcs = ["xml_test.go"], +) + +-- xml_test.go -- +package test + +import ( + "math/rand" + "testing" + "time" +) + +func TestPass(t *testing.T) { + t.Parallel() +} + +func TestPassLog(t *testing.T) { + t.Parallel() + t.Log("pass") +} + +func TestFail(t *testing.T) { + t.Error("Not working") +} + +func TestSubtests(t *testing.T) { + for i, subtest := range []string{"subtest a", "testB", "another subtest"} { + t.Run(subtest, func(t *testing.T) { + t.Logf("from subtest %s", subtest) + time.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond) + t.Logf("from subtest %s", subtest) + if i%3 == 0 { + t.Skip("skipping this test") + } + if i%2 == 0 { + t.Fail() + } + }) + } +} +`, + }) +} + +// test execution time attributes will vary per testrun, so we must parse the +// xml to inspect a subset of testresults +type xmlTestSuite struct { + XMLName xml.Name `xml:"testsuite"` + Errors int `xml:"errors,attr"` + Failures int `xml:"failures,attr"` + Skipped int `xml:"skipped,attr"` + Tests int `xml:"tests,attr"` + Name string `xml:"name,attr"` +} +type xmlTestSuites struct { + XMLName xml.Name `xml:"testsuites"` + Suites []xmlTestSuite `xml:"testsuite"` +} + +func Test(t *testing.T) { + tests := []struct { + name string + args []string + expected xmlTestSuites + }{ + { + name: "default", + args: []string{"test", "//:xml_test"}, + expected: xmlTestSuites{ + XMLName: xml.Name{Local: "testsuites"}, + Suites: []xmlTestSuite{{ + XMLName: xml.Name{Local: "testsuite"}, + Name: "github.com/bazelbuild/rules_go/tests/core/go_test/xml_test", + Errors: 0, + Failures: 3, + Tests: 3, + }}, + }, + }, + { + name: "verbose", + args: []string{"test", "--test_env=GO_TEST_WRAP_TESTV=1", "//:xml_test"}, + expected: xmlTestSuites{ + XMLName: xml.Name{Local: "testsuites"}, + Suites: []xmlTestSuite{{ + XMLName: xml.Name{Local: "testsuite"}, + Name: "github.com/bazelbuild/rules_go/tests/core/go_test/xml_test", + Errors: 0, + Failures: 3, + Skipped: 1, + Tests: 7, + }}, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := bazel_testing.RunBazel(tt.args...) + if err == nil { + t.Fatal("expected bazel test to have failed") + } + if xerr, ok := err.(*bazel_testing.StderrExitError); !ok || xerr.Err.ExitCode() != 3 { + t.Fatalf("expected bazel tests to fail with exit code 3 (TESTS_FAILED), got: %s", err) + } + + p, err := bazel_testing.BazelOutput("info", "bazel-testlogs") + if err != nil { + t.Fatal("could not find testlog root: %s", err) + } + path := filepath.Join(strings.TrimSpace(string(p)), "xml_test/test.xml") + b, err := ioutil.ReadFile(path) + if err != nil { + t.Fatalf("could not read generated xml file: %s", err) + } + + var suites xmlTestSuites + if err := xml.Unmarshal(b, &suites); err != nil { + t.Fatalf("could not unmarshall generated xml: %s", err) + } + + if !reflect.DeepEqual(suites, tt.expected) { + t.Fatalf("expected %#v, got: %#v", tt.expected, suites) + } + }) + } +} diff --git a/tests/core/go_test/y b/tests/core/go_test/y new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/core/go_test/y diff --git a/tests/core/go_test/z b/tests/core/go_test/z new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/core/go_test/z diff --git a/tests/core/importmap/BUILD.bazel b/tests/core/importmap/BUILD.bazel new file mode 100644 index 00000000..115ab878 --- /dev/null +++ b/tests/core/importmap/BUILD.bazel @@ -0,0 +1,45 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +test_suite( + name = "importmap", +) + +go_library( + name = "lib_a", + srcs = ["lib.go"], + importmap = "a/lib", + importpath = "lib", + x_defs = {"Value": "ValueA"}, +) + +go_library( + name = "lib_b", + srcs = ["lib.go"], + importmap = "b/lib", + importpath = "lib", + x_defs = {"Value": "ValueB"}, +) + +go_library( + name = "a", + srcs = ["import.go"], + importpath = "a", + deps = [":lib_a"], +) + +go_library( + name = "b", + srcs = ["import.go"], + importpath = "b", + deps = [":lib_b"], +) + +go_test( + name = "importmap_test", + size = "small", + srcs = ["importmap_test.go"], + deps = [ + ":a", + ":b", + ], +) diff --git a/tests/core/importmap/README.rst b/tests/core/importmap/README.rst new file mode 100644 index 00000000..7abf58d5 --- /dev/null +++ b/tests/core/importmap/README.rst @@ -0,0 +1,16 @@ +Import maps +=========== + +.. _go_library: /docs/go/core/rules.md#_go_library + +Tests to ensure that importmap is working as expected. + +.. contents:: + +importmap_test +-------------- + +Test that importmap declarations on go_library_ are propagated and obeyed. +This builds libraries using `src.go <src.go>`_ as multiple outputs with the differing importpaths, +adds identical importmap declarations and then checks that the libraries can be correctly imported +without colliding through differing intermediate libraries into `the main test <importmap_test.go>`_. diff --git a/tests/core/importmap/import.go b/tests/core/importmap/import.go new file mode 100644 index 00000000..6d70cbfc --- /dev/null +++ b/tests/core/importmap/import.go @@ -0,0 +1,24 @@ +/* Copyright 2017 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 importmap + +import ( + "lib" +) + +func Get() string { + return lib.Value +} diff --git a/tests/core/importmap/importmap_test.go b/tests/core/importmap/importmap_test.go new file mode 100644 index 00000000..dc1d18b0 --- /dev/null +++ b/tests/core/importmap/importmap_test.go @@ -0,0 +1,31 @@ +/* Copyright 2017 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 importmap_test + +import ( + a "a" + b "b" + "testing" +) + +func TestImportmap(t *testing.T) { + if a.Get() != "ValueA" { + t.Errorf("Got %v from a instead of ValueA", a.Get()) + } + if b.Get() != "ValueB" { + t.Errorf("Got %v from b instead of ValueB", b.Get()) + } +} diff --git a/tests/core/importmap/lib.go b/tests/core/importmap/lib.go new file mode 100644 index 00000000..a4ad9c47 --- /dev/null +++ b/tests/core/importmap/lib.go @@ -0,0 +1,18 @@ +/* Copyright 2017 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 lib + +var Value = "Unstamped" diff --git a/tests/core/nogo/BUILD.bazel b/tests/core/nogo/BUILD.bazel new file mode 100644 index 00000000..46c7ee89 --- /dev/null +++ b/tests/core/nogo/BUILD.bazel @@ -0,0 +1,5 @@ +filegroup( + name = "rules_go_deps", + srcs = [":common.bzl"], + visibility = ["//visibility:public"], +) diff --git a/tests/core/nogo/README.rst b/tests/core/nogo/README.rst new file mode 100644 index 00000000..06b4e12a --- /dev/null +++ b/tests/core/nogo/README.rst @@ -0,0 +1,18 @@ +Core Go rules tests +=================== + +This contains tests of the nogo build-time code analysis tool. + +Contents +-------- + +.. Child list start + +* `Vet check <vet/README.rst>`_ +* `Nogo configuration <config/README.rst>`_ +* `nogo analyzers with dependencies <deps/README.rst>`_ +* `Custom nogo analyzers <custom/README.rst>`_ +* `nogo test with coverage <coverage/README.rst>`_ + +.. Child list end + diff --git a/tests/core/nogo/common.bzl b/tests/core/nogo/common.bzl new file mode 100644 index 00000000..14089ce2 --- /dev/null +++ b/tests/core/nogo/common.bzl @@ -0,0 +1,38 @@ +# Macros used by all nogo integration tests. + +BUILD_FAILED_TMPL = """ +if [[ result -eq 0 ]]; then + echo "TEST FAILED: expected build error" >&2 + result=1 +else + result=0 + {check_err} +fi +""" + +BUILD_PASSED_TMPL = """ +if [[ result -ne 0 ]]; then + echo "TEST FAILED: unexpected build error" >&2 + result=1 +else + {check_err} +fi +""" + +CONTAINS_ERR_TMPL = """ + lines=$(grep '{err}' bazel-output.txt | wc -l) + if [ $lines -eq 0 ]; then + echo "TEST FAILED: expected error message containing: '{err}'" >&2 + result=1 + elif [ $lines -ne 1 ]; then + echo "TEST FAILED: expected error message '{err}' appears more than once" >&2 + result=1 + fi +""" + +DOES_NOT_CONTAIN_ERR_TMPL = """ + if grep -q '{err}' bazel-output.txt; then + echo "TEST FAILED: received error message containing: '{err}'" >&2 + result=1 + fi +""" diff --git a/tests/core/nogo/config/BUILD.bazel b/tests/core/nogo/config/BUILD.bazel new file mode 100644 index 00000000..ce5ad406 --- /dev/null +++ b/tests/core/nogo/config/BUILD.bazel @@ -0,0 +1,6 @@ +load("@io_bazel_rules_go//go/tools/bazel_testing:def.bzl", "go_bazel_test") + +go_bazel_test( + name = "config_test", + srcs = ["config_test.go"], +) diff --git a/tests/core/nogo/config/README.rst b/tests/core/nogo/config/README.rst new file mode 100644 index 00000000..bcd4b730 --- /dev/null +++ b/tests/core/nogo/config/README.rst @@ -0,0 +1,17 @@ +Nogo configuration +================== + +.. _nogo: /go/nogo.rst +.. _go_binary: /docs/go/core/rules.md#_go_binary +.. _#1850: https://github.com/bazelbuild/rules_go/issues/1850 +.. _#2470: https://github.com/bazelbuild/rules_go/issues/2470 + +Tests that verify nogo_ works on targets compiled in non-default configurations. + +.. contents:: + +config_test +----------- + +Verifies that a `go_binary`_ can be built in non-default configurations with +nogo. Verifies `#1850`_, `#2470`_. diff --git a/tests/core/nogo/config/config_test.go b/tests/core/nogo/config/config_test.go new file mode 100644 index 00000000..3758d438 --- /dev/null +++ b/tests/core/nogo/config/config_test.go @@ -0,0 +1,62 @@ +// 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 config_test + +import ( + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Nogo: "@io_bazel_rules_go//:tools_nogo", + Main: ` +-- BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") + +go_binary( + name = "shared_bin", + srcs = ["shared_bin.go"], + linkmode = "c-shared", + deps = [":shared_lib"], +) + +go_library( + name = "shared_lib", + srcs = ["shared_lib.go"], + importpath = "example.com/nogo/config/shared_lib", +) + +-- shared_bin.go -- +package main + +import _ "example.com/nogo/config/shared_lib" + +func main() { +} + +-- shared_lib.go -- +package shared_lib + +`, + }) +} + +func TestShared(t *testing.T) { + if err := bazel_testing.RunBazel("build", "//:shared_bin"); err != nil { + t.Fatal(err) + } +} diff --git a/tests/core/nogo/coverage/BUILD.bazel b/tests/core/nogo/coverage/BUILD.bazel new file mode 100644 index 00000000..b14fcaf4 --- /dev/null +++ b/tests/core/nogo/coverage/BUILD.bazel @@ -0,0 +1,11 @@ +load("@io_bazel_rules_go//go/tools/bazel_testing:def.bzl", "go_bazel_test") + +go_bazel_test( + name = "coverage_test", + srcs = ["coverage_test.go"], +) + +go_bazel_test( + name = "gen_code_test", + srcs = ["gen_code_test.go"], +) diff --git a/tests/core/nogo/coverage/README.rst b/tests/core/nogo/coverage/README.rst new file mode 100644 index 00000000..2bb6de95 --- /dev/null +++ b/tests/core/nogo/coverage/README.rst @@ -0,0 +1,23 @@ +nogo test with coverage +======================= + +.. _nogo: /go/nogo.rst +.. _#1940: https://github.com/bazelbuild/rules_go/issues/1940 +.. _#2146: https://github.com/bazelbuild/rules_go/issues/2146 + +Tests to ensure that `nogo`_ works with coverage. + +coverage_test +------------- +Checks that `nogo`_ works when coverage is enabled. All covered libraries gain +an implicit dependencies on ``//go/tools/coverdata``, which is a +`go_tool_library`_, which isn't built with `nogo`_. We should be able to +handle libraries like this that do not have serialized facts. Verifies `#1940`_. + +Also checks that `nogo`_ itself can be built with coverage enabled. +Verifies `#2146`_. + +gen_code_test +------------- +Checks how `nogo`_ should not run on source code that was generated as part of +rules_go's coverage implementation. diff --git a/tests/core/nogo/coverage/coverage_test.go b/tests/core/nogo/coverage/coverage_test.go new file mode 100644 index 00000000..97dc63dc --- /dev/null +++ b/tests/core/nogo/coverage/coverage_test.go @@ -0,0 +1,65 @@ +// 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 coverage_test + +import ( + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Main: ` +-- BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_test", "go_tool_library", "nogo") + +go_test( + name = "coverage_target", + srcs = ["coverage_target_test.go"], + deps = [":coverage_target_dep"], +) + +go_tool_library( + name = "coverage_target_dep", + importmap = "mapped/coverage_target/dep", + importpath = "coverage_target/dep", +) + +nogo( + name = "nogo", + vet = True, + visibility = ["//visibility:public"], +) +-- coverage_target_test.go -- +package coverage_target_test +`, + Nogo: `@//:nogo`, + }) +} + +func TestCoverageWithNogo(t *testing.T) { + if out, err := bazel_testing.BazelOutput("coverage", "//:coverage_target"); err != nil { + println(string(out)) + t.Fatal(err) + } +} + +func TestCoverageOfNogo(t *testing.T) { + if out, err := bazel_testing.BazelOutput("build", "--instrumentation_filter=.*", "--collect_code_coverage", "//:nogo"); err != nil { + println(string(out)) + t.Fatal(err) + } +} diff --git a/tests/core/nogo/coverage/gen_code_test.go b/tests/core/nogo/coverage/gen_code_test.go new file mode 100644 index 00000000..6f2c55b6 --- /dev/null +++ b/tests/core/nogo/coverage/gen_code_test.go @@ -0,0 +1,130 @@ +// 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 gen_code_test + +import ( + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Main: ` +-- BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test", "nogo") + +go_library( + name = "simple", + srcs = ["simple.go"], + importpath = "simple" +) + +go_test( + name = "simple_test", + srcs = ["simple_test.go"], + embed = [":simple"] +) + +nogo( + name = "nogo", + deps = ["//nocover"], + visibility = ["//visibility:public"], +) +-- simple.go -- +package simple + +func Foo() string { + return "foo" +} +-- simple_test.go -- +package simple + +import "testing" + +func TestFoo(t *testing.T) { + if actual, expected := Foo(), "foo"; actual != expected { + t.Errorf("Foo() should return foo") + } +} +-- nocover/BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "nocover", + srcs = ["analyzer.go"], + importpath = "nocover", + visibility = ["//visibility:public"], + deps = [ + "@org_golang_x_tools//go/analysis", + "@org_golang_x_tools//go/analysis/passes/inspect", + "@org_golang_x_tools//go/ast/inspector", + ], +) +-- nocover/analyzer.go -- +package nocover + +import ( + "fmt" + "go/ast" + "strings" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/ast/inspector" +) + +var Analyzer = &analysis.Analyzer{ + Name: "nocover", + Doc: "nocover ensure that source code was not a generated file created by rules_go's coverage implementation", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, +} + +func run(pass *analysis.Pass) (interface{}, error) { + inspector := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) + + inspector.Preorder([]ast.Node{(*ast.ValueSpec)(nil)}, func(node ast.Node) { + valueSpec := node.(*ast.ValueSpec) + if len(valueSpec.Names) != 1 { + return + } + + varName := valueSpec.Names[0].Name + + // check for coverage variable name that matches this pattern: CoverZ%sZ%dZ%s + // see go/tools/builders/compilepkg.go -> coverVar for more information + if strings.HasPrefix(varName, "CoverZ") && strings.Count(varName, "Z") >= 3 { + pass.Report(analysis.Diagnostic{ + Pos: valueSpec.Pos(), + End: valueSpec.End(), + Message: fmt.Sprintf("variable %s was generated by rules_go", varName), + }) + } + }) + + return nil, nil +} +`, + Nogo: `@//:nogo`, + }) +} + +func TestNogoCoverGenCode(t *testing.T) { + if out, err := bazel_testing.BazelOutput("coverage", "//:simple_test"); err != nil { + println(string(out)) + t.Fatal(err) + } +} diff --git a/tests/core/nogo/custom/BUILD.bazel b/tests/core/nogo/custom/BUILD.bazel new file mode 100644 index 00000000..06317a0b --- /dev/null +++ b/tests/core/nogo/custom/BUILD.bazel @@ -0,0 +1,6 @@ +load("@io_bazel_rules_go//go/tools/bazel_testing:def.bzl", "go_bazel_test") + +go_bazel_test( + name = "custom_test", + srcs = ["custom_test.go"], +) diff --git a/tests/core/nogo/custom/README.rst b/tests/core/nogo/custom/README.rst new file mode 100644 index 00000000..285c8ff6 --- /dev/null +++ b/tests/core/nogo/custom/README.rst @@ -0,0 +1,16 @@ +Custom nogo analyzers +===================== + +.. _nogo: /go/nogo.rst +.. _go_library: /docs/go/core/rules.md#_go_library + +Tests to ensure that custom `nogo`_ analyzers run and detect errors. + +.. contents:: + +custom_test +----------- +Verifies that custom analyzers print errors and fail a `go_library`_ build when +a configuration file is not provided, and that analyzers with the same package +name do not conflict. Also checks that custom analyzers can be configured to +apply only to certain file paths using a custom configuration file. diff --git a/tests/core/nogo/custom/custom_test.go b/tests/core/nogo/custom/custom_test.go new file mode 100644 index 00000000..27624431 --- /dev/null +++ b/tests/core/nogo/custom/custom_test.go @@ -0,0 +1,510 @@ +// 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 custom_test + +import ( + "bytes" + "fmt" + "io/ioutil" + "regexp" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +const origConfig = `# config = "",` + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Nogo: "@//:nogo", + Main: ` +-- BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_library", "nogo") + +nogo( + name = "nogo", + deps = [ + ":foofuncname", + ":importfmt", + ":visibility", + ], + # config = "", + visibility = ["//visibility:public"], +) + +go_library( + name = "importfmt", + srcs = ["importfmt.go"], + importpath = "importfmtanalyzer", + deps = ["@org_golang_x_tools//go/analysis"], + visibility = ["//visibility:public"], +) + +go_library( + name = "foofuncname", + srcs = ["foofuncname.go"], + importpath = "foofuncanalyzer", + deps = ["@org_golang_x_tools//go/analysis"], + visibility = ["//visibility:public"], +) + +go_library( + name = "visibility", + srcs = ["visibility.go"], + importpath = "visibilityanalyzer", + deps = [ + "@org_golang_x_tools//go/analysis", + "@org_golang_x_tools//go/ast/inspector", + ], + visibility = ["//visibility:public"], +) + +go_library( + name = "has_errors", + srcs = ["has_errors.go"], + importpath = "haserrors", + deps = [":dep"], +) + +go_library( + name = "has_errors_linedirective", + srcs = ["has_errors_linedirective.go"], + importpath = "haserrors_linedirective", + deps = [":dep"], +) + +go_library( + name = "uses_cgo_with_errors", + srcs = [ + "examplepkg/uses_cgo_clean.go", + "examplepkg/pure_src_with_err_calling_native.go", + ], + importpath = "examplepkg", + cgo = True, +) + +go_library( + name = "no_errors", + srcs = ["no_errors.go"], + importpath = "noerrors", + deps = [":dep"], +) + +go_library( + name = "dep", + srcs = ["dep.go"], + importpath = "dep", +) + +-- foofuncname.go -- +// importfmt checks for functions named "Foo". +// It has the same package name as another check to test the checks with +// the same package name do not conflict. +package importfmt + +import ( + "go/ast" + + "golang.org/x/tools/go/analysis" +) + +const doc = "report calls of functions named \"Foo\"\n\nThe foofuncname analyzer reports calls to functions that are\nnamed \"Foo\"." + +var Analyzer = &analysis.Analyzer{ + Name: "foofuncname", + Run: run, + Doc: doc, +} + +func run(pass *analysis.Pass) (interface{}, error) { + for _, f := range pass.Files { + // TODO(samueltan): use package inspector once the latest golang.org/x/tools + // changes are pulled into this branch (see #1755). + ast.Inspect(f, func(n ast.Node) bool { + switch n := n.(type) { + case *ast.FuncDecl: + if n.Name.Name == "Foo" { + pass.Reportf(n.Pos(), "function must not be named Foo") + } + return true + } + return true + }) + } + return nil, nil +} + +-- importfmt.go -- +// importfmt checks for the import of package fmt. +package importfmt + +import ( + "go/ast" + "strconv" + + "golang.org/x/tools/go/analysis" +) + +const doc = "report imports of package fmt\n\nThe importfmt analyzer reports imports of package fmt." + +var Analyzer = &analysis.Analyzer{ + Name: "importfmt", + Run: run, + Doc: doc, +} + +func run(pass *analysis.Pass) (interface{}, error) { + for _, f := range pass.Files { + // TODO(samueltan): use package inspector once the latest golang.org/x/tools + // changes are pulled into this branch (see #1755). + ast.Inspect(f, func(n ast.Node) bool { + switch n := n.(type) { + case *ast.ImportSpec: + if path, _ := strconv.Unquote(n.Path.Value); path == "fmt" { + pass.Reportf(n.Pos(), "package fmt must not be imported") + } + return true + } + return true + }) + } + return nil, nil +} + +-- visibility.go -- +// visibility looks for visibility annotations on functions and +// checks they are only called from packages allowed to call them. +package visibility + +import ( + "encoding/gob" + "go/ast" + "regexp" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/ast/inspector" +) + +var Analyzer = &analysis.Analyzer{ + Name: "visibility", + Run: run, + Doc: "enforce visibility requirements for functions\n\nThe visibility analyzer reads visibility annotations on functions and\nchecks that packages that call those functions are allowed to do so.", + FactTypes: []analysis.Fact{(*VisibilityFact)(nil)}, +} + +type VisibilityFact struct { + Paths []string +} + +func (_ *VisibilityFact) AFact() {} // dummy method to satisfy interface + +func init() { gob.Register((*VisibilityFact)(nil)) } + +var visibilityRegexp = regexp.MustCompile("visibility:([^\\s]+)") + +func run(pass *analysis.Pass) (interface{}, error) { + in := inspector.New(pass.Files) + + // Find visibility annotations on function declarations. + in.Nodes([]ast.Node{(*ast.FuncDecl)(nil)}, func(n ast.Node, push bool) (prune bool) { + if !push { + return false + } + + fn := n.(*ast.FuncDecl) + + if fn.Doc == nil { + return true + } + obj := pass.TypesInfo.ObjectOf(fn.Name) + if obj == nil { + return true + } + doc := fn.Doc.Text() + + if matches := visibilityRegexp.FindAllStringSubmatch(doc, -1); matches != nil { + fact := &VisibilityFact{Paths: make([]string, len(matches))} + for i, m := range matches { + fact.Paths[i] = m[1] + } + pass.ExportObjectFact(obj, fact) + } + + return true + }) + + // Find calls that may be affected by visibility declarations. + in.Nodes([]ast.Node{(*ast.CallExpr)(nil)}, func(n ast.Node, push bool) (prune bool) { + if !push { + return false + } + + callee, ok := n.(*ast.CallExpr).Fun.(*ast.SelectorExpr) + if !ok { + return false + } + obj := pass.TypesInfo.ObjectOf(callee.Sel) + if obj == nil { + return false + } + var fact VisibilityFact + if ok := pass.ImportObjectFact(obj, &fact); !ok { + return false + } + visible := false + for _, path := range fact.Paths { + if path == pass.Pkg.Path() { + visible = true + break + } + } + if !visible { + pass.Reportf(callee.Pos(), "function %s is not visible in this package", callee.Sel.Name) + } + + return false + }) + + return nil, nil +} + +-- config.json -- +{ + "importfmt": { + "only_files": { + "has_errors\\.go": "" + } + }, + "foofuncname": { + "description": "no exemptions since we know this check is 100% accurate" + }, + "visibility": { + "exclude_files": { + "has_.*\\.go": "special exception to visibility rules" + } + } +} + +-- baseconfig.json -- +{ + "_base": { + "exclude_files": { + "has_.*\\.go": "Visibility analyzer not specified. Still inherits this special exception." + } + }, + "importfmt": { + "only_files": { + "has_errors\\.go": "" + } + }, + "foofuncname": { + "description": "no exemptions since we know this check is 100% accurate, so override base config", + "exclude_files": {} + } +} + +-- has_errors.go -- +package haserrors + +import ( + _ "fmt" // This should fail importfmt + + "dep" +) + +func Foo() bool { // This should fail foofuncname + dep.D() // This should fail visibility + return true +} + +-- has_errors_linedirective.go -- +//line linedirective.go:1 +package haserrors_linedirective + +import ( + /*line linedirective_importfmt.go:4*/ _ "fmt" // This should fail importfmt + + "dep" +) + +//line linedirective_foofuncname.go:9 +func Foo() bool { // This should fail foofuncname +//line linedirective_visibility.go:10 + dep.D() // This should fail visibility + return true +} + +-- no_errors.go -- +// package noerrors contains no analyzer errors. +package noerrors + +import "dep" + +func Baz() int { + dep.D() + return 1 +} + +-- dep.go -- +package dep + +// visibility:noerrors +func D() { +} + +-- examplepkg/uses_cgo_clean.go -- +package examplepkg + +// #include <stdlib.h> +import "C" + +func Bar() bool { + if C.rand() > 10 { + return true + } + return false +} + +-- examplepkg/pure_src_with_err_calling_native.go -- +package examplepkg + +func Foo() bool { // This should fail foofuncname + return Bar() +} + +`, + }) +} + +func Test(t *testing.T) { + for _, test := range []struct { + desc, config, target string + wantSuccess bool + includes, excludes []string + }{ + { + desc: "default_config", + target: "//:has_errors", + wantSuccess: false, + includes: []string{ + `has_errors.go:.*package fmt must not be imported \(importfmt\)`, + `has_errors.go:.*function must not be named Foo \(foofuncname\)`, + `has_errors.go:.*function D is not visible in this package \(visibility\)`, + }, + }, { + desc: "default_config_linedirective", + target: "//:has_errors_linedirective", + wantSuccess: false, + includes: []string{ + `linedirective_importfmt.go:.*package fmt must not be imported \(importfmt\)`, + `linedirective_foofuncname.go:.*function must not be named Foo \(foofuncname\)`, + `linedirective_visibility.go:.*function D is not visible in this package \(visibility\)`, + }, + }, { + desc: "custom_config", + config: "config.json", + target: "//:has_errors", + wantSuccess: false, + includes: []string{ + `has_errors.go:.*package fmt must not be imported \(importfmt\)`, + `has_errors.go:.*function must not be named Foo \(foofuncname\)`, + }, + excludes: []string{ + `visib`, + }, + }, { + desc: "custom_config_linedirective", + config: "config.json", + target: "//:has_errors_linedirective", + wantSuccess: false, + includes: []string{ + `linedirective_foofuncname.go:.*function must not be named Foo \(foofuncname\)`, + `linedirective_visibility.go:.*function D is not visible in this package \(visibility\)`, + }, + excludes: []string{ + `importfmt`, + }, + }, { + desc: "custom_config_with_base_linedirective", + config: "baseconfig.json", + target: "//:has_errors_linedirective", + wantSuccess: false, + includes: []string{ + `linedirective_foofuncname.go:.*function must not be named Foo \(foofuncname\)`, + `linedirective_visibility.go:.*function D is not visible in this package \(visibility\)`, + }, + excludes: []string{ + `importfmt`, + }, + }, { + desc: "uses_cgo_with_errors", + config: "config.json", + target: "//:uses_cgo_with_errors", + wantSuccess: false, + includes: []string{ + // note the cross platform regex :) + `.*[\\/]cgo[\\/]examplepkg[\\/]pure_src_with_err_calling_native.go:.*function must not be named Foo \(foofuncname\)`, + }, + }, { + desc: "no_errors", + target: "//:no_errors", + wantSuccess: true, + excludes: []string{"no_errors.go"}, + }, + } { + t.Run(test.desc, func(t *testing.T) { + if test.config != "" { + customConfig := fmt.Sprintf("config = %q,", test.config) + if err := replaceInFile("BUILD.bazel", origConfig, customConfig); err != nil { + t.Fatal(err) + } + defer replaceInFile("BUILD.bazel", customConfig, origConfig) + } + + cmd := bazel_testing.BazelCmd("build", test.target) + stderr := &bytes.Buffer{} + cmd.Stderr = stderr + if err := cmd.Run(); err == nil && !test.wantSuccess { + t.Fatal("unexpected success") + } else if err != nil && test.wantSuccess { + t.Fatalf("unexpected error: %v", err) + } + + for _, pattern := range test.includes { + if matched, err := regexp.Match(pattern, stderr.Bytes()); err != nil { + t.Fatal(err) + } else if !matched { + t.Errorf("got output:\n %s\n which does not contain pattern: %s", string(stderr.Bytes()), pattern) + } + } + for _, pattern := range test.excludes { + if matched, err := regexp.Match(pattern, stderr.Bytes()); err != nil { + t.Fatal(err) + } else if matched { + t.Errorf("output contained pattern: %s", pattern) + } + } + }) + } +} + +func replaceInFile(path, old, new string) error { + data, err := ioutil.ReadFile(path) + if err != nil { + return err + } + data = bytes.ReplaceAll(data, []byte(old), []byte(new)) + return ioutil.WriteFile(path, data, 0666) +} diff --git a/tests/core/nogo/custom/flags/BUILD.bazel b/tests/core/nogo/custom/flags/BUILD.bazel new file mode 100644 index 00000000..cdf4c76a --- /dev/null +++ b/tests/core/nogo/custom/flags/BUILD.bazel @@ -0,0 +1,6 @@ +load("@io_bazel_rules_go//go/tools/bazel_testing:def.bzl", "go_bazel_test") + +go_bazel_test( + name = "flags_test", + srcs = ["flags_test.go"], +) diff --git a/tests/core/nogo/custom/flags/README.rst b/tests/core/nogo/custom/flags/README.rst new file mode 100644 index 00000000..e525250b --- /dev/null +++ b/tests/core/nogo/custom/flags/README.rst @@ -0,0 +1,19 @@ +Custom nogo analyzer flags +===================== + +.. _nogo: /go/nogo.rst +.. _go_library: /docs/go/core/rules.md#_go_library + +Tests to ensure that custom `nogo`_ analyzers that consume flags can be +supplied those flags via nono config. + +.. contents:: + +flags_test +----------- +Verifies that a simple custom analyzer's behavior can be modified by setting +its analyzer flags in the nogo driver, and that these flags can be provided to +the driver via the nogo config `analyzer_flags` field. Also checks that +invalid flags as defined by the `flag` package cause the driver to immediately +return an error. + diff --git a/tests/core/nogo/custom/flags/flags_test.go b/tests/core/nogo/custom/flags/flags_test.go new file mode 100644 index 00000000..7381a3f0 --- /dev/null +++ b/tests/core/nogo/custom/flags/flags_test.go @@ -0,0 +1,262 @@ +// 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 flags_test + +import ( + "bytes" + "fmt" + "io/ioutil" + "regexp" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +const origConfig = `# config = "",` + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Nogo: "@//:nogo", + Main: ` +-- BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_library", "nogo") + +nogo( + name = "nogo", + deps = [ + ":flagger", + ], + # config = "", + visibility = ["//visibility:public"], +) + +go_library( + name = "flagger", + srcs = ["flagger.go"], + importpath = "flaggeranalyzer", + deps = [ + "@org_golang_x_tools//go/analysis", + ], + visibility = ["//visibility:public"], +) + +go_library( + name = "some_file", + srcs = ["some_file.go"], + importpath = "somefile", + deps = [":dep"], +) + +go_library( + name = "dep", + srcs = ["dep.go"], + importpath = "dep", +) + +-- flagger.go -- +// flagger crashes when three flags are set in the config or else it no-ops +package flagger + +import ( + "errors" + + "golang.org/x/tools/go/analysis" +) + +var ( + boolSwitch bool + stringSwitch string + intSwitch int +) + +var Analyzer = &analysis.Analyzer{ + Name: "flagger", + Run: run, + Doc: "Dummy analyzer that crashes when all its flags are set correctly", +} + +func init() { + Analyzer.Flags.BoolVar(&boolSwitch, "bool-switch", false, "Bool must be set to true to run") + Analyzer.Flags.StringVar(&stringSwitch, "string-switch", "no", "String must be set to \"yes\" to run") + Analyzer.Flags.IntVar(&intSwitch, "int-switch", 0, "Int must be set to 1 to run") +} + +func run(pass *analysis.Pass) (interface{}, error) { + if !boolSwitch { + return nil, nil + } + if stringSwitch != "yes" { + return nil, nil + } + if intSwitch != 1 { + return nil, nil + } + return nil, errors.New("all switches were set -> fail") +} + +-- all_flags_set.json -- +{ + "flagger": { + "description": "this will crash on every file", + "analyzer_flags": { + "bool-switch": "true", + "int-switch": "1", + "string-switch": "yes" + } + } +} + +-- two_flags_set.json -- +{ + "flagger": { + "description": "this will succeed on every file", + "analyzer_flags": { + "bool-switch": "true", + "int-switch": "1" + } + } +} + +-- invalid_int.json -- +{ + "flagger": { + "description": "this will crash immediately due to an invalid int flag", + "analyzer_flags": { + "int-switch": "one", + "string-switch": "yes" + } + } +} + +-- nonexistent_flag.json -- +{ + "flagger": { + "description": "this will crash immediately due to a nonexistent flag", + "analyzer_flags": { + "int-switch": "1", + "bool-switch": "true", + "string-switch": "yes", + "description": "This is a good analyzer" + } + } +} + +-- hyphenated_flag.json -- +{ + "flagger": { + "description": "this will crash immediately due to a hyphenated flag", + "analyzer_flags": { + "-int-switch": "1" + } + } +} + +-- some_file.go -- +// package somefile contains a file and has a dep +package somefile + +import "dep" + +func Baz() int { + dep.D() + return 1 +} + +-- dep.go -- +package dep + +func D() { +} + +`, + }) +} + +func Test(t *testing.T) { + for _, test := range []struct { + desc, config string + wantSuccess bool + includes, excludes []string + }{ + { + desc: "config_flags_triggering_error", + wantSuccess: false, + config: "all_flags_set.json", + includes: []string{"all switches were set -> fail"}, + }, { + desc: "config_flags_triggering_success", + wantSuccess: true, + config: "two_flags_set.json", + }, { + desc: "invalid_int_triggering_error", + wantSuccess: false, + config: "invalid_int.json", + includes: []string{"flagger: invalid value for flag: int-switch=one"}, + }, { + desc: "nonexistent_flag_triggering_error", + wantSuccess: false, + config: "nonexistent_flag.json", + includes: []string{"flagger: unrecognized flag: description"}, + }, { + desc: "hyphenated_flag_triggering_error", + wantSuccess: false, + config: "hyphenated_flag.json", + includes: []string{"flagger: flag should not begin with '-': -int-switch"}, + }, + } { + t.Run(test.desc, func(t *testing.T) { + if test.config != "" { + customConfig := fmt.Sprintf("config = %q,", test.config) + if err := replaceInFile("BUILD.bazel", origConfig, customConfig); err != nil { + t.Fatal(err) + } + defer replaceInFile("BUILD.bazel", customConfig, origConfig) + } + + cmd := bazel_testing.BazelCmd("build", "//:some_file") + stderr := &bytes.Buffer{} + cmd.Stderr = stderr + if err := cmd.Run(); err == nil && !test.wantSuccess { + t.Fatal("unexpected success") + } else if err != nil && test.wantSuccess { + t.Fatalf("unexpected error: %v", err) + } + + for _, pattern := range test.includes { + if matched, err := regexp.Match(pattern, stderr.Bytes()); err != nil { + t.Fatal(err) + } else if !matched { + t.Errorf("got output:\n %s\n which does not contain pattern: %s", string(stderr.Bytes()), pattern) + } + } + for _, pattern := range test.excludes { + if matched, err := regexp.Match(pattern, stderr.Bytes()); err != nil { + t.Fatal(err) + } else if matched { + t.Errorf("output contained pattern: %s", pattern) + } + } + }) + } +} + +func replaceInFile(path, old, new string) error { + data, err := ioutil.ReadFile(path) + if err != nil { + return err + } + data = bytes.ReplaceAll(data, []byte(old), []byte(new)) + return ioutil.WriteFile(path, data, 0666) +} diff --git a/tests/core/nogo/deps/BUILD.bazel b/tests/core/nogo/deps/BUILD.bazel new file mode 100644 index 00000000..a1d998fd --- /dev/null +++ b/tests/core/nogo/deps/BUILD.bazel @@ -0,0 +1,6 @@ +load("@io_bazel_rules_go//go/tools/bazel_testing:def.bzl", "go_bazel_test") + +go_bazel_test( + name = "deps_test", + srcs = ["deps_test.go"], +) diff --git a/tests/core/nogo/deps/README.rst b/tests/core/nogo/deps/README.rst new file mode 100644 index 00000000..93affd23 --- /dev/null +++ b/tests/core/nogo/deps/README.rst @@ -0,0 +1,26 @@ +nogo analyzers with dependencies +============================= + +.. _nogo: /go/nogo.rst +.. _go_library: /docs/go/core/rules.md#_go_library + +Tests to ensure that custom `nogo`_ analyzers that depend on each other are +run in the correct order. + +.. contents:: + +deps_test +--------- +Given the following dependency graph of analyzers: + + a ----+ + | + v + b --> c --> d + +Where analyzers a, b, c are explicitly depended on by the `nogo`_ rule and d +isn't, verifies that a `go_library`_ build causes both paths in the graph +(a->c->d and b->c->d) to be executed, and that each analyzer runs exactly once. + +Also verify that the diagnostics reported by d are not printed to the build log +since d was not explicitly depended on by the declared `nogo`_ rule. diff --git a/tests/core/nogo/deps/deps_test.go b/tests/core/nogo/deps/deps_test.go new file mode 100644 index 00000000..f527effd --- /dev/null +++ b/tests/core/nogo/deps/deps_test.go @@ -0,0 +1,211 @@ +// 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 deps_test + +import ( + "bytes" + "regexp" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Nogo: "@//:nogo", + Main: ` +-- BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_library", "nogo") + +nogo( + name = "nogo", + deps = [ + ":a", + ":b", + ":c", + ], + visibility = ["//visibility:public"], +) + +go_library( + name = "a", + srcs = ["a.go"], + importpath = "a", + deps = [ + ":c", + "@org_golang_x_tools//go/analysis" + ], + visibility = ["//visibility:public"], +) + +go_library( + name = "b", + srcs = ["b.go"], + importpath = "b", + deps = [ + ":c", + "@org_golang_x_tools//go/analysis" + ], + visibility = ["//visibility:public"], +) + +go_library( + name = "c", + srcs = ["c.go"], + importpath = "c", + deps = [ + ":d", + "@org_golang_x_tools//go/analysis" + ], + visibility = ["//visibility:public"], +) + +go_library( + name = "d", + srcs = ["d.go"], + importpath = "d", + deps = ["@org_golang_x_tools//go/analysis"], + visibility = ["//visibility:public"], +) + +go_library( + name = "src", + srcs = ["src.go"], + importpath = "src", +) + +-- a.go -- +package a + +import ( + "c" + "go/token" + + "golang.org/x/tools/go/analysis" +) + +var Analyzer = &analysis.Analyzer{ + Name: "a", + Doc: "an analyzer that depends on c.Analyzer", + Run: run, + Requires: []*analysis.Analyzer{c.Analyzer}, +} + +func run(pass *analysis.Pass) (interface{}, error) { + pass.Reportf(token.NoPos, "a %s", pass.ResultOf[c.Analyzer]) + return nil, nil +} + +-- b.go -- +package b + +import ( + "c" + "go/token" + + "golang.org/x/tools/go/analysis" +) + +var Analyzer = &analysis.Analyzer{ + Name: "b", + Doc: "an analyzer that depends on c.Analyzer", + Run: run, + Requires: []*analysis.Analyzer{c.Analyzer}, +} + +func run(pass *analysis.Pass) (interface{}, error) { + pass.Reportf(token.NoPos, "b %s", pass.ResultOf[c.Analyzer]) + return nil, nil +} + +-- c.go -- +package c + +import ( + "d" + "fmt" + "go/token" + "reflect" + + "golang.org/x/tools/go/analysis" +) + +var Analyzer = &analysis.Analyzer{ + Name: "c", + Doc: "an analyzer that depends on d.Analyzer", + Run: run, + Requires: []*analysis.Analyzer{d.Analyzer}, + ResultType: reflect.TypeOf(""), +} + +func run(pass *analysis.Pass) (interface{}, error) { + pass.Reportf(token.NoPos, "only printed once") + return fmt.Sprintf("c %s", pass.ResultOf[d.Analyzer]), nil +} + +-- d.go -- +package d + +import ( + "go/token" + "reflect" + + "golang.org/x/tools/go/analysis" +) + +var Analyzer = &analysis.Analyzer{ + Name: "d", + Doc: "an analyzer that does not depend on other analyzers", + Run: run, + ResultType: reflect.TypeOf(""), +} + +func run(pass *analysis.Pass) (interface{}, error) { + pass.Reportf(token.NoPos, "this should not be printed") + return "d", nil +} + +-- src.go -- +package src + +func Foo() int { + return 1 +} + +`, + }) +} + +func Test(t *testing.T) { + cmd := bazel_testing.BazelCmd("build", "//:src") + stderr := &bytes.Buffer{} + cmd.Stderr = stderr + if err := cmd.Run(); err == nil { + t.Fatal("unexpected success") + } + + for _, pattern := range []string{ + "a c d", + "b c d", + "only printed once", + } { + if matched, _ := regexp.Match(pattern, stderr.Bytes()); !matched { + t.Errorf("output does not contain pattern: %s", pattern) + } + } + if bytes.Contains(stderr.Bytes(), []byte("this should not be printed")) { + t.Errorf("%q was printed", "this should not be printed") + } +} diff --git a/tests/core/nogo/generate/BUILD.bazel b/tests/core/nogo/generate/BUILD.bazel new file mode 100644 index 00000000..52567b61 --- /dev/null +++ b/tests/core/nogo/generate/BUILD.bazel @@ -0,0 +1,6 @@ +load("@io_bazel_rules_go//go/tools/bazel_testing:def.bzl", "go_bazel_test") + +go_bazel_test( + name = "empty_test", + srcs = ["empty_test.go"], +) diff --git a/tests/core/nogo/generate/README.rst b/tests/core/nogo/generate/README.rst new file mode 100644 index 00000000..d9604fab --- /dev/null +++ b/tests/core/nogo/generate/README.rst @@ -0,0 +1,12 @@ +nogo test with generated code +======================= + +.. _nogo: /go/nogo.rst + +Tests to ensure `nogo`_ interaction with generated code. + +empty_test +------------- +Checks that `nogo`_ is not running over the `_empty.go` file that was +generated as part of GoCompilePkg. + diff --git a/tests/core/nogo/generate/empty_test.go b/tests/core/nogo/generate/empty_test.go new file mode 100644 index 00000000..a4f8de12 --- /dev/null +++ b/tests/core/nogo/generate/empty_test.go @@ -0,0 +1,102 @@ +// 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 empty_test + +import ( + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Main: ` +-- BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test", "nogo") + +go_test( + name = "simple_test", + size = "small", + srcs = ["simple_test.go"], +) + +nogo( + name = "nogo", + deps = ["//noempty"], + visibility = ["//visibility:public"], +) +-- simple_test.go -- +package simple + +import ( + "testing" +) + +func TestFoo(t *testing.T) { +} +-- noempty/BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "noempty", + srcs = ["analyzer.go"], + importpath = "noempty", + visibility = ["//visibility:public"], + deps = [ + "@org_golang_x_tools//go/analysis", + ], +) +-- noempty/analyzer.go -- +package noempty + +import ( + "fmt" + "path/filepath" + "strings" + + "golang.org/x/tools/go/analysis" +) + +var Analyzer = &analysis.Analyzer{ + Name: "noempty", + Doc: "noempty ensure that source code was not a generated file created by rules_go test rewrite", + Run: run, +} + +func run(pass *analysis.Pass) (interface{}, error) { + for _, f := range pass.Files { + pos := pass.Fset.PositionFor(f.Pos(), false) + + if strings.HasSuffix(pos.Filename, filepath.Join(".", "_empty.go")) { + pass.Report(analysis.Diagnostic{ + Pos: 0, + Message: fmt.Sprintf("Detected generated source code from rules_go: %s", pos.Filename), + }) + } + } + + return nil, nil +} +`, + Nogo: `@//:nogo`, + }) +} + +func TestNogoGenEmptyCode(t *testing.T) { + if out, err := bazel_testing.BazelOutput("build", "-k", "//:simple_test"); err != nil { + println(string(out)) + t.Fatal(err) + } +} diff --git a/tests/core/nogo/generics/BUILD.bazel b/tests/core/nogo/generics/BUILD.bazel new file mode 100644 index 00000000..a40df345 --- /dev/null +++ b/tests/core/nogo/generics/BUILD.bazel @@ -0,0 +1,6 @@ +load("@io_bazel_rules_go//go/tools/bazel_testing:def.bzl", "go_bazel_test") + +go_bazel_test( + name = "generics_test", + srcs = ["generics_test.go"], +) diff --git a/tests/core/nogo/generics/README.rst b/tests/core/nogo/generics/README.rst new file mode 100644 index 00000000..c348a992 --- /dev/null +++ b/tests/core/nogo/generics/README.rst @@ -0,0 +1,18 @@ +nogo analyzers run against code using generics +============================================== + +.. _nogo: /go/nogo.rst +.. _buildssa: https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/buildssa +.. _nilness: https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/nilness + +Tests to ensure that `nogo`_ analyzers that run on code using generics get correct +type instantiation information. + +.. contents:: + +generics_test +------------- + +Verifies that code using generic types gets loaded including all type instantiation +information, so that analyzers based on the `buildssa`_ analyzer (such as `nilness`_) get +a complete picture of all types in the code. diff --git a/tests/core/nogo/generics/generics_test.go b/tests/core/nogo/generics/generics_test.go new file mode 100644 index 00000000..562d52ed --- /dev/null +++ b/tests/core/nogo/generics/generics_test.go @@ -0,0 +1,85 @@ +// Copyright 2022 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 generics_test + +import ( + "bytes" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Nogo: "@//:nogo", + Main: ` +-- BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_library", "nogo") + +nogo( + name = "nogo", + visibility = ["//visibility:public"], + deps = ["@org_golang_x_tools//go/analysis/passes/buildssa"], +) + +go_library( + name = "src", + srcs = ["src.go"], + importpath = "src", +) + +-- src.go -- +package src + +type Set[T comparable] struct { + m map[T]struct{} +} + +func New[T comparable](s ...T) *Set[T] { + set := &Set[T]{} + set.Add(s...) + return set +} + +func (set *Set[T]) Add(s ...T) { + if set.m == nil { + set.m = make(map[T]struct{}) + } + for _, s := range s { + set.m[s] = struct{}{} + } +} + +func S(x ...string) *Set[string] { + return New[string](x...) +} +`, + }) +} + +func Test(t *testing.T) { + cmd := bazel_testing.BazelCmd("build", "//:src") + var stderr bytes.Buffer + cmd.Stderr = &stderr + + if err := cmd.Run(); err != nil { + t.Log("output:", stderr.String()) + t.Fatal("unexpected error:", err) + } + + if bytes.Contains(stderr.Bytes(), []byte("panic")) { + t.Errorf("found panic in Bazel output: \n%s", stderr.String()) + } +} diff --git a/tests/core/nogo/nolint/BUILD.bazel b/tests/core/nogo/nolint/BUILD.bazel new file mode 100644 index 00000000..c828045c --- /dev/null +++ b/tests/core/nogo/nolint/BUILD.bazel @@ -0,0 +1,6 @@ +load("@io_bazel_rules_go//go/tools/bazel_testing:def.bzl", "go_bazel_test") + +go_bazel_test( + name = "nolint_test", + srcs = ["nolint_test.go"], +) diff --git a/tests/core/nogo/nolint/README.rst b/tests/core/nogo/nolint/README.rst new file mode 100644 index 00000000..83436898 --- /dev/null +++ b/tests/core/nogo/nolint/README.rst @@ -0,0 +1,14 @@ +Nolint check +========= + +.. _go_library: /docs/go/core/rules.md#_go_library + +Tests to ensure that errors found by nogo and annotated with //nolint are +ignored. + +.. contents:: + +nolint_test +-------- +Verified that errors emitted by ``nogo`` are ignored when `//nolint` appears as +a comment. diff --git a/tests/core/nogo/nolint/nolint_test.go b/tests/core/nogo/nolint/nolint_test.go new file mode 100644 index 00000000..78ecbbcf --- /dev/null +++ b/tests/core/nogo/nolint/nolint_test.go @@ -0,0 +1,227 @@ +// 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 nolint_test + +import ( + "bytes" + "io/ioutil" + "strings" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Main: ` +-- BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_tool_library", "nogo") + +nogo( + name = "nogo", + vet = True, + deps = ["@org_golang_x_tools//go/analysis/passes/nilness"], + visibility = ["//visibility:public"], +) + +go_library( + name = "inline", + srcs = ["inline.go"], + importpath = "test", +) + +go_library( + name = "inline_filter", + srcs = ["inline_filter.go"], + importpath = "test", +) + +go_library( + name = "block", + srcs = ["block.go"], + importpath = "test", +) + +go_library( + name = "block_multiline", + srcs = ["block_multiline.go"], + importpath = "test", +) + +go_library( + name = "inline_errors", + srcs = ["inline_errors.go"], + importpath = "test", +) + +go_library( + name = "inline_column", + srcs = ["inline_column.go"], + importpath = "test", +) + +go_library( + name = "large_block", + srcs = ["large_block.go"], + importpath = "test", +) +-- inline.go -- +package test + +import "fmt" + +func F() { + s := "hello" + fmt.Printf("%d", s) //nolint +} + +-- inline_filter.go -- +package test + +func F() bool { + return true || true //nolint:bools +} + +-- block.go -- +package test + +import "fmt" + +func F() { + //nolint + fmt.Printf("%d", "hello") +} + +-- block_multiline.go -- +package test + +func F() bool { + var i *int + //nolint + return true && + i != nil +} + +-- inline_errors.go -- +package test + +import "fmt" + +func F() { + var i *int + if i == nil { + fmt.Printf("%d", "hello") //nolint + fmt.Println(*i) // Keep nil deref error + } +} + +-- inline_column.go -- +package test + +import "fmt" + +func F() { + // Purposely used 'helo' to align the column + fmt.Printf("%d", "helo") //nolint + superLongVariableName := true || true + var _ = superLongVariableName +} + +-- large_block.go -- +package test + +import "fmt" + +var V = struct { + S string + B bool +} { + S: fmt.Sprintf("%d", "hello"), //nolint + B: true || true, +} +`, + }) +} + +func Test(t *testing.T) { + customRegister := `go_register_toolchains(nogo = "@//:nogo")` + if err := replaceInFile("WORKSPACE", "go_register_toolchains()", customRegister); err != nil { + t.Fatal(err) + } + + tests := []struct { + Name string + Target string + Expected string + }{ + { + Name: "Inline comment", + Target: "//:inline", + }, + { + Name: "Inline with lint filter", + Target: "//:inline_filter", + }, + { + Name: "Block comment", + Target: "//:block", + }, + { + Name: "Multiline block comment", + Target: "//:block_multiline", + }, + { + Name: "Inline with errors", + Target: "//:inline_errors", + Expected: "inline_errors.go:9:15: nil dereference in load (nilness)", + }, + { + Name: "Inline comment on same column does not apply", + Target: "//:inline_column", + Expected: "inline_column.go:8:27: redundant or: true || true (bools)", + }, + { + Name: "Inline comment does not apply to larger block", + Target: "//:large_block", + Expected: "large_block.go:10:5: redundant or: true || true (bools)", + }, + } + + for _, tc := range tests { + t.Run(tc.Name, func(t *testing.T) { + cmd := bazel_testing.BazelCmd("build", tc.Target) + b, err := cmd.CombinedOutput() + output := string(b) + if tc.Expected != "" && err == nil { + t.Fatal("unexpected success", output) + } + if tc.Expected == "" && err != nil { + t.Fatal("unexpected failure", output) + } + if !strings.Contains(output, tc.Expected) { + t.Errorf("output did not contain expected: %s\n%s", tc.Expected, output) + } + }) + } +} + +func replaceInFile(path, old, new string) error { + data, err := ioutil.ReadFile(path) + if err != nil { + return err + } + data = bytes.ReplaceAll(data, []byte(old), []byte(new)) + return ioutil.WriteFile(path, data, 0666) +} diff --git a/tests/core/nogo/vet/BUILD.bazel b/tests/core/nogo/vet/BUILD.bazel new file mode 100644 index 00000000..b01e0482 --- /dev/null +++ b/tests/core/nogo/vet/BUILD.bazel @@ -0,0 +1,6 @@ +load("@io_bazel_rules_go//go/tools/bazel_testing:def.bzl", "go_bazel_test") + +go_bazel_test( + name = "vet_test", + srcs = ["vet_test.go"], +) diff --git a/tests/core/nogo/vet/README.rst b/tests/core/nogo/vet/README.rst new file mode 100644 index 00000000..28f95c0e --- /dev/null +++ b/tests/core/nogo/vet/README.rst @@ -0,0 +1,14 @@ +Vet check +========= + +.. _go_library: /docs/go/core/rules.md#_go_library + +Tests to ensure that vet runs and detects errors. + +.. contents:: + +vet_test +-------- +Verifies that vet errors are emitted on a `go_library`_ with problems when built +with a ``nogo`` binary with ``vet = True``. No errors should be emitted when +analyzing error-free source code. Vet should not be enabled by default. diff --git a/tests/core/nogo/vet/vet_test.go b/tests/core/nogo/vet/vet_test.go new file mode 100644 index 00000000..4bc2163f --- /dev/null +++ b/tests/core/nogo/vet/vet_test.go @@ -0,0 +1,179 @@ +// 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 vet_test + +import ( + "bytes" + "fmt" + "io/ioutil" + "regexp" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Main: ` +-- BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_tool_library", "nogo") + +nogo( + name = "nogo", + vet = True, + visibility = ["//visibility:public"], +) + +go_library( + name = "has_errors", + srcs = ["has_errors.go"], + importpath = "haserrors", + deps = [":fmtwrap"], +) + +go_library( + name = "no_errors", + srcs = ["no_errors.go"], + cgo = True, + importpath = "noerrors", +) + +go_library( + name = "fmtwrap", + srcs = ["fmtwrap.go"], + importpath = "fmtwrap", +) + +-- has_errors.go -- +package haserrors + +// +build build_tags_error + +import ( + "fmtwrap" + "sync/atomic" +) + +func F() {} + +func Foo() bool { + x := uint64(1) + _ = atomic.AddUint64(&x, 1) + if F == nil { // nilfunc error. + return false + } + fmtwrap.Printf("%b", "hi") // printf error. + return true || true // redundant boolean error. +} + +-- no_errors.go -- +package noerrors + +// const int x = 1; +import "C" + +func Foo() bool { + return bool(C.x == 1) +} + +-- fmtwrap.go -- +package fmtwrap + +import "fmt" + +func Printf(format string, args ...interface{}) { + fmt.Printf(format, args...) +} +`, + }) +} + +func Test(t *testing.T) { + for _, test := range []struct { + desc, nogo, target string + wantSuccess bool + includes, excludes []string + }{ + { + desc: "default", + target: "//:has_errors", + wantSuccess: true, + excludes: []string{ + "\\+build comment must appear before package clause and be followed by a blank line", + "comparison of function F == nil is always false", + "Printf format %b has arg \"hi\" of wrong type string", + "redundant or: true \\|\\| true", + }, + }, { + desc: "enabled_no_errors", + target: "//:no_errors", + wantSuccess: true, + }, { + desc: "enabled_has_errors", + nogo: "@//:nogo", + target: "//:has_errors", + includes: []string{ + "misplaced \\+build comment", + "comparison of function F == nil is always false", + "Printf format %b has arg \"hi\" of wrong type string", + "redundant or: true \\|\\| true", + }, + }, + } { + t.Run(test.desc, func(t *testing.T) { + if test.nogo != "" { + origRegister := "go_register_toolchains()" + customRegister := fmt.Sprintf("go_register_toolchains(nogo = %q)", test.nogo) + if err := replaceInFile("WORKSPACE", origRegister, customRegister); err != nil { + t.Fatal(err) + } + defer replaceInFile("WORKSPACE", customRegister, origRegister) + } + + cmd := bazel_testing.BazelCmd("build", test.target) + stderr := &bytes.Buffer{} + cmd.Stderr = stderr + if err := cmd.Run(); err == nil && !test.wantSuccess { + t.Fatal("unexpected success") + } else if err != nil && test.wantSuccess { + t.Fatalf("unexpected error: %v", err) + } + + for _, pattern := range test.includes { + if matched, err := regexp.Match(pattern, stderr.Bytes()); err != nil { + t.Fatal(err) + } else if !matched { + t.Errorf("output did not contain pattern: %s", pattern) + } + } + for _, pattern := range test.excludes { + if matched, err := regexp.Match(pattern, stderr.Bytes()); err != nil { + t.Fatal(err) + } else if matched { + t.Errorf("output contained pattern: %s", pattern) + } + } + }) + } +} + +func replaceInFile(path, old, new string) error { + data, err := ioutil.ReadFile(path) + if err != nil { + return err + } + data = bytes.ReplaceAll(data, []byte(old), []byte(new)) + return ioutil.WriteFile(path, data, 0666) +} diff --git a/tests/core/output_groups/BUILD.bazel b/tests/core/output_groups/BUILD.bazel new file mode 100644 index 00000000..ec8b86c9 --- /dev/null +++ b/tests/core/output_groups/BUILD.bazel @@ -0,0 +1,36 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") + +go_library( + name = "lib", + srcs = ["lib.go"], + importpath = "lib", +) + +go_test( + name = "lib_test", + srcs = ["lib_test.go"], + embed = [":lib"], +) + +go_binary( + name = "bin", + srcs = ["bin.go"], +) + +filegroup( + name = "compilation_outputs", + testonly = True, + srcs = [ + ":bin", + ":lib", + ":lib_test", + ], + output_group = "compilation_outputs", +) + +go_test( + name = "compilation_outputs_test", + srcs = ["compilation_outputs_test.go"], + data = [":compilation_outputs"], + deps = ["//go/tools/bazel:go_default_library"], +) diff --git a/tests/core/output_groups/README.rst b/tests/core/output_groups/README.rst new file mode 100644 index 00000000..5d3d4e41 --- /dev/null +++ b/tests/core/output_groups/README.rst @@ -0,0 +1,10 @@ +output_groups functionality +=========================== + +Tests to ensure the supported `output_groups` are working as expected. + +compilation_outputs_test +------------------------ + +Checks that the `compilation_outputs` output group is populated with the +compiled archives from `go_library`, `go_test`, and `go_binary` targets. diff --git a/tests/core/output_groups/bin.go b/tests/core/output_groups/bin.go new file mode 100644 index 00000000..38dd16da --- /dev/null +++ b/tests/core/output_groups/bin.go @@ -0,0 +1,3 @@ +package main + +func main() {} diff --git a/tests/core/output_groups/compilation_outputs_test.go b/tests/core/output_groups/compilation_outputs_test.go new file mode 100644 index 00000000..25daced7 --- /dev/null +++ b/tests/core/output_groups/compilation_outputs_test.go @@ -0,0 +1,59 @@ +package output_groups + +import ( + "os" + "path/filepath" + "runtime" + "sort" + "strings" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel" +) + +func TestCompilationOutputs(t *testing.T) { + runfiles, err := bazel.ListRunfiles() + if err != nil { + t.Fatal(err) + } + + exe := "" + if runtime.GOOS == "windows" { + exe = ".exe" + } + expectedFiles := map[string]bool{ + "compilation_outputs_test" + exe: true, // test binary; not relevant + + "lib.a": false, // :lib archive + "lib_test.internal.a": false, // :lib_test archive + "bin.a": false, // :bin archive + } + for _, rf := range runfiles { + info, err := os.Stat(rf.Path) + if err != nil { + t.Error(err) + continue + } + if info.IsDir() { + continue + } + + base := filepath.Base(rf.Path) + if seen, ok := expectedFiles[base]; !ok { + t.Errorf("unexpected runfile: %s %s", rf.Path, base) + } else if !seen { + expectedFiles[base] = true + } + } + + missingFiles := make([]string, 0, len(expectedFiles)) + for path, seen := range expectedFiles { + if !seen { + missingFiles = append(missingFiles, path) + } + } + sort.Strings(missingFiles) + if len(missingFiles) > 0 { + t.Errorf("did not find expected files: %s", strings.Join(missingFiles, " ")) + } +} diff --git a/tests/core/output_groups/lib.go b/tests/core/output_groups/lib.go new file mode 100644 index 00000000..55c21f80 --- /dev/null +++ b/tests/core/output_groups/lib.go @@ -0,0 +1 @@ +package lib diff --git a/tests/core/output_groups/lib_test.go b/tests/core/output_groups/lib_test.go new file mode 100644 index 00000000..55c21f80 --- /dev/null +++ b/tests/core/output_groups/lib_test.go @@ -0,0 +1 @@ +package lib diff --git a/tests/core/race/BUILD.bazel b/tests/core/race/BUILD.bazel new file mode 100644 index 00000000..842dff95 --- /dev/null +++ b/tests/core/race/BUILD.bazel @@ -0,0 +1,6 @@ +load("@io_bazel_rules_go//go/tools/bazel_testing:def.bzl", "go_bazel_test") + +go_bazel_test( + name = "race_test", + srcs = ["race_test.go"], +) diff --git a/tests/core/race/README.rst b/tests/core/race/README.rst new file mode 100644 index 00000000..a5152211 --- /dev/null +++ b/tests/core/race/README.rst @@ -0,0 +1,10 @@ +race instrumentation +==================== + +race_test +--------- + +Embeds a library that triggers a data race inside a binary and a test. +Verifies that no race is reported by default and a race is reported when either +target is build with the ``race = "on"`` attribute or the ``--features=race`` +flag. diff --git a/tests/core/race/race_test.go b/tests/core/race/race_test.go new file mode 100644 index 00000000..815b2b18 --- /dev/null +++ b/tests/core/race/race_test.go @@ -0,0 +1,302 @@ +// 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 race_test + +import ( + "bytes" + "errors" + "fmt" + "os/exec" + "runtime" + "strings" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Main: ` +-- BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") + +go_library( + name = "racy", + srcs = [ + "race_off.go", + "race_on.go", + "racy.go", + "empty.s", # verify #2143 + ], + importpath = "example.com/racy", +) + +go_binary( + name = "racy_cmd", + srcs = ["main.go"], + embed = [":racy"], +) + +go_binary( + name = "racy_cmd_race_mode", + srcs = ["main.go"], + embed = [":racy"], + race = "on", +) + +go_test( + name = "racy_test", + srcs = ["racy_test.go"], + embed = [":racy"], +) + +go_test( + name = "racy_test_race_mode", + srcs = ["racy_test.go"], + embed = [":racy"], + race = "on", +) + +go_binary( + name = "pure_bin", + srcs = ["pure_bin.go"], + pure = "on", +) + +go_binary( + name = "pure_race_bin", + srcs = ["pure_bin.go"], + pure = "on", + race = "on", +) + +go_library( + name = "coverrace", + srcs = ["coverrace.go"], + importpath = "example.com/coverrace", +) + +go_test( + name = "coverrace_test", + srcs = ["coverrace_test.go"], + embed = [":coverrace"], + race = "on", +) +-- race_off.go -- +// +build !race + +package main + +const RaceEnabled = false + +-- race_on.go -- +// +build race + +package main + +const RaceEnabled = true + +-- racy.go -- +package main + +import ( + "flag" + "fmt" + "os" +) + +var wantRace = flag.Bool("wantrace", false, "") + +func Race() { + if *wantRace != RaceEnabled { + fmt.Fprintf(os.Stderr, "!!! -wantrace is %v, but RaceEnabled is %v\n", *wantRace, RaceEnabled) + os.Exit(1) + } + + done := make(chan bool) + m := make(map[string]string) + m["name"] = "world" + go func() { + m["name"] = "data race" + done <- true + }() + fmt.Println("Hello,", m["name"]) + <-done +} + +-- main.go -- +package main + +import "flag" + +func main() { + flag.Parse() + Race() +} + +-- racy_test.go -- +package main + +import "testing" + +func TestRace(t *testing.T) { + Race() +} + +-- empty.s -- +-- pure_bin.go -- +// +build !race + +// pure_bin will not build in race mode, since its sources will be excluded. +package main + +func main() {} + +-- coverrace.go -- +package coverrace +// copied from https://hermanschaaf.com/running-the-go-race-detector-with-cover/ +func add100() int { + total := 0 + c := make(chan int, 1) + for i := 0; i < 100; i++ { + go func(chan int) { + c <- 1 + }(c) + } + for u := 0; u < 100; u++ { + total += <-c + } + return total +} + +-- coverrace_test.go -- +package coverrace +// copied from https://hermanschaaf.com/running-the-go-race-detector-with-cover/ + +import "testing" + +func TestCoverRace(t *testing.T) { + got := add100() + if got != 100 { + t.Errorf("got %d, want %d", got, 100) + } +} +`, + }) +} + +func Test(t *testing.T) { + for _, test := range []struct { + desc, cmd, target string + featureFlag, wantRace, wantBuildFail bool + }{ + { + desc: "cmd_auto", + cmd: "run", + target: "//:racy_cmd", + }, { + desc: "cmd_attr", + cmd: "run", + target: "//:racy_cmd_race_mode", + wantRace: true, + }, { + desc: "cmd_feature", + cmd: "run", + target: "//:racy_cmd", + featureFlag: true, + wantRace: true, + }, { + desc: "test_auto", + cmd: "test", + target: "//:racy_test", + }, { + desc: "test_attr", + cmd: "test", + target: "//:racy_test_race_mode", + wantRace: true, + }, { + desc: "test_feature", + cmd: "test", + target: "//:racy_test", + featureFlag: true, + wantRace: true, + }, { + desc: "pure_bin", + cmd: "build", + target: "//:pure_bin", + featureFlag: true, + }, { + desc: "pure_race_bin", + cmd: "build", + target: "//:pure_race_bin", + wantBuildFail: true, + }, { + desc: "cover_race", + cmd: "coverage", + target: "//:coverrace_test", + featureFlag: true, + }, + } { + t.Run(test.desc, func(t *testing.T) { + // TODO(#2518): fix coverage tests on Windows + if test.cmd == "coverage" && runtime.GOOS == "windows" { + t.Skip("TODO(#2518): fix and enable coverage tests on Windows") + } + args := []string{test.cmd} + if test.featureFlag { + args = append(args, "--@io_bazel_rules_go//go/config:race") + } + args = append(args, test.target) + if test.cmd == "test" { + args = append(args, fmt.Sprintf("--test_arg=-wantrace=%v", test.wantRace)) + } else if test.cmd == "run" { + args = append(args, "--", fmt.Sprintf("-wantrace=%v", test.wantRace)) + } + cmd := bazel_testing.BazelCmd(args...) + stderr := &bytes.Buffer{} + cmd.Stderr = stderr + t.Logf("running: bazel %s", strings.Join(args, " ")) + if err := cmd.Run(); err != nil { + var xerr *exec.ExitError + if !errors.As(err, &xerr) { + t.Fatalf("unexpected error: %v", err) + } + if xerr.ExitCode() == bazel_testing.BUILD_FAILURE { + if !test.wantBuildFail { + t.Fatalf("unexpected build failure: %v\nstderr:\n%s", err, stderr.Bytes()) + } + return + } else if xerr.ExitCode() == bazel_testing.TESTS_FAILED { + if bytes.Contains(stderr.Bytes(), []byte("!!!")) { + t.Fatalf("error running %s:\n%s", strings.Join(cmd.Args, " "), stderr.Bytes()) + } else if !test.wantRace { + t.Fatalf("error running %s without race enabled\n%s", strings.Join(cmd.Args, " "), stderr.Bytes()) + } + } else if test.wantRace { + if !bytes.Contains(stderr.Bytes(), []byte("WARNING: DATA RACE")) { + t.Fatalf("wanted data race; command failed with: %v\nstderr:\n%s", err, stderr.Bytes()) + } + return + } else { + t.Fatalf("unexpected error: %v\nstderr:\n%s", err, stderr.Bytes()) + } + } else if test.wantRace { + t.Fatalf("command %s with race enabled did not fail", strings.Join(cmd.Args, " ")) + } else if test.wantBuildFail { + t.Fatalf("target %s did not fail to build", test.target) + } + }) + } +} diff --git a/tests/core/runfiles/BUILD.bazel b/tests/core/runfiles/BUILD.bazel new file mode 100644 index 00000000..c7db6d3d --- /dev/null +++ b/tests/core/runfiles/BUILD.bazel @@ -0,0 +1,48 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") + +package(default_visibility = ["//visibility:public"]) + +test_suite( + name = "runfiles_tests", + tests = [ + ":local_test", + "@runfiles_remote_test//:remote_test", + ], +) + +go_test( + name = "local_test", + srcs = ["runfiles_test.go"], + deps = [":check_runfiles"], +) + +go_binary( + name = "local_cmd", + srcs = ["runfiles_cmd.go"], + deps = [":check_runfiles"], +) + +go_binary( + name = "local_bin", + srcs = ["empty_bin.go"], +) + +go_library( + name = "check_runfiles", + srcs = ["check_runfiles.go"], + data = [ + "local_file.txt", + ":local_bin", + ":local_group", + "@runfiles_remote_test//:remote_bin", + "@runfiles_remote_test//:remote_file.txt", + "@runfiles_remote_test//:remote_group", + ], + importpath = "github.com/bazelbuild/rules_go/tests/core/runfiles/check", + deps = ["//go/tools/bazel:go_default_library"], +) + +filegroup( + name = "local_group", + srcs = ["local_group.txt"], +) diff --git a/tests/core/runfiles/README.rst b/tests/core/runfiles/README.rst new file mode 100644 index 00000000..ec357826 --- /dev/null +++ b/tests/core/runfiles/README.rst @@ -0,0 +1,23 @@ +Runfiles functionality +===================== + +runfiles_tests +-------------- + +Checks that functions in ``//go/tools/bazel:go_default_library`` that +provide access to runfiles behave correctly. In particular, this checks: + +* ``Runfile`` works for regular files. +* ``FindBinary`` works for binaries. +* ``ListRunfiles`` lists all expected files. +* These functions work for runfiles in the local workspace and for files in + external repositories (``@runfiles_remote_test`` is a ``local_repository`` + that points to a subdirectory here). +* These functions work in tests invoked with ``bazel test`` and + binaries invoked with ``bazel run``. +* These functions work on Windows and other platforms. Bazel doesn't + create a symlink tree for runfiles on Windows since symbolic links + can't be created without administrative privilege by default. + +TODO: Verify binary behavior in CI. The ``local_bin`` and ``remote_bin`` +targets verify behavior for binaries, but they are not tests. diff --git a/tests/core/runfiles/bin.go b/tests/core/runfiles/bin.go new file mode 100644 index 00000000..0ced7a9e --- /dev/null +++ b/tests/core/runfiles/bin.go @@ -0,0 +1,17 @@ +// 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 main + +func main() {} diff --git a/tests/core/runfiles/check_runfiles.go b/tests/core/runfiles/check_runfiles.go new file mode 100644 index 00000000..8858a573 --- /dev/null +++ b/tests/core/runfiles/check_runfiles.go @@ -0,0 +1,121 @@ +// 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 check + +import ( + "fmt" + "os" + "path/filepath" + "runtime" + "sort" + "strings" + + "github.com/bazelbuild/rules_go/go/tools/bazel" +) + +type TestFile struct { + Workspace, ShortPath, Path string + Binary bool +} + +var DefaultTestFiles = []TestFile{ + {Workspace: "io_bazel_rules_go", Path: "tests/core/runfiles/local_file.txt"}, + {Workspace: "io_bazel_rules_go", Path: "tests/core/runfiles/local_group.txt"}, + {Workspace: "io_bazel_rules_go", Path: "tests/core/runfiles/local_bin", Binary: true}, + {Workspace: "runfiles_remote_test", Path: "remote_file.txt"}, + {Workspace: "runfiles_remote_test", Path: "remote_group.txt"}, + {Workspace: "runfiles_remote_test", Path: "remote_bin", Binary: true}, +} + +func CheckRunfiles(files []TestFile) error { + // Check that the runfiles directory matches the current workspace. + // There is no runfiles directory on Windows. + if runtime.GOOS != "windows" { + dir, err := bazel.RunfilesPath() + if err != nil { + return err + } + root, base := filepath.Dir(dir), filepath.Base(dir) + if !strings.HasSuffix(root, ".runfiles") { + return fmt.Errorf("RunfilesPath: %q is not a .runfiles directory", dir) + } + workspace := os.Getenv("TEST_WORKSPACE") + if workspace != "" && workspace != base { + return fmt.Errorf("RunfilesPath: %q does not match test workspace %s", dir, workspace) + } + if srcDir := os.Getenv("TEST_SRCDIR"); srcDir != "" && filepath.Join(srcDir, workspace) != dir { + return fmt.Errorf("RunfilesPath: %q does not match TEST_SRCDIR %q", dir, srcDir) + } + } + + // Check that files can be found with Runfile or FindBinary. + // Make sure the paths returned are absolute paths to actual files. + seen := make(map[string]string) + for _, f := range files { + var got string + var err error + if !f.Binary { + if got, err = bazel.Runfile(f.Path); err != nil { + return err + } + if !filepath.IsAbs(got) { + return fmt.Errorf("Runfile %s: got a relative path %q; want absolute", f.Path, got) + } + seen[f.Path] = got + } else { + var pkg, name string + if i := strings.LastIndex(f.Path, "/"); i < 0 { + name = f.Path + } else { + pkg = f.Path[:i] + name = f.Path[i+1:] + } + var ok bool + if got, ok = bazel.FindBinary(pkg, name); !ok { + return fmt.Errorf("FindBinary %s %s: could not find binary", pkg, name) + } + if !filepath.IsAbs(got) { + return fmt.Errorf("FindBinary %s %s: got a relative path %q; want absolute", pkg, name, got) + } + } + + if _, err := os.Stat(got); err != nil { + return fmt.Errorf("%s: could not stat: %v", f.Path, err) + } + } + + // Check that the files can be listed. + entries, err := bazel.ListRunfiles() + if err != nil { + return err + } + for _, e := range entries { + if want, ok := seen[e.ShortPath]; ok && want != e.Path { + return err + } + delete(seen, e.ShortPath) + } + + if len(seen) > 0 { + unseen := make([]string, 0, len(seen)) + for short := range seen { + unseen = append(unseen, short) + } + sort.Strings(unseen) + return fmt.Errorf("ListRunfiles did not include files:\n\t%s", strings.Join(unseen, "\n\t")) + } + + return nil +} diff --git a/tests/core/runfiles/empty_bin.go b/tests/core/runfiles/empty_bin.go new file mode 100644 index 00000000..38dd16da --- /dev/null +++ b/tests/core/runfiles/empty_bin.go @@ -0,0 +1,3 @@ +package main + +func main() {} diff --git a/tests/core/runfiles/local_file.txt b/tests/core/runfiles/local_file.txt new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/core/runfiles/local_file.txt diff --git a/tests/core/runfiles/local_group.txt b/tests/core/runfiles/local_group.txt new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/core/runfiles/local_group.txt diff --git a/tests/core/runfiles/runfiles_cmd.go b/tests/core/runfiles/runfiles_cmd.go new file mode 100644 index 00000000..0ab124b5 --- /dev/null +++ b/tests/core/runfiles/runfiles_cmd.go @@ -0,0 +1,28 @@ +// 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 main + +import ( + "fmt" + "os" + + "github.com/bazelbuild/rules_go/tests/core/runfiles/check" +) + +func main() { + if err := check.CheckRunfiles(check.DefaultTestFiles); err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + } +} diff --git a/tests/core/runfiles/runfiles_remote_test/BUILD.bazel b/tests/core/runfiles/runfiles_remote_test/BUILD.bazel new file mode 100644 index 00000000..112dcd35 --- /dev/null +++ b/tests/core/runfiles/runfiles_remote_test/BUILD.bazel @@ -0,0 +1,27 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_test") + +package(default_visibility = ["//visibility:public"]) + +go_test( + name = "remote_test", + srcs = ["@io_bazel_rules_go//tests/core/runfiles:runfiles_test.go"], + deps = ["@io_bazel_rules_go//tests/core/runfiles:check_runfiles"], +) + +go_binary( + name = "remote_cmd", + srcs = ["@io_bazel_rules_go//tests/core/runfiles:runfiles_cmd.go"], + deps = ["@io_bazel_rules_go//tests/core/runfiles:check_runfiles"], +) + +go_binary( + name = "remote_bin", + srcs = ["@io_bazel_rules_go//tests/core/runfiles:empty_bin.go"], +) + +filegroup( + name = "remote_group", + srcs = ["remote_group.txt"], +) + +exports_files(["remote_file.txt"]) diff --git a/tests/core/runfiles/runfiles_remote_test/WORKSPACE b/tests/core/runfiles/runfiles_remote_test/WORKSPACE new file mode 100644 index 00000000..c9af3f85 --- /dev/null +++ b/tests/core/runfiles/runfiles_remote_test/WORKSPACE @@ -0,0 +1 @@ +workspace(name = "runfiles_remote_test") diff --git a/tests/core/runfiles/runfiles_remote_test/remote_file.txt b/tests/core/runfiles/runfiles_remote_test/remote_file.txt new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/core/runfiles/runfiles_remote_test/remote_file.txt diff --git a/tests/core/runfiles/runfiles_remote_test/remote_group.txt b/tests/core/runfiles/runfiles_remote_test/remote_group.txt new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/core/runfiles/runfiles_remote_test/remote_group.txt diff --git a/tests/core/runfiles/runfiles_test.go b/tests/core/runfiles/runfiles_test.go new file mode 100644 index 00000000..62aab3af --- /dev/null +++ b/tests/core/runfiles/runfiles_test.go @@ -0,0 +1,27 @@ +// 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 main + +import ( + "testing" + + "github.com/bazelbuild/rules_go/tests/core/runfiles/check" +) + +func Test(t *testing.T) { + if err := check.CheckRunfiles(check.DefaultTestFiles); err != nil { + t.Fatal(err) + } +} diff --git a/tests/core/starlark/BUILD.bazel b/tests/core/starlark/BUILD.bazel new file mode 100644 index 00000000..c26a6bea --- /dev/null +++ b/tests/core/starlark/BUILD.bazel @@ -0,0 +1,6 @@ +load(":common_tests.bzl", "common_test_suite") +load(":sdk_tests.bzl", "sdk_test_suite") + +common_test_suite() + +sdk_test_suite() diff --git a/tests/core/starlark/README.rst b/tests/core/starlark/README.rst new file mode 100644 index 00000000..e1f8df76 --- /dev/null +++ b/tests/core/starlark/README.rst @@ -0,0 +1,9 @@ +Starlark unit tests +======================= + +common_test_suite +--------- + +Checks that ``has_shared_lib_extension`` from ``//go/private:common.bzl`` +correctly matches shared library filenames, which may optionally have a version +number at the end. diff --git a/tests/core/starlark/common_tests.bzl b/tests/core/starlark/common_tests.bzl new file mode 100644 index 00000000..e83d65f3 --- /dev/null +++ b/tests/core/starlark/common_tests.bzl @@ -0,0 +1,53 @@ +load("@bazel_skylib//lib:unittest.bzl", "asserts", "unittest") +load("//go/private:common.bzl", "count_group_matches", "has_shared_lib_extension") + +def _versioned_shared_libraries_test(ctx): + env = unittest.begin(ctx) + + # See //src/test/java/com/google/devtools/build/lib/rules/cpp:CppFileTypesTest.java + # for the corresponding native C++ rules tests. + asserts.true(env, has_shared_lib_extension("somelibrary.so")) + asserts.true(env, has_shared_lib_extension("somelibrary.so.2")) + asserts.true(env, has_shared_lib_extension("somelibrary.so.20")) + asserts.true(env, has_shared_lib_extension("somelibrary.so.20.2")) + asserts.true(env, has_shared_lib_extension("a/somelibrary.so.2")) + asserts.true(env, has_shared_lib_extension("somelibrary✅.so.2")) + asserts.true(env, has_shared_lib_extension("somelibrary✅.so.2.1")) + asserts.false(env, has_shared_lib_extension("somelibrary.so.e")) + asserts.false(env, has_shared_lib_extension("xx.1")) + asserts.false(env, has_shared_lib_extension("somelibrary.so.2e")) + asserts.false(env, has_shared_lib_extension("somelibrary.so.e2")) + asserts.false(env, has_shared_lib_extension("somelibrary.so.20.e2")) + asserts.false(env, has_shared_lib_extension("somelibrary.a.2")) + asserts.false(env, has_shared_lib_extension("somelibrary.a..2")) + asserts.false(env, has_shared_lib_extension("somelibrary.so.2.")) + asserts.false(env, has_shared_lib_extension("somelibrary.so.")) + asserts.false(env, has_shared_lib_extension("somelibrary.so.2🚫")) + asserts.false(env, has_shared_lib_extension("somelibrary.so.🚫2")) + asserts.false(env, has_shared_lib_extension("somelibrary.so🚫.2.0")) + + return unittest.end(env) + +versioned_shared_libraries_test = unittest.make(_versioned_shared_libraries_test) + +def _count_group_matches_test(ctx): + env = unittest.begin(ctx) + + asserts.equals(env, 1, count_group_matches("{foo_status}", "{foo_", "}")) + asserts.equals(env, 1, count_group_matches("{foo_status} {status}", "{foo_", "}")) + asserts.equals(env, 0, count_group_matches("{foo_status}", "{bar_", "}")) + asserts.equals(env, 1, count_group_matches("{foo_status}", "{", "}")) + asserts.equals(env, 2, count_group_matches("{foo} {bar}", "{", "}")) + asserts.equals(env, 2, count_group_matches("{foo{bar} {baz}", "{", "}")) + + return unittest.end(env) + +count_group_matches_test = unittest.make(_count_group_matches_test) + +def common_test_suite(): + """Creates the test targets and test suite for common.bzl tests.""" + unittest.suite( + "common_tests", + versioned_shared_libraries_test, + count_group_matches_test, + ) diff --git a/tests/core/starlark/packagedriver/BUILD.bazel b/tests/core/starlark/packagedriver/BUILD.bazel new file mode 100644 index 00000000..45dd6aba --- /dev/null +++ b/tests/core/starlark/packagedriver/BUILD.bazel @@ -0,0 +1,5 @@ +load(":go_pkg_info_aspect_test.bzl", "package_driver_suite") + +package_driver_suite( + name = "package_driver_test", +) diff --git a/tests/core/starlark/packagedriver/fixtures/a/BUILD.bazel b/tests/core/starlark/packagedriver/fixtures/a/BUILD.bazel new file mode 100644 index 00000000..0dad32d9 --- /dev/null +++ b/tests/core/starlark/packagedriver/fixtures/a/BUILD.bazel @@ -0,0 +1,8 @@ +load("//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = ["a.go"], + importpath = "example.com/a", + visibility = ["//tests/core/starlark/packagedriver:__subpackages__"], +) diff --git a/tests/core/starlark/packagedriver/fixtures/a/a.go b/tests/core/starlark/packagedriver/fixtures/a/a.go new file mode 100644 index 00000000..af058f6d --- /dev/null +++ b/tests/core/starlark/packagedriver/fixtures/a/a.go @@ -0,0 +1,5 @@ +package a + +func A() { + return +} diff --git a/tests/core/starlark/packagedriver/fixtures/b/BUILD.bazel b/tests/core/starlark/packagedriver/fixtures/b/BUILD.bazel new file mode 100644 index 00000000..cdf5fd41 --- /dev/null +++ b/tests/core/starlark/packagedriver/fixtures/b/BUILD.bazel @@ -0,0 +1,8 @@ +load("//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = ["b.go"], + importpath = "example.com/b", + visibility = ["//tests/core/starlark/packagedriver:__subpackages__"], +) diff --git a/tests/core/starlark/packagedriver/fixtures/b/b.go b/tests/core/starlark/packagedriver/fixtures/b/b.go new file mode 100644 index 00000000..869da65a --- /dev/null +++ b/tests/core/starlark/packagedriver/fixtures/b/b.go @@ -0,0 +1,5 @@ +package b + +func B() { + return +} diff --git a/tests/core/starlark/packagedriver/fixtures/c/BUILD.bazel b/tests/core/starlark/packagedriver/fixtures/c/BUILD.bazel new file mode 100644 index 00000000..28e8ef92 --- /dev/null +++ b/tests/core/starlark/packagedriver/fixtures/c/BUILD.bazel @@ -0,0 +1,20 @@ +load("//go:def.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = ["c.go"], + importpath = "example.com/c", + visibility = ["//tests/core/starlark/packagedriver:__subpackages__"], + deps = [ + "//tests/core/starlark/packagedriver/fixtures/a:go_default_library", + "//tests/core/starlark/packagedriver/fixtures/b:go_default_library", + ], +) + +go_test( + name = "go_default_test", + srcs = ["c_test.go"], + embed = [":go_default_library"], + tags = ["manual"], + visibility = ["//tests/core/starlark/packagedriver:__subpackages__"], +) diff --git a/tests/core/starlark/packagedriver/fixtures/c/c.go b/tests/core/starlark/packagedriver/fixtures/c/c.go new file mode 100644 index 00000000..f8a8dd3b --- /dev/null +++ b/tests/core/starlark/packagedriver/fixtures/c/c.go @@ -0,0 +1,11 @@ +package c + +import ( + "example.com/a" + "example.com/b" +) + +func C() { + a.A() + b.B() +} diff --git a/tests/core/starlark/packagedriver/fixtures/c/c_test.go b/tests/core/starlark/packagedriver/fixtures/c/c_test.go new file mode 100644 index 00000000..4b25df88 --- /dev/null +++ b/tests/core/starlark/packagedriver/fixtures/c/c_test.go @@ -0,0 +1,9 @@ +package c + +import ( + "testing" +) + +func TestC(t *testing.T) { + C() +} diff --git a/tests/core/starlark/packagedriver/go_pkg_info_aspect_test.bzl b/tests/core/starlark/packagedriver/go_pkg_info_aspect_test.bzl new file mode 100644 index 00000000..80b8d8b6 --- /dev/null +++ b/tests/core/starlark/packagedriver/go_pkg_info_aspect_test.bzl @@ -0,0 +1,32 @@ +load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") +load("//go/tools/gopackagesdriver:aspect.bzl", "go_pkg_info_aspect") + +def _package_driver_pkg_json_test_impl(ctx): + env = analysistest.begin(ctx) + + target_under_test = analysistest.target_under_test(env) + json_files = [f.basename for f in target_under_test[OutputGroupInfo].go_pkg_driver_json_file.to_list()] + asserts.true(env, "go_default_test.pkg.json" in json_files, "{} does not contain go_default_test.pkg.json".format(json_files)) + + return analysistest.end(env) + +package_driver_pkg_json_test = analysistest.make( + _package_driver_pkg_json_test_impl, + extra_target_under_test_aspects = [go_pkg_info_aspect], +) + +def _test_package_driver(): + package_driver_pkg_json_test( + name = "package_driver_should_return_pkg_json_for_go_test", + target_under_test = "//tests/core/starlark/packagedriver/fixtures/c:go_default_test", + ) + +def package_driver_suite(name): + _test_package_driver() + + native.test_suite( + name = name, + tests = [ + ":package_driver_should_return_pkg_json_for_go_test", + ], + ) diff --git a/tests/core/starlark/sdk_tests.bzl b/tests/core/starlark/sdk_tests.bzl new file mode 100644 index 00000000..5339aec6 --- /dev/null +++ b/tests/core/starlark/sdk_tests.bzl @@ -0,0 +1,86 @@ +load("@bazel_skylib//lib:unittest.bzl", "asserts", "unittest") +load("//go/private:sdk.bzl", "go_toolchains_single_definition") + +def _go_toolchains_single_definition_with_version_test(ctx): + env = unittest.begin(ctx) + + result = go_toolchains_single_definition( + ctx = None, + prefix = "123_prefix_", + goos = "linux", + goarch = "amd64", + sdk_repo = "sdk_repo", + sdk_type = "download", + sdk_version = "1.20.2rc1", + ) + asserts.equals(env, [], result.loads) + asserts.equals(env, [ + """ +_123_PREFIX_MAJOR_VERSION = "1" +_123_PREFIX_MINOR_VERSION = "20" +_123_PREFIX_PATCH_VERSION = "2" +_123_PREFIX_PRERELEASE_SUFFIX = "rc1" +""", + """declare_bazel_toolchains( + prefix = "123_prefix_", + go_toolchain_repo = "@sdk_repo", + host_goarch = "amd64", + host_goos = "linux", + major = _123_PREFIX_MAJOR_VERSION, + minor = _123_PREFIX_MINOR_VERSION, + patch = _123_PREFIX_PATCH_VERSION, + prerelease = _123_PREFIX_PRERELEASE_SUFFIX, + sdk_type = "download", +) +""", + ], result.chunks) + + return unittest.end(env) + +go_toolchains_single_definition_with_version_test = unittest.make(_go_toolchains_single_definition_with_version_test) + +def _go_toolchains_single_definition_without_version_test(ctx): + env = unittest.begin(ctx) + + result = go_toolchains_single_definition( + ctx = None, + prefix = "123_prefix_", + goos = "linux", + goarch = "amd64", + sdk_repo = "sdk_repo", + sdk_type = "download", + sdk_version = None, + ) + asserts.equals(env, ["""load( + "@sdk_repo//:version.bzl", + _123_PREFIX_MAJOR_VERSION = "MAJOR_VERSION", + _123_PREFIX_MINOR_VERSION = "MINOR_VERSION", + _123_PREFIX_PATCH_VERSION = "PATCH_VERSION", + _123_PREFIX_PRERELEASE_SUFFIX = "PRERELEASE_SUFFIX", +) +"""], result.loads) + asserts.equals(env, [ + """declare_bazel_toolchains( + prefix = "123_prefix_", + go_toolchain_repo = "@sdk_repo", + host_goarch = "amd64", + host_goos = "linux", + major = _123_PREFIX_MAJOR_VERSION, + minor = _123_PREFIX_MINOR_VERSION, + patch = _123_PREFIX_PATCH_VERSION, + prerelease = _123_PREFIX_PRERELEASE_SUFFIX, + sdk_type = "download", +) +""", + ], result.chunks) + + return unittest.end(env) + +go_toolchains_single_definition_without_version_test = unittest.make(_go_toolchains_single_definition_without_version_test) + +def sdk_test_suite(): + unittest.suite( + "sdk_tests", + go_toolchains_single_definition_with_version_test, + go_toolchains_single_definition_without_version_test, + ) diff --git a/tests/core/stdlib/BUILD.bazel b/tests/core/stdlib/BUILD.bazel new file mode 100644 index 00000000..eed94654 --- /dev/null +++ b/tests/core/stdlib/BUILD.bazel @@ -0,0 +1,11 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") +load(":stdlib_files.bzl", "stdlib_files") + +go_test( + name = "buildid_test", + srcs = ["buildid_test.go"], + data = [":stdlib_files"], + deps = ["//go/runfiles"], +) + +stdlib_files(name = "stdlib_files") diff --git a/tests/core/stdlib/README.rst b/tests/core/stdlib/README.rst new file mode 100644 index 00000000..4a942953 --- /dev/null +++ b/tests/core/stdlib/README.rst @@ -0,0 +1,13 @@ +stdlib functionality +==================== + +buildid_test +------------ + +Checks that the ``stdlib`` rule builds archives without Go build ids. + +Go build ids are used for caching within ``go build``; they are not needed by +Bazel, which has its own caching mechanism. The build id is influenced by +all inputs to the build, including cgo environment variables. Since these +variables may include sandbox paths, they can make the build id +non-reproducible, even though they don't affect the final binary. diff --git a/tests/core/stdlib/buildid_test.go b/tests/core/stdlib/buildid_test.go new file mode 100644 index 00000000..d95ba903 --- /dev/null +++ b/tests/core/stdlib/buildid_test.go @@ -0,0 +1,93 @@ +//go:build go1.10 +// +build go1.10 + +/* Copyright 2018 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 buildid_test + +import ( + "bytes" + "errors" + "os" + "os/exec" + "path/filepath" + "testing" + + "github.com/bazelbuild/rules_go/go/runfiles" +) + +func TestEmptyBuildID(t *testing.T) { + // Locate the buildid tool and several archive files to check. + // fmt.a - pure go + // crypto/aes.a - contains assembly + // runtime/cgo.a - contains cgo + // The path may vary depending on platform and architecture, so just + // do a search. + var buildidPath string + pkgPaths := map[string]string{ + "fmt.a": "", + "aes.a": "", + "cgo.a": "", + } + stdlibPkgDir, err := runfiles.Rlocation("io_bazel_rules_go/stdlib_/pkg") + if err != nil { + t.Fatal(err) + } + n := len(pkgPaths) + done := errors.New("done") + var visit filepath.WalkFunc + visit = func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if filepath.Base(path) == "buildid" && (info.Mode()&0111) != 0 { + buildidPath = path + } + for pkg := range pkgPaths { + if filepath.Base(path) == pkg { + pkgPaths[pkg] = path + n-- + } + } + if buildidPath != "" && n == 0 { + return done + } + return nil + } + if err = filepath.Walk(stdlibPkgDir, visit); err != nil && err != done { + t.Fatal(err) + } + if buildidPath == "" { + t.Fatal("buildid not found") + } + + for pkg, path := range pkgPaths { + if path == "" { + t.Errorf("could not locate %s", pkg) + continue + } + // Equivalent to: go tool buildid pkg.a + // It's an error if this produces any output. + cmd := exec.Command(buildidPath, path) + out, err := cmd.Output() + if err != nil { + t.Error(err) + } + if len(bytes.TrimSpace(out)) > 0 { + t.Errorf("%s: unexpected buildid: %s", path, out) + } + } +} diff --git a/tests/core/stdlib/reproduce_test.sh b/tests/core/stdlib/reproduce_test.sh new file mode 100755 index 00000000..b709ab56 --- /dev/null +++ b/tests/core/stdlib/reproduce_test.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +set -eux -o pipefail + +function run_bazel() { + bazel clean --expunge + bazel test //tests/core/go_binary:all + find bazel-out/ -name '*.a' | sort | uniq | grep stdlib | xargs shasum > $1 +} + +FILE1=$(mktemp) +FILE2=$(mktemp) + +echo First run +run_bazel ${FILE1} + +echo Second run +run_bazel ${FILE2} + +echo Diffing runs +diff ${FILE1} ${FILE2} + +echo Removing files +rm ${FILE1} ${FILE2} diff --git a/tests/core/stdlib/stdlib_files.bzl b/tests/core/stdlib/stdlib_files.bzl new file mode 100644 index 00000000..35d48f33 --- /dev/null +++ b/tests/core/stdlib/stdlib_files.bzl @@ -0,0 +1,49 @@ +# Copyright 2018 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("//go/private:providers.bzl", "GoStdLib") + +def _force_rebuild_transition_impl(settings, attr): + return {"//go/config:race": True} + +force_rebuild_transition = transition( + implementation = _force_rebuild_transition_impl, + inputs = ["//go/config:race"], + outputs = ["//go/config:race"], +) + +def _stdlib_files_impl(ctx): + # When an outgoing transition (aka split transition) is used, + # ctx.attr._stdlib is a list of Target. + stdlib = ctx.attr._stdlib[0][GoStdLib] + libs = stdlib.libs + runfiles = ctx.runfiles(files = libs) + return [DefaultInfo( + files = depset(libs + [stdlib._list_json]), + runfiles = runfiles, + )] + +stdlib_files = rule( + implementation = _stdlib_files_impl, + attrs = { + "_stdlib": attr.label( + default = "@io_bazel_rules_go//:stdlib", + providers = [GoStdLib], + cfg = force_rebuild_transition, + ), + "_allowlist_function_transition": attr.label( + default = "@bazel_tools//tools/allowlists/function_transition_allowlist", + ), + }, +) diff --git a/tests/core/strip/BUILD.bazel b/tests/core/strip/BUILD.bazel new file mode 100644 index 00000000..bc2f6bd8 --- /dev/null +++ b/tests/core/strip/BUILD.bazel @@ -0,0 +1,6 @@ +load("@io_bazel_rules_go//go/tools/bazel_testing:def.bzl", "go_bazel_test") + +go_bazel_test( + name = "strip_test", + srcs = ["strip_test.go"], +) diff --git a/tests/core/strip/README.rst b/tests/core/strip/README.rst new file mode 100644 index 00000000..1b5c1f96 --- /dev/null +++ b/tests/core/strip/README.rst @@ -0,0 +1,13 @@ +symbol stripping +==================== + +strip_test +--------- + +Tests that the global Bazel configuration for stripping are applied to go_binary +targets. +In particular, it tests that stripping is performed iff the bazel flag ``--strip`` +is set to ``always`` or ``--strip`` is set to ``sometimes`` and ``--compilation_mode`` +is ``fastbuild``. +Additionally, it tests that stack traces still contain the same information when stripping +is enabled. diff --git a/tests/core/strip/strip_test.go b/tests/core/strip/strip_test.go new file mode 100644 index 00000000..b7183033 --- /dev/null +++ b/tests/core/strip/strip_test.go @@ -0,0 +1,270 @@ +// 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 strip_test + +import ( + "bytes" + "errors" + "fmt" + "os/exec" + "strings" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Main: ` +-- BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_binary") + +go_binary( + name = "strip", + srcs = ["strip.go"], +) +-- strip.go -- +package main + +import ( + "debug/elf" + "debug/macho" + "debug/pe" + "errors" + "flag" + "fmt" + "io" + "os" + "regexp" + "runtime" + "runtime/debug" + "strings" +) + +var wantStrip = flag.Bool("wantstrip", false, "") + +func main() { + flag.Parse() + stackTrace, err := panicAndRecover() + if err != nil { + panic(err) + } + gotStackTrace := strings.Split(stackTrace, "\n") + if len(gotStackTrace) != len(wantStackTrace) { + panic(fmt.Sprintf("got %d lines of stack trace, want %d", len(gotStackTrace), len(wantStackTrace))) + } + for i := range gotStackTrace { + expectedLine := regexp.MustCompile(wantStackTrace[i]) + if !expectedLine.MatchString(gotStackTrace[i]) { + panic(fmt.Sprintf("got unexpected stack trace line %q at index %d", gotStackTrace[i], i)) + } + } + stripped, err := isStripped() + if err != nil { + panic(err) + } + if stripped != *wantStrip { + panic(fmt.Sprintf("got stripped=%t, want stripped=%t", stripped, *wantStrip)) + } +} + +func panicAndRecover() (stackTrace string, err error) { + defer func() { + if r := recover(); r != nil { + stackTrace = string(debug.Stack()) + } + }() + panic("test") + return "", errors.New("should not reach here") +} + +func isStripped() (bool, error) { + ownLocation, err := os.Executable() + if err != nil { + return false, err + } + ownBinary, err := os.Open(ownLocation) + if err != nil { + return false, err + } + defer ownBinary.Close() + switch runtime.GOOS { + case "darwin": + return isStrippedMachO(ownBinary) + case "linux": + return isStrippedElf(ownBinary) + case "windows": + return isStrippedPE(ownBinary) + default: + return false, fmt.Errorf("unsupported OS: %s", runtime.GOOS) + } +} + +func isStrippedMachO(f io.ReaderAt) (bool, error) { + macho, err := macho.NewFile(f) + if err != nil { + return false, err + } + gotDwarf := macho.Segment("__DWARF") != nil + gotDebugInfo := macho.Section("__zdebug_info") != nil + if gotDwarf != gotDebugInfo { + return false, fmt.Errorf("inconsistent stripping: gotDwarf=%v, gotDebugInfo=%v", gotDwarf, gotDebugInfo) + } + return !gotDwarf, nil +} + +func isStrippedElf(f io.ReaderAt) (bool, error) { + elf, err := elf.NewFile(f) + if err != nil { + return false, err + } + var gotSymtab bool + for _, section := range elf.Sections { + if section.Name == ".symtab" { + gotSymtab = true + break + } + } + return !gotSymtab, nil +} + + +func isStrippedPE(f io.ReaderAt) (bool, error) { + pe, err := pe.NewFile(f) + if err != nil { + return false, err + } + symtab := pe.Section(".symtab") + if symtab == nil { + return false, fmt.Errorf("no .symtab section") + } + emptySymtab := (symtab.VirtualSize <= 4) && (symtab.Size <= 512) + return emptySymtab, nil +} + + +` + embedWantedStackTraces(), + }) +} + +func Test(t *testing.T) { + for _, test := range []struct { + desc, stripFlag, compilationMode string + wantStrip bool + }{ + { + desc: "run_auto", + wantStrip: true, + }, + { + desc: "run_fastbuild", + compilationMode: "fastbuild", + wantStrip: true, + }, + { + desc: "run_dbg", + compilationMode: "dbg", + }, + { + desc: "run_opt", + compilationMode: "opt", + }, + { + desc: "run_always", + stripFlag: "always", + wantStrip: true, + }, + { + desc: "run_always_opt", + stripFlag: "always", + compilationMode: "opt", + wantStrip: true, + }, + { + desc: "run_never", + stripFlag: "never", + }, + { + desc: "run_sometimes_fastbuild", + stripFlag: "sometimes", + compilationMode: "fastbuild", + wantStrip: true, + }, + { + desc: "run_sometimes_dbg", + stripFlag: "sometimes", + compilationMode: "dbg", + }, + { + desc: "run_sometimes_opt", + stripFlag: "sometimes", + compilationMode: "opt", + }, + } { + t.Run(test.desc, func(t *testing.T) { + args := []string{"run"} + if len(test.stripFlag) > 0 { + args = append(args, "--strip", test.stripFlag) + } + if len(test.compilationMode) > 0 { + args = append(args, "--compilation_mode", test.compilationMode) + } + args = append(args, "//:strip", "--", fmt.Sprintf("-wantstrip=%v", test.wantStrip)) + cmd := bazel_testing.BazelCmd(args...) + stderr := &bytes.Buffer{} + cmd.Stderr = stderr + t.Logf("running: bazel %s", strings.Join(args, " ")) + if err := cmd.Run(); err != nil { + var xerr *exec.ExitError + if !errors.As(err, &xerr) { + t.Fatalf("unexpected error: %v", err) + } + if xerr.ExitCode() == bazel_testing.BUILD_FAILURE { + t.Fatalf("unexpected build failure: %v\nstderr:\n%s", err, stderr.Bytes()) + return + } else if xerr.ExitCode() == bazel_testing.TESTS_FAILED { + t.Fatalf("error running %s:\n%s", strings.Join(cmd.Args, " "), stderr.Bytes()) + } else { + t.Fatalf("unexpected error: %v\nstderr:\n%s", err, stderr.Bytes()) + } + } + }) + } +} + +var wantStackTrace = []string{ + `^goroutine \d+ \[running\]:$`, + `^runtime/debug\.Stack\(\)$`, + `^ GOROOT/src/runtime/debug/stack\.go:\d+ \+0x[0-9a-f]+$`, + `^main\.panicAndRecover\.func1\(\)$`, + `^ strip\.go:\d+ \+0x[0-9a-f]+$`, + `^panic\({0x[0-9a-f]+, 0x[0-9a-f]+}\)$`, + `^ GOROOT/src/runtime/panic\.go:\d+ \+0x[0-9a-f]+$`, + `^main\.panicAndRecover\(\)$`, + `^ strip\.go:\d+ \+0x[0-9a-f]+$`, + `^main\.main\(\)$`, + `^ strip\.go:\d+ \+0x[0-9a-f]+$`, + `^$`, +} + +func embedWantedStackTraces() string { + buf := &bytes.Buffer{} + fmt.Fprintln(buf, "var wantStackTrace = []string{") + for _, s := range wantStackTrace { + fmt.Fprintf(buf, "`%s`,\n", s) + } + fmt.Fprintln(buf, "}") + return buf.String() +} diff --git a/tests/core/transition/BUILD.bazel b/tests/core/transition/BUILD.bazel new file mode 100644 index 00000000..12fe1504 --- /dev/null +++ b/tests/core/transition/BUILD.bazel @@ -0,0 +1,13 @@ +load("//go/tools/bazel_testing:def.bzl", "go_bazel_test") + +go_bazel_test( + name = "cmdline_test", + size = "medium", + srcs = ["cmdline_test.go"], +) + +go_bazel_test( + name = "hermeticity_test", + size = "medium", + srcs = ["hermeticity_test.go"], +) diff --git a/tests/core/transition/README.rst b/tests/core/transition/README.rst new file mode 100644 index 00000000..5033f7b3 --- /dev/null +++ b/tests/core/transition/README.rst @@ -0,0 +1,18 @@ +Misc configuration transition tests +=================================== + +.. _go_binary: /docs/go/core/rules.md#_go_binary +.. _go_test: /docs/go/core/rules.md#_go_test + +Tests that check that configuration transitions for `go_binary`_ and `go_test`_ +are working correctly. + +Most tests for specific attributes are in other directories, for example, +``c_linkmodes``, ``cross``, ``nogo``, ``race``. This directory covers +transition-related stuff that doesn't fit anywhere else. + +cmdline_test +------------ +Tests that build settings can be set with flags on the command line. The test +builds a target with and without a command line flag and verifies the output +is different. diff --git a/tests/core/transition/cmdline_test.go b/tests/core/transition/cmdline_test.go new file mode 100644 index 00000000..1de0b3f5 --- /dev/null +++ b/tests/core/transition/cmdline_test.go @@ -0,0 +1,84 @@ +// Copyright 2020 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 cmdline_test + +import ( + "bytes" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Main: ` +-- BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_binary") + +go_binary( + name = "maybe_pure", + srcs = [ + "not_pure.go", + "pure.go", + ], +) + +-- not_pure.go -- +// +build cgo + +package main + +import "fmt" + +func main() { + fmt.Println("not pure") +} + +-- pure.go -- +// +build !cgo + +package main + +import "fmt" + +func main() { + fmt.Println("pure") +} +`, + }) +} + +// TestPure checks that the --@io_bazel_rules_go//go/config:pure flag controls +// whether a target is built in pure mode. It doesn't actually require cgo, +// since that doesn't work within go_bazel_test on Windows. +func TestPure(t *testing.T) { + out, err := bazel_testing.BazelOutput("run", "//:maybe_pure") + if err != nil { + t.Fatalf("running //:maybe_pure without flag: %v", err) + } + got := string(bytes.TrimSpace(out)) + if want := "not pure"; got != want { + t.Fatalf("got %q; want %q", got, want) + } + + out, err = bazel_testing.BazelOutput("run", "--@io_bazel_rules_go//go/config:pure", "//:maybe_pure") + if err != nil { + t.Fatalf("running //:maybe_pure with flag: %v", err) + } + got = string(bytes.TrimSpace(out)) + if want := "pure"; got != want { + t.Fatalf("got %q; want %q", got, want) + } +} diff --git a/tests/core/transition/hermeticity_test.go b/tests/core/transition/hermeticity_test.go new file mode 100644 index 00000000..08eda7d6 --- /dev/null +++ b/tests/core/transition/hermeticity_test.go @@ -0,0 +1,289 @@ +// Copyright 2021 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 hermeticity_test + +import ( + "bytes" + "encoding/json" + "fmt" + "strings" + "testing" + + "github.com/bazelbuild/rules_go/go/tools/bazel_testing" +) + +func TestMain(m *testing.M) { + bazel_testing.TestMain(m, bazel_testing.Args{ + Main: ` +-- BUILD.bazel -- +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") +load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") +load("@rules_proto//proto:defs.bzl", "proto_library") + +go_binary( + name = "main", + srcs = [ + "main.go", + ":gen_go", + ], + data = [":helper"], + embedsrcs = [":helper"], + cdeps = [":helper"], + cgo = True, + linkmode = "c-archive", + gotags = ["foo"], + deps = [":lib"], +) + +go_library( + name = "lib", + srcs = [ + "lib.go", + ":gen_indirect_go", + ], + importpath = "example.com/lib", + data = [":indirect_helper"], + embedsrcs = [":indirect_helper"], + cdeps = [":indirect_helper"], + cgo = True, +) + +go_test( + name = "main_test", + srcs = [ + "main.go", + ":gen_go", + ], + data = [":helper"], + embedsrcs = [":helper"], + cdeps = [":helper"], + cgo = True, + linkmode = "c-archive", + gotags = ["foo"], +) + +cc_library( + name = "helper", +) + +cc_library( + name = "indirect_helper", +) + +genrule( + name = "gen_go", + outs = ["gen.go"], + exec_tools = [":helper"], + cmd = "# Not needed for bazel cquery", +) + +genrule( + name = "gen_indirect_go", + outs = ["gen_indirect.go"], + exec_tools = [":indirect_helper"], + cmd = "# Not needed for bazel cquery", +) + +proto_library( + name = "foo_proto", + srcs = ["foo.proto"], +) + +go_proto_library( + name = "foo_go_proto", + importpath = "github.com/bazelbuild/rules_go/tests/core/transition/foo", + proto = ":foo_proto", +) +-- main.go -- +package main + +func main() {} +-- lib.go -- +-- foo.proto -- +syntax = "proto3"; + +package tests.core.transition.foo; +option go_package = "github.com/bazelbuild/rules_go/tests/core/transition/foo"; + +message Foo { + int64 value = 1; +} +`, + WorkspaceSuffix: ` +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +http_archive( + name = "com_google_protobuf", + sha256 = "75be42bd736f4df6d702a0e4e4d30de9ee40eac024c4b845d17ae4cc831fe4ae", + strip_prefix = "protobuf-21.7", + # latest available in BCR, as of 2022-09-30 + urls = [ + "https://github.com/protocolbuffers/protobuf/archive/v21.7.tar.gz", + "https://mirror.bazel.build/github.com/protocolbuffers/protobuf/archive/v21.7.tar.gz", + ], +) + +load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps") + +protobuf_deps() + +http_archive( + name = "rules_proto", + sha256 = "4d421d51f9ecfe9bf96ab23b55c6f2b809cbaf0eea24952683e397decfbd0dd0", + strip_prefix = "rules_proto-f6b8d89b90a7956f6782a4a3609b2f0eee3ce965", + # master, as of 2020-01-06 + urls = [ + "https://mirror.bazel.build/github.com/bazelbuild/rules_proto/archive/f6b8d89b90a7956f6782a4a3609b2f0eee3ce965.tar.gz", + "https://github.com/bazelbuild/rules_proto/archive/f6b8d89b90a7956f6782a4a3609b2f0eee3ce965.tar.gz", + ], +) +`, + }) +} + +func TestGoBinaryNonGoAttrsAreReset(t *testing.T) { + assertDependsCleanlyOnWithFlags( + t, + "//:main", + "//:helper") +} + +func TestGoLibraryNonGoAttrsAreReset(t *testing.T) { + assertDependsCleanlyOnWithFlags( + t, + "//:main", + "//:indirect_helper") +} + +func TestGoTestNonGoAttrsAreReset(t *testing.T) { + assertDependsCleanlyOnWithFlags( + t, + "//:main_test", + "//:helper") +} + +func TestGoProtoLibraryToolAttrsAreReset(t *testing.T) { + assertDependsCleanlyOnWithFlags( + t, + "//:foo_go_proto", + "@com_google_protobuf//:protoc", + "--@io_bazel_rules_go//go/config:static", + "--@io_bazel_rules_go//go/config:msan", + "--@io_bazel_rules_go//go/config:race", + "--@io_bazel_rules_go//go/config:debug", + "--@io_bazel_rules_go//go/config:linkmode=c-archive", + "--@io_bazel_rules_go//go/config:tags=fake_tag", + ) + assertDependsCleanlyOnWithFlags( + t, + "//:foo_go_proto", + "@com_google_protobuf//:protoc", + "--@io_bazel_rules_go//go/config:pure", + ) +} + +func assertDependsCleanlyOnWithFlags(t *testing.T, targetA, targetB string, flags ...string) { + query := fmt.Sprintf("deps(%s) intersect %s", targetA, targetB) + out, err := bazel_testing.BazelOutput(append( + []string{ + "cquery", + "--transitions=full", + "--output=jsonproto", + query, + }, + flags..., + )..., + ) + if err != nil { + t.Fatalf("bazel cquery '%s': %v", query, err) + } + cqueryOut := bytes.TrimSpace(out) + configHashes := extractConfigHashes(t, cqueryOut) + if len(configHashes) != 1 { + differingGoOptions := getGoOptions(t, configHashes...) + if len(differingGoOptions) != 0 { + t.Fatalf( + "%s depends on %s in multiple configs with these differences in rules_go options: %s", + targetA, + targetB, + strings.Join(differingGoOptions, "\n"), + ) + } + } + goOptions := getGoOptions(t, configHashes[0]) + if len(goOptions) != 0 { + t.Fatalf( + "%s depends on %s in a config with rules_go options: %s", + targetA, + targetB, + strings.Join(goOptions, "\n"), + ) + } +} + +func extractConfigHashes(t *testing.T, rawJsonOut []byte) []string { + var jsonOut bazelCqueryOutput + err := json.Unmarshal(rawJsonOut, &jsonOut) + if err != nil { + t.Fatalf("Failed to decode bazel config JSON output %v: %q", err, string(rawJsonOut)) + } + var hashes []string + for _, result := range jsonOut.Results { + hashes = append(hashes, result.Configuration.Checksum) + } + return hashes +} + +func getGoOptions(t *testing.T, hashes ...string) []string { + out, err := bazel_testing.BazelOutput(append([]string{"config", "--output=json"}, hashes...)...) + if err != nil { + t.Fatalf("bazel config %s: %v", strings.Join(hashes, " "), err) + } + rawJsonOut := bytes.TrimSpace(out) + var jsonOut bazelConfigOutput + err = json.Unmarshal(rawJsonOut, &jsonOut) + if err != nil { + t.Fatalf("Failed to decode bazel config JSON output %v: %q", err, string(rawJsonOut)) + } + var differingGoOptions []string + for _, fragment := range jsonOut.Fragments { + if fragment.Name != starlarkOptionsFragment { + continue + } + for key, value := range fragment.Options { + if strings.HasPrefix(key, "@io_bazel_rules_go//") { + differingGoOptions = append(differingGoOptions, fmt.Sprintf("%s=%s", key, value)) + } + } + } + return differingGoOptions +} + +const starlarkOptionsFragment = "user-defined" + +type bazelConfigOutput struct { + Fragments []struct { + Name string `json:"name"` + Options map[string]string `json:"options"` + } `json:"fragmentOptions"` +} + +type bazelCqueryOutput struct { + Results []struct { + Configuration struct { + Checksum string `json:"checksum"` + } `json:"configuration"` + } `json:"results"` +} |