// Copyright 2015 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.analysis; import static com.google.common.truth.Truth.assertThat; import static com.google.devtools.build.lib.testutil.MoreAsserts.assertEventCount; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import com.google.common.base.Function; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.common.eventbus.EventBus; import com.google.common.truth.Truth; import com.google.devtools.build.lib.actions.Action; import com.google.devtools.build.lib.actions.Actions; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.actions.FailAction; import com.google.devtools.build.lib.analysis.DependencyResolver.Dependency; import com.google.devtools.build.lib.analysis.config.BuildConfiguration; import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException; import com.google.devtools.build.lib.analysis.util.BuildViewTestBase; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.packages.Aspect; import com.google.devtools.build.lib.packages.Attribute; import com.google.devtools.build.lib.packages.Rule; import com.google.devtools.build.lib.pkgcache.LoadingFailedException; import com.google.devtools.build.lib.skyframe.SkyFunctions; import com.google.devtools.build.lib.skyframe.TargetPatternValue.TargetPatternKey; import com.google.devtools.build.lib.testutil.Suite; import com.google.devtools.build.lib.testutil.TestSpec; import com.google.devtools.build.lib.testutil.TestUtils; import com.google.devtools.build.lib.util.Pair; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.PathFragment; import com.google.devtools.build.skyframe.DeterministicInMemoryGraph; import com.google.devtools.build.skyframe.NotifyingInMemoryGraph; import com.google.devtools.build.skyframe.NotifyingInMemoryGraph.EventType; import com.google.devtools.build.skyframe.NotifyingInMemoryGraph.Listener; import com.google.devtools.build.skyframe.NotifyingInMemoryGraph.Order; import com.google.devtools.build.skyframe.SkyKey; import com.google.devtools.build.skyframe.TrackingAwaiter; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.util.Collection; import java.util.LinkedHashSet; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; /** * Tests for the {@link BuildView}. */ @TestSpec(size = Suite.SMALL_TESTS) @RunWith(JUnit4.class) public final class BuildViewTest extends BuildViewTestBase { @Test public void testRuleConfiguredTarget() throws Exception { scratch.file("pkg/BUILD", "genrule(name='foo', ", " cmd = '',", " srcs=['a.src'],", " outs=['a.out'])"); update("//pkg:foo"); Rule ruleTarget = (Rule) getTarget("//pkg:foo"); assertEquals("genrule", ruleTarget.getRuleClass()); ConfiguredTarget ruleCT = getConfiguredTarget("//pkg:foo"); assertSame(ruleTarget, ruleCT.getTarget()); } @Test public void testFilterByTargets() throws Exception { scratch.file("tests/BUILD", "sh_test(name = 'small_test_1',", " srcs = ['small_test_1.sh'],", " data = [':xUnit'],", " size = 'small',", " tags = ['tag1'])", "", "sh_test(name = 'small_test_2',", " srcs = ['small_test_2.sh'],", " size = 'small',", " tags = ['tag2'])", "", "", "test_suite( name = 'smallTests', tags=['small'])"); //scratch.file("tests/small_test_1.py"); update("//tests:smallTests"); ConfiguredTarget test1 = getConfiguredTarget("//tests:small_test_1"); ConfiguredTarget test2 = getConfiguredTarget("//tests:small_test_2"); ConfiguredTarget suite = getConfiguredTarget("//tests:smallTests"); assertNoEvents(); // start from a clean slate Collection targets = new LinkedHashSet<>(ImmutableList.of(test1, test2, suite)); targets = Lists.newArrayList( BuildView.filterTestsByTargets(targets, Sets.newHashSet(test1.getTarget(), suite.getTarget()))); assertThat(targets).containsExactlyElementsIn(Sets.newHashSet(test1, suite)); } @Test public void testSourceArtifact() throws Exception { setupDummyRule(); update("//pkg:a.src"); InputFileConfiguredTarget inputCT = getInputFileConfiguredTarget("//pkg:a.src"); Artifact inputArtifact = inputCT.getArtifact(); assertNull(getGeneratingAction(inputArtifact)); assertEquals("pkg/a.src", inputArtifact.getExecPathString()); } @Test public void testGeneratedArtifact() throws Exception { setupDummyRule(); update("//pkg:a.out"); OutputFileConfiguredTarget outputCT = (OutputFileConfiguredTarget) getConfiguredTarget("//pkg:a.out"); Artifact outputArtifact = outputCT.getArtifact(); assertEquals(getTargetConfiguration().getBinDirectory(), outputArtifact.getRoot()); assertEquals(getTargetConfiguration().getBinFragment().getRelative("pkg/a.out"), outputArtifact.getExecPath()); assertEquals(new PathFragment("pkg/a.out"), outputArtifact.getRootRelativePath()); Action action = getGeneratingAction(outputArtifact); assertSame(FailAction.class, action.getClass()); } // TODO(bazel-team): this test is bad, it seems to rely on genrule emitting a warning to make the // analysis fail, this needs a proper way to inject errors/warnings @Test @Ignore public void disabled_testReportsAnalysisRootCauses() throws Exception { scratch.file("pkg/BUILD", "genrule(name='foo',", " tools=[:missing],", " outs=['foofile'],", " cmd='')", "genrule(name='bar',", " srcs=['foofile'],", " outs=['barfile'],", " cmd='')"); reporter.removeHandler(failFastHandler); EventBus eventBus = new EventBus(); AnalysisFailureRecorder recorder = new AnalysisFailureRecorder(); eventBus.register(recorder); update(eventBus, defaultFlags().with(Flag.KEEP_GOING), "//pkg:bar"); assertThat(recorder.events).hasSize(1); AnalysisFailureEvent event = recorder.events.get(0); assertEquals("//pkg:foo", event.getFailureReason().toString()); assertEquals("//pkg:bar", event.getFailedTarget().getLabel().toString()); } @Test public void testReportsLoadingRootCauses() throws Exception { scratch.file("pkg/BUILD", "genrule(name='foo',", " tools=['//nopackage:missing'],", " cmd='')"); reporter.removeHandler(failFastHandler); EventBus eventBus = new EventBus(); LoadingFailureRecorder recorder = new LoadingFailureRecorder(); eventBus.register(recorder); // Note: no need to run analysis for a loading failure. update(eventBus, defaultFlags().with(Flag.KEEP_GOING), "//pkg:foo"); assertThat(recorder.events) .contains( Pair.of(Label.parseAbsolute("//pkg:foo"), Label.parseAbsolute("//nopackage:missing"))); assertContainsEvent("missing value for mandatory attribute 'outs'"); assertContainsEvent("no such package 'nopackage'"); // Skyframe correctly reports the other root cause as the genrule itself (since it is // missing attributes). assertThat(recorder.events).hasSize(2); assertThat(recorder.events) .contains(Pair.of(Label.parseAbsolute("//pkg:foo"), Label.parseAbsolute("//pkg:foo"))); } @Test public void testConvolutedLoadRootCauseAnalysis() throws Exception { // You need license declarations in third_party. We use this constraint to // create targets that are loadable, but are in error. scratch.file("third_party/first/BUILD", "sh_library(name='first', deps=['//third_party/second'], licenses=['notice'])"); scratch.file("third_party/second/BUILD", "sh_library(name='second', deps=['//third_party/third'], licenses=['notice'])"); scratch.file("third_party/third/BUILD", "sh_library(name='third', deps=['//third_party/fourth'], licenses=['notice'])"); scratch.file("third_party/fourth/BUILD", "sh_library(name='fourth', deps=['//third_party/fifth'])"); scratch.file("third_party/fifth/BUILD", "sh_library(name='fifth', licenses=['notice'])"); reporter.removeHandler(failFastHandler); EventBus eventBus = new EventBus(); LoadingFailureRecorder recorder = new LoadingFailureRecorder(); eventBus.register(recorder); // Note: no need to run analysis for a loading failure. update(eventBus, defaultFlags().with(Flag.KEEP_GOING), "//third_party/first", "//third_party/third"); assertThat(recorder.events).hasSize(2); assertTrue(recorder.events.toString(), recorder.events.contains( Pair.of(Label.parseAbsolute("//third_party/first"), Label.parseAbsolute("//third_party/fourth")))); assertThat(recorder.events) .contains(Pair.of( Label.parseAbsolute("//third_party/third"), Label.parseAbsolute("//third_party/fourth"))); } @Test public void testMultipleRootCauseReporting() throws Exception { scratch.file("gp/BUILD", "sh_library(name = 'gp', deps = ['//p:p'])"); scratch.file("p/BUILD", "sh_library(name = 'p', deps = ['//c1:not', '//c2:not'])"); scratch.file("c1/BUILD"); scratch.file("c2/BUILD"); reporter.removeHandler(failFastHandler); EventBus eventBus = new EventBus(); LoadingFailureRecorder recorder = new LoadingFailureRecorder(); eventBus.register(recorder); update(eventBus, defaultFlags().with(Flag.KEEP_GOING), "//gp"); assertThat(recorder.events).hasSize(2); assertTrue(recorder.events.toString(), recorder.events.contains( Pair.of(Label.parseAbsolute("//gp"), Label.parseAbsolute("//c1:not")))); assertThat(recorder.events) .contains(Pair.of(Label.parseAbsolute("//gp"), Label.parseAbsolute("//c2:not"))); } /** * Regression test for: "Package group includes are broken" */ @Test public void testTopLevelPackageGroup() throws Exception { scratch.file("tropical/BUILD", "package_group(name='guava', includes=[':mango'])", "package_group(name='mango')"); // If the analysis phase results in an error, this will throw an exception update("//tropical:guava"); // Check if the included package group also got analyzed assertNotNull(getConfiguredTarget("//tropical:mango", null)); } @Test public void testTopLevelInputFile() throws Exception { scratch.file("tropical/BUILD", "exports_files(['file.txt'])"); update("//tropical:file.txt"); assertNotNull(getConfiguredTarget("//tropical:file.txt", null)); } @Test public void testGetDirectPrerequisites() throws Exception { scratch.file( "package/BUILD", "filegroup(name='top', srcs=[':inner', 'file'])", "sh_binary(name='inner', srcs=['script.sh'])"); update("//package:top"); ConfiguredTarget top = getConfiguredTarget("//package:top", getTargetConfiguration()); Iterable targets = getView().getDirectPrerequisitesForTesting( reporter, top, getBuildConfigurationCollection()); Iterable