1// Copyright 2022 The Bazel Authors. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package lcov_coverage_test 16 17import ( 18 "io/ioutil" 19 "path/filepath" 20 "strings" 21 "testing" 22 23 "github.com/bazelbuild/rules_go/go/tools/bazel_testing" 24) 25 26func TestMain(m *testing.M) { 27 bazel_testing.TestMain(m, bazel_testing.Args{ 28 Main: ` 29-- src/BUILD.bazel -- 30load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") 31 32go_library( 33 name = "lib", 34 srcs = ["lib.go"], 35 importpath = "example.com/lib", 36 deps = [":other_lib"], 37) 38 39go_library( 40 name = "other_lib", 41 srcs = ["other_lib.go"], 42 importpath = "example.com/other_lib", 43) 44 45go_test( 46 name = "lib_test", 47 srcs = ["lib_test.go"], 48 deps = [":lib"], 49) 50 51java_binary( 52 name = "Tool", 53 srcs = ["Tool.java"], 54) 55 56go_test( 57 name = "lib_with_tool_test", 58 srcs = ["lib_with_tool_test.go"], 59 data = [":Tool"], 60 deps = [":lib"], 61) 62-- src/lib.go -- 63package lib 64 65import ( 66 "strings" 67 68 "example.com/other_lib" 69) 70 71func HelloFromLib(informal bool) string { 72 var greetings []string 73 if informal { 74 greetings = []string{"Hey there, other_lib!"} 75 } else { 76 greetings = []string{"Good morning, other_lib!"} 77 } 78 greetings = append(greetings, other_lib.HelloOtherLib(informal)) 79 return strings.Join(greetings, "\n") 80} 81-- src/other_lib.go -- 82package other_lib 83 84func HelloOtherLib(informal bool) string { 85 if informal { 86 return "Hey there, other_lib!" 87 } 88 return "Good morning, other_lib!" 89} 90-- src/lib_test.go -- 91package lib_test 92 93import ( 94 "strings" 95 "testing" 96 97 "example.com/lib" 98) 99 100func TestLib(t *testing.T) { 101 if !strings.Contains(lib.HelloFromLib(false), "\n") { 102 t.Error("Expected a newline in the output") 103 } 104} 105-- src/Tool.java -- 106public class Tool { 107 public static void main(String[] args) { 108 if (args.length != 0) { 109 System.err.println("Expected no arguments"); 110 System.exit(1); 111 } 112 System.err.println("Hello, world!"); 113 } 114} 115-- src/lib_with_tool_test.go -- 116package lib_test 117 118import ( 119 "os/exec" 120 "path/filepath" 121 "strings" 122 "testing" 123 124 "example.com/lib" 125) 126 127func TestLib(t *testing.T) { 128 if !strings.Contains(lib.HelloFromLib(false), "\n") { 129 t.Error("Expected a newline in the output") 130 } 131} 132 133func TestTool(t *testing.T) { 134 err := exec.Command("." + string(filepath.Separator) + "Tool").Run() 135 if err != nil { 136 t.Error(err) 137 } 138} 139 140`, 141 }) 142} 143 144func TestLcovCoverage(t *testing.T) { 145 t.Run("without-race", func(t *testing.T) { 146 testLcovCoverage(t) 147 }) 148 149 t.Run("with-race", func(t *testing.T) { 150 testLcovCoverage(t, "--@io_bazel_rules_go//go/config:race") 151 }) 152} 153 154func testLcovCoverage(t *testing.T, extraArgs ...string) { 155 args := append([]string{ 156 "coverage", 157 "--combined_report=lcov", 158 "//src:lib_test", 159 }, extraArgs...) 160 161 if err := bazel_testing.RunBazel(args...); err != nil { 162 t.Fatal(err) 163 } 164 165 individualCoveragePath := filepath.FromSlash("bazel-testlogs/src/lib_test/coverage.dat") 166 individualCoverageData, err := ioutil.ReadFile(individualCoveragePath) 167 if err != nil { 168 t.Fatal(err) 169 } 170 for _, expectedIndividualCoverage := range expectedGoCoverage { 171 if !strings.Contains(string(individualCoverageData), expectedIndividualCoverage) { 172 t.Errorf( 173 "%s: does not contain:\n\n%s\nactual content:\n\n%s", 174 individualCoveragePath, 175 expectedIndividualCoverage, 176 string(individualCoverageData), 177 ) 178 } 179 } 180 181 combinedCoveragePath := filepath.FromSlash("bazel-out/_coverage/_coverage_report.dat") 182 combinedCoverageData, err := ioutil.ReadFile(combinedCoveragePath) 183 if err != nil { 184 t.Fatal(err) 185 } 186 for _, include := range []string{ 187 "SF:src/lib.go\n", 188 "SF:src/other_lib.go\n", 189 } { 190 if !strings.Contains(string(combinedCoverageData), include) { 191 t.Errorf("%s: does not contain %q\n", combinedCoverageData, include) 192 } 193 } 194} 195 196func TestLcovCoverageWithTool(t *testing.T) { 197 args := append([]string{ 198 "coverage", 199 "--combined_report=lcov", 200 "//src:lib_with_tool_test", 201 }) 202 203 if err := bazel_testing.RunBazel(args...); err != nil { 204 t.Fatal(err) 205 } 206 207 individualCoveragePath := filepath.FromSlash("bazel-testlogs/src/lib_with_tool_test/coverage.dat") 208 individualCoverageData, err := ioutil.ReadFile(individualCoveragePath) 209 if err != nil { 210 t.Fatal(err) 211 } 212 expectedCoverage := append(expectedGoCoverage, expectedToolCoverage) 213 for _, expected := range expectedCoverage { 214 if !strings.Contains(string(individualCoverageData), expected) { 215 t.Errorf( 216 "%s: does not contain:\n\n%s\nactual content:\n\n%s", 217 individualCoveragePath, 218 expected, 219 string(individualCoverageData), 220 ) 221 } 222 } 223 224 combinedCoveragePath := filepath.FromSlash("bazel-out/_coverage/_coverage_report.dat") 225 combinedCoverageData, err := ioutil.ReadFile(combinedCoveragePath) 226 if err != nil { 227 t.Fatal(err) 228 } 229 for _, include := range []string{ 230 "SF:src/lib.go\n", 231 "SF:src/other_lib.go\n", 232 "SF:src/Tool.java\n", 233 } { 234 if !strings.Contains(string(combinedCoverageData), include) { 235 t.Errorf("%s: does not contain %q\n", combinedCoverageData, include) 236 } 237 } 238} 239 240var expectedGoCoverage = []string{ 241 `SF:src/other_lib.go 242FNF:0 243FNH:0 244DA:3,1 245DA:4,1 246DA:5,0 247DA:6,0 248DA:7,1 249LH:3 250LF:5 251end_of_record 252`, 253 `SF:src/lib.go 254FNF:0 255FNH:0 256DA:9,1 257DA:10,1 258DA:11,1 259DA:12,0 260DA:13,1 261DA:14,1 262DA:15,1 263DA:16,1 264DA:17,1 265LH:8 266LF:9 267end_of_record 268`} 269 270const expectedToolCoverage = `SF:src/Tool.java 271FN:1,Tool::<init> ()V 272FN:3,Tool::main ([Ljava/lang/String;)V 273FNDA:0,Tool::<init> ()V 274FNDA:1,Tool::main ([Ljava/lang/String;)V 275FNF:2 276FNH:1 277BRDA:3,0,0,1 278BRDA:3,0,1,0 279BRF:2 280BRH:1 281DA:1,0 282DA:3,1 283DA:4,0 284DA:5,0 285DA:7,1 286DA:8,1 287LH:3 288LF:6 289end_of_record 290` 291