blob: dc98c3e445c67950ede1fbb826a914d972329cdb [file] [log] [blame]
// Copyright 2024 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 com.google.devtools.build.lib.buildtool;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
import com.google.common.collect.ImmutableList;
import com.google.devtools.build.lib.actions.BuildFailedException;
import com.google.devtools.build.lib.analysis.util.AnalysisMock;
import com.google.devtools.build.lib.authandtls.credentialhelper.CredentialModule;
import com.google.devtools.build.lib.buildeventservice.BazelBuildEventServiceModule;
import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos;
import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.BuildEvent;
import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.BuildEventId.IdCase;
import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.TestStatus;
import com.google.devtools.build.lib.buildtool.util.BuildIntegrationTestCase;
import com.google.devtools.build.lib.runtime.BlazeCommandDispatcher;
import com.google.devtools.build.lib.runtime.BlazeRuntime;
import com.google.devtools.build.lib.runtime.NoSpawnCacheModule;
import com.google.devtools.build.lib.testutil.BlazeTestUtils;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.Path;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import javax.annotation.Nullable;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
* Integration test verifying behavior of {@code
* com.google.devtools.build.lib.runtime.TargetSummaryEvent} event.
*/
@RunWith(JUnit4.class)
public class TargetSummaryEventTest extends BuildIntegrationTestCase {
@Rule public final TemporaryFolder tmpFolder = new TemporaryFolder();
@Before
public void stageEmbeddedTools() throws Exception {
AnalysisMock.get().setupMockToolsRepository(mockToolsConfig);
}
@Override
protected BlazeRuntime.Builder getRuntimeBuilder() throws Exception {
return super.getRuntimeBuilder()
.addBlazeModule(new NoSpawnCacheModule())
.addBlazeModule(new CredentialModule())
.addBlazeModule(new BazelBuildEventServiceModule());
}
private void afterBuildCommand() throws Exception {
runtimeWrapper.newCommand();
}
@Test
public void plainTarget_buildSuccess() throws Exception {
write("foo/BUILD", "genrule(name = 'foobin', outs = ['out.txt'], cmd = 'echo -n Hello > $@')");
File bep = buildTargetAndCaptureBuildEventProtocol("//foo:foobin");
BuildEventStreamProtos.TargetSummary summary = findTargetSummaryEventInBuildEventStream(bep);
assertThat(summary.getOverallBuildSuccess()).isTrue();
assertThat(summary.getOverallTestStatus()).isEqualTo(TestStatus.NO_STATUS);
}
@Test
public void plainTarget_buildFails() throws Exception {
write("foo/BUILD", "genrule(name = 'foobin', outs = ['out.txt'], cmd = 'false')");
File bep = buildFailingTargetAndCaptureBuildEventProtocol("//foo:foobin");
BuildEventStreamProtos.TargetSummary summary = findTargetSummaryEventInBuildEventStream(bep);
assertThat(summary.getOverallBuildSuccess()).isFalse();
assertThat(summary.getOverallTestStatus()).isEqualTo(TestStatus.NO_STATUS);
}
@Test
public void test_buildSucceeds_testSucceeds() throws Exception {
write("foo/good_test.sh", "#!/bin/bash", "true").setExecutable(true);
write(
"foo/BUILD",
"load('//test_defs:foo_test.bzl', 'foo_test')",
"foo_test(name = 'good_test', srcs = ['good_test.sh'])");
File bep = testTargetAndCaptureBuildEventProtocol("//foo:good_test");
BuildEventStreamProtos.TargetSummary summary = findTargetSummaryEventInBuildEventStream(bep);
assertThat(summary.getOverallBuildSuccess()).isTrue();
assertThat(summary.getOverallTestStatus()).isEqualTo(TestStatus.PASSED);
}
@Test
public void test_buildSucceeds_testFails() throws Exception {
write("foo/bad_test.sh", "#!/bin/bash", "false").setExecutable(true);
write(
"foo/BUILD",
"load('//test_defs:foo_test.bzl', 'foo_test')",
"foo_test(name = 'bad_test', srcs = ['bad_test.sh'])");
File bep = testTargetAndCaptureBuildEventProtocol("//foo:bad_test");
BuildEventStreamProtos.TargetSummary summary = findTargetSummaryEventInBuildEventStream(bep);
assertThat(summary.getOverallBuildSuccess()).isTrue();
assertThat(summary.getOverallTestStatus()).isEqualTo(TestStatus.FAILED);
}
@Test
public void test_buildSucceeds_testRuntimeFailsToBuild() throws Exception {
write("foo/good_test.sh", "#!/bin/bash", "true").setExecutable(true);
write(
"foo/BUILD",
"load('//test_defs:foo_test.bzl', 'foo_test')",
"foo_test(name = 'good_test', srcs = ['good_test.sh'])");
// Hack: the path to the tools/test/BUILD file is prefixed in the Bazel tests.
String pathToToolsTestBuildPrefix = AnalysisMock.get().isThisBazel() ? "embedded_tools/" : "";
Path toolsTestBuildPath =
mockToolsConfig.getPath(pathToToolsTestBuildPrefix + "tools/test/BUILD");
// Delete the test-setup.sh file and introduce a broken genrule to create test-setup.sh.
mockToolsConfig.getPath(pathToToolsTestBuildPrefix + "tools/test/test-setup.sh").delete();
String bogusTestSetupGenrule =
"""
genrule(
name = 'bogus-make-test-setup',
outs = ['test-setup.sh'],
cmd = 'false',
)
""";
FileSystemUtils.appendIsoLatin1(toolsTestBuildPath, bogusTestSetupGenrule);
File bep = testTargetAndCaptureBuildEventProtocol("//foo:good_test");
BuildEventStreamProtos.TargetSummary summary = findTargetSummaryEventInBuildEventStream(bep);
assertThat(summary.getOverallBuildSuccess()).isTrue();
assertThat(summary.getOverallTestStatus()).isEqualTo(TestStatus.FAILED_TO_BUILD);
}
private File buildTargetAndCaptureBuildEventProtocol(String target) throws Exception {
File bep = tmpFolder.newFile();
// We use WAIT_FOR_UPLOAD_COMPLETE because it's the easiest way to force the BES module to
// wait until the BEP binary file has been written.
addOptions(
"--keep_going",
"--experimental_bep_target_summary",
"--build_event_binary_file=" + bep.getAbsolutePath(),
"--bes_upload_mode=WAIT_FOR_UPLOAD_COMPLETE");
buildTarget(target);
// We need to wait for all events to be written to the file, which is done in #afterCommand()
// if --bes_upload_mode=WAIT_FOR_UPLOAD_COMPLETE.
afterBuildCommand();
return bep;
}
private File buildFailingTargetAndCaptureBuildEventProtocol(String target) throws Exception {
File bep = tmpFolder.newFile();
// We use WAIT_FOR_UPLOAD_COMPLETE because it's the easiest way to force the BES module to
// wait until the BEP binary file has been written.
addOptions(
"--keep_going",
"--experimental_bep_target_summary",
"--build_event_binary_file=" + bep.getAbsolutePath(),
"--bes_upload_mode=WAIT_FOR_UPLOAD_COMPLETE");
assertThrows(BuildFailedException.class, () -> buildTarget(target));
// We need to wait for all events to be written to the file, which is done in #afterCommand()
// if --bes_upload_mode=WAIT_FOR_UPLOAD_COMPLETE.
afterBuildCommand();
return bep;
}
private File testTargetAndCaptureBuildEventProtocol(String target) throws Exception {
File bep = tmpFolder.newFile();
BlazeCommandDispatcher dispatcher = new BlazeCommandDispatcher(getRuntime());
ImmutableList.Builder<String> args = ImmutableList.builder();
args.add("test", target);
args.addAll(getDefaultBlazeTestArguments());
// We use WAIT_FOR_UPLOAD_COMPLETE because it's the easiest way to force the BES module to
// wait until the BEP binary file has been written.
args.add(
"--keep_going",
"--client_env=PATH=/bin:/usr/bin:/usr/sbin:/sbin",
"--experimental_bep_target_summary",
"--build_event_binary_file=" + bep.getAbsolutePath(),
"--bes_upload_mode=WAIT_FOR_UPLOAD_COMPLETE");
dispatcher.exec(args.build(), /* clientDescription= */ "test", outErr);
return bep;
}
protected List<String> getDefaultBlazeTestArguments() {
return BlazeTestUtils.makeArgs("--default_visibility=public", "--test_output=all");
}
private static ImmutableList<BuildEvent> parseBuildEventsFromBuildEventStream(File bep)
throws IOException {
ImmutableList.Builder<BuildEvent> buildEvents = ImmutableList.builder();
try (InputStream in = new FileInputStream(bep)) {
BuildEvent ev;
while ((ev = BuildEvent.parseDelimitedFrom(in)) != null) {
buildEvents.add(ev);
}
}
return buildEvents.build();
}
@Nullable
private static BuildEventStreamProtos.TargetSummary findTargetSummaryEventInBuildEventStream(
File bep) throws IOException {
for (BuildEvent buildEvent : parseBuildEventsFromBuildEventStream(bep)) {
if (buildEvent.getId().getIdCase() == IdCase.TARGET_SUMMARY) {
return buildEvent.getTargetSummary();
}
}
return null;
}
}