/* * Copyright 2014-present Facebook, Inc. * * 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.facebook.buck.cxx; import static org.hamcrest.Matchers.hasItem; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import com.facebook.buck.cli.FakeBuckConfig; import com.facebook.buck.io.ProjectFilesystem; import com.facebook.buck.model.BuildTarget; import com.facebook.buck.model.BuildTargetFactory; import com.facebook.buck.parser.NoSuchBuildTargetException; import com.facebook.buck.rules.BuildRule; import com.facebook.buck.rules.BuildRuleResolver; import com.facebook.buck.rules.BuildRules; import com.facebook.buck.rules.DefaultTargetNodeToBuildRuleTransformer; import com.facebook.buck.rules.RuleKey; import com.facebook.buck.rules.SourcePathResolver; import com.facebook.buck.rules.SourcePathRuleFinder; import com.facebook.buck.rules.TargetGraph; import com.facebook.buck.rules.TargetNode; import com.facebook.buck.rules.TestRule; import com.facebook.buck.rules.args.Arg; import com.facebook.buck.rules.coercer.PatternMatchedCollection; import com.facebook.buck.rules.keys.DefaultRuleKeyFactory; import com.facebook.buck.rules.macros.LocationMacro; import com.facebook.buck.rules.macros.StringWithMacros; import com.facebook.buck.rules.macros.StringWithMacrosUtils; import com.facebook.buck.shell.Genrule; import com.facebook.buck.shell.GenruleBuilder; import com.facebook.buck.step.Step; import com.facebook.buck.step.TestExecutionContext; import com.facebook.buck.test.TestRunningOptions; import com.facebook.buck.test.selectors.TestSelectorList; import com.facebook.buck.testutil.FakeProjectFilesystem; import com.facebook.buck.testutil.TargetGraphFactory; import com.facebook.buck.util.cache.DefaultFileHashCache; import com.facebook.buck.util.cache.FileHashCache; import com.facebook.buck.util.cache.StackedFileHashCache; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.Iterables; import java.nio.file.Path; import java.util.Collection; import java.util.Optional; import java.util.regex.Pattern; import org.hamcrest.Matchers; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @RunWith(Parameterized.class) public class CxxTestDescriptionTest { @Parameterized.Parameters(name = "sandbox_sources={0}") public static Collection<Object[]> data() { return ImmutableList.of(new Object[] {false}, new Object[] {true}); } private final ImmutableMap<String, ImmutableMap<String, String>> rawConfig; private final CxxBuckConfig cxxBuckConfig; public CxxTestDescriptionTest(boolean sandboxSources) { this.rawConfig = ImmutableMap.of( "cxx", ImmutableMap.of("sandbox_sources", Boolean.toString(sandboxSources))); this.cxxBuckConfig = new CxxBuckConfig(FakeBuckConfig.builder().setSections(rawConfig).build()); } private void addSandbox( BuildRuleResolver resolver, ProjectFilesystem filesystem, BuildTarget libTarget) throws NoSuchBuildTargetException { BuildTarget target = BuildTarget.builder(libTarget) .addFlavors(CxxLibraryDescription.Type.SANDBOX_TREE.getFlavor()) .build(); createTestBuilder(target.toString()).build(resolver, filesystem); } private void addFramework(BuildRuleResolver resolver, ProjectFilesystem filesystem) throws NoSuchBuildTargetException { GenruleBuilder.newGenruleBuilder(BuildTargetFactory.newInstance("//:framework_rule")) .setOut("out") .build(resolver, filesystem); } private CxxTestBuilder createTestBuilder() throws NoSuchBuildTargetException { return createTestBuilder("//:test"); } private CxxTestBuilder createTestBuilder(String target) throws NoSuchBuildTargetException { return new CxxTestBuilder( BuildTargetFactory.newInstance(target), cxxBuckConfig, CxxPlatformUtils.DEFAULT_PLATFORM, CxxTestUtils.createDefaultPlatforms()); } @Test public void findDepsFromParams() { BuildTarget gtest = BuildTargetFactory.newInstance("//:gtest"); BuildTarget gtestMain = BuildTargetFactory.newInstance("//:gtest_main"); CxxBuckConfig cxxBuckConfig = new CxxBuckConfig( FakeBuckConfig.builder() .setSections(rawConfig) .setSections( ImmutableMap.of( "cxx", ImmutableMap.of( "gtest_dep", gtest.toString(), "gtest_default_test_main_dep", gtestMain.toString()))) .build()); BuildTarget target = BuildTargetFactory.newInstance("//:target"); CxxTestBuilder builder = new CxxTestBuilder(target, cxxBuckConfig) .setFramework(CxxTestType.GTEST) .setUseDefaultTestMain(true); ImmutableSortedSet<BuildTarget> implicit = builder.findImplicitDeps(); assertThat(implicit, hasItem(gtest)); assertThat(implicit, hasItem(gtestMain)); } @Test public void environmentIsPropagated() throws Exception { ProjectFilesystem filesystem = new FakeProjectFilesystem(); BuildRuleResolver resolver = new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer()); SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver)); addFramework(resolver, filesystem); BuildRule someRule = GenruleBuilder.newGenruleBuilder(BuildTargetFactory.newInstance("//:some_rule")) .setOut("someRule") .build(resolver); CxxTestBuilder builder = createTestBuilder().setEnv(ImmutableMap.of("TEST", "value $(location //:some_rule)")); addSandbox(resolver, filesystem, builder.getTarget()); CxxTest cxxTest = builder.build(resolver); TestRunningOptions options = TestRunningOptions.builder().setTestSelectorList(TestSelectorList.empty()).build(); ImmutableList<Step> steps = cxxTest.runTests( TestExecutionContext.newInstance(), options, pathResolver, TestRule.NOOP_REPORTING_CALLBACK); CxxTestStep testStep = (CxxTestStep) Iterables.getLast(steps); assertThat( testStep.getEnv(), Matchers.equalTo( Optional.of( ImmutableMap.of( "TEST", "value " + pathResolver.getAbsolutePath( Preconditions.checkNotNull(someRule.getSourcePathToOutput())))))); } @Test public void testArgsArePropagated() throws Exception { ProjectFilesystem filesystem = new FakeProjectFilesystem(); BuildRuleResolver resolver = new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer()); SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver)); addFramework(resolver, filesystem); BuildRule someRule = GenruleBuilder.newGenruleBuilder(BuildTargetFactory.newInstance("//:some_rule")) .setOut("someRule") .build(resolver); CxxTestBuilder builder = createTestBuilder().setArgs(ImmutableList.of("value $(location //:some_rule)")); addSandbox(resolver, filesystem, builder.getTarget()); CxxTest cxxTest = builder.build(resolver); TestRunningOptions testOptions = TestRunningOptions.builder() .setShufflingTests(false) .setTestSelectorList(TestSelectorList.empty()) .build(); ImmutableList<Step> steps = cxxTest.runTests( TestExecutionContext.newInstance(), testOptions, pathResolver, TestRule.NOOP_REPORTING_CALLBACK); CxxTestStep testStep = (CxxTestStep) Iterables.getLast(steps); assertThat( testStep.getCommand(), hasItem( "value " + pathResolver.getAbsolutePath( Preconditions.checkNotNull(someRule.getSourcePathToOutput())))); } @Test public void runTestSeparately() throws Exception { for (CxxTestType framework : CxxTestType.values()) { ProjectFilesystem filesystem = new FakeProjectFilesystem(); BuildRuleResolver resolver = new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer()); addFramework(resolver, filesystem); CxxTestBuilder builder = createTestBuilder() .setRunTestSeparately(true) .setUseDefaultTestMain(true) .setFramework(framework); addSandbox(resolver, filesystem, builder.getTarget()); CxxTest cxxTest = builder.build(resolver); assertTrue(cxxTest.runTestSeparately()); } } @Test public void runtimeDepOnDeps() throws Exception { ProjectFilesystem filesystem = new FakeProjectFilesystem(); BuildTarget cxxBinaryTarget = BuildTargetFactory.newInstance("//:dep"); BuildTarget cxxLibraryTarget = BuildTargetFactory.newInstance("//:lib"); CxxBinaryBuilder cxxBinaryBuilder = new CxxBinaryBuilder(cxxBinaryTarget); CxxLibraryBuilder cxxLibraryBuilder = new CxxLibraryBuilder(cxxLibraryTarget).setDeps(ImmutableSortedSet.of(cxxBinaryTarget)); BuildRuleResolver resolver = new BuildRuleResolver( TargetGraphFactory.newInstance(cxxLibraryBuilder.build(), cxxBinaryBuilder.build()), new DefaultTargetNodeToBuildRuleTransformer()); addFramework(resolver, filesystem); BuildRule cxxBinary = cxxBinaryBuilder.build(resolver, filesystem); cxxLibraryBuilder.build(resolver, filesystem); CxxTestBuilder cxxTestBuilder = createTestBuilder().setDeps(ImmutableSortedSet.of(cxxLibraryTarget)); addSandbox(resolver, filesystem, cxxTestBuilder.getTarget()); CxxTest cxxTest = cxxTestBuilder.build(resolver, filesystem); assertThat( BuildRules.getTransitiveRuntimeDeps(cxxTest, resolver), hasItem(cxxBinary.getBuildTarget())); } @Test public void locationMacro() throws Exception { ProjectFilesystem filesystem = new FakeProjectFilesystem(); BuildRuleResolver resolver = new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer()); SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver)); Genrule dep = GenruleBuilder.newGenruleBuilder(BuildTargetFactory.newInstance("//:dep")) .setOut("out") .build(resolver); CxxTestBuilder builder = createTestBuilder() .setLinkerFlags( ImmutableList.of( StringWithMacrosUtils.format( "--linker-script=%s", LocationMacro.of(dep.getBuildTarget())))); addFramework(resolver, filesystem); addSandbox(resolver, filesystem, builder.getTarget()); assertThat(builder.build().getExtraDeps(), hasItem(dep.getBuildTarget())); CxxTest test = builder.build(resolver); CxxLink binary = (CxxLink) resolver.getRule( CxxDescriptionEnhancer.createCxxLinkTarget( test.getBuildTarget(), Optional.empty())); assertThat( Arg.stringify(binary.getArgs(), pathResolver), hasItem(String.format("--linker-script=%s", dep.getAbsoluteOutputFilePath(pathResolver)))); assertThat(binary.getBuildDeps(), hasItem(dep)); } @Test public void linkerFlagsLocationMacro() throws Exception { BuildRuleResolver resolver = new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer()); SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver)); Genrule dep = GenruleBuilder.newGenruleBuilder(BuildTargetFactory.newInstance("//:dep")) .setOut("out") .build(resolver); CxxTestBuilder builder = createTestBuilder("//:rule") .setLinkerFlags( ImmutableList.of( StringWithMacrosUtils.format( "--linker-script=%s", LocationMacro.of(dep.getBuildTarget())))); assertThat(builder.build().getExtraDeps(), hasItem(dep.getBuildTarget())); FakeProjectFilesystem filesystem = new FakeProjectFilesystem(); addFramework(resolver, filesystem); addSandbox(resolver, filesystem, builder.getTarget()); CxxTest test = builder.build(resolver); CxxLink binary = (CxxLink) resolver.getRule( CxxDescriptionEnhancer.createCxxLinkTarget( test.getBuildTarget(), Optional.empty())); assertThat(binary, Matchers.instanceOf(CxxLink.class)); assertThat( Arg.stringify(binary.getArgs(), pathResolver), hasItem(String.format("--linker-script=%s", dep.getAbsoluteOutputFilePath(pathResolver)))); assertThat(binary.getBuildDeps(), hasItem(dep)); } @Test public void platformLinkerFlagsLocationMacroWithMatch() throws Exception { FakeProjectFilesystem filesystem = new FakeProjectFilesystem(); BuildRuleResolver resolver = new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer()); SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver)); Genrule dep = GenruleBuilder.newGenruleBuilder(BuildTargetFactory.newInstance("//:dep")) .setOut("out") .build(resolver); CxxTestBuilder builder = createTestBuilder() .setPlatformLinkerFlags( new PatternMatchedCollection.Builder<ImmutableList<StringWithMacros>>() .add( Pattern.compile( Pattern.quote( CxxPlatformUtils.DEFAULT_PLATFORM.getFlavor().toString())), ImmutableList.of( StringWithMacrosUtils.format( "--linker-script=%s", LocationMacro.of(dep.getBuildTarget())))) .build()); addFramework(resolver, filesystem); addSandbox(resolver, filesystem, builder.getTarget()); assertThat(builder.build().getExtraDeps(), hasItem(dep.getBuildTarget())); CxxTest test = builder.build(resolver); CxxLink binary = (CxxLink) resolver.getRule( CxxDescriptionEnhancer.createCxxLinkTarget( test.getBuildTarget(), Optional.empty())); assertThat( Arg.stringify(binary.getArgs(), pathResolver), hasItem(String.format("--linker-script=%s", dep.getAbsoluteOutputFilePath(pathResolver)))); assertThat(binary.getBuildDeps(), hasItem(dep)); } @Test public void platformLinkerFlagsLocationMacroWithoutMatch() throws Exception { ProjectFilesystem filesystem = new FakeProjectFilesystem(); BuildRuleResolver resolver = new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer()); SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver)); Genrule dep = GenruleBuilder.newGenruleBuilder(BuildTargetFactory.newInstance("//:dep")) .setOut("out") .build(resolver); addFramework(resolver, filesystem); CxxTestBuilder builder = createTestBuilder() .setPlatformLinkerFlags( new PatternMatchedCollection.Builder<ImmutableList<StringWithMacros>>() .add( Pattern.compile("nothing matches this string"), ImmutableList.of( StringWithMacrosUtils.format( "--linker-script=%s", LocationMacro.of(dep.getBuildTarget())))) .build()); assertThat(builder.build().getExtraDeps(), hasItem(dep.getBuildTarget())); addSandbox(resolver, filesystem, builder.getTarget()); CxxTest test = builder.build(resolver); CxxLink binary = (CxxLink) resolver.getRule( CxxDescriptionEnhancer.createCxxLinkTarget( test.getBuildTarget(), Optional.empty())); assertThat( Arg.stringify(binary.getArgs(), pathResolver), Matchers.not( hasItem( String.format("--linker-script=%s", dep.getAbsoluteOutputFilePath(pathResolver))))); assertThat(binary.getBuildDeps(), Matchers.not(hasItem(dep))); } @Test public void resourcesAffectRuleKey() throws Exception { ProjectFilesystem filesystem = new FakeProjectFilesystem(); Path resource = filesystem.getPath("resource"); filesystem.touch(resource); for (CxxTestType framework : CxxTestType.values()) { // Create a test rule without resources attached. BuildRuleResolver resolver = new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer()); addFramework(resolver, filesystem); CxxTestBuilder builder = createTestBuilder().setFramework(framework); addSandbox(resolver, filesystem, builder.getTarget()); CxxTest cxxTestWithoutResources = builder.build(resolver, filesystem); RuleKey ruleKeyWithoutResource = getRuleKey(resolver, cxxTestWithoutResources); // Create a rule with a resource attached. resolver = new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer()); addFramework(resolver, filesystem); builder = createTestBuilder().setFramework(framework).setResources(ImmutableSortedSet.of(resource)); addSandbox(resolver, filesystem, builder.getTarget()); CxxTest cxxTestWithResources = builder.build(resolver, filesystem); RuleKey ruleKeyWithResource = getRuleKey(resolver, cxxTestWithResources); // Verify that their rule keys are different. assertThat(ruleKeyWithoutResource, Matchers.not(Matchers.equalTo(ruleKeyWithResource))); } } @Test public void resourcesAreInputs() throws Exception { ProjectFilesystem filesystem = new FakeProjectFilesystem(); Path resource = filesystem.getPath("resource"); filesystem.touch(resource); for (CxxTestType framework : CxxTestType.values()) { TargetNode<?, ?> cxxTestWithResources = createTestBuilder() .setFramework(framework) .setResources(ImmutableSortedSet.of(resource)) .build(); assertThat(cxxTestWithResources.getInputs(), hasItem(resource)); } } private RuleKey getRuleKey(BuildRuleResolver resolver, BuildRule rule) { SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(resolver); SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder); FileHashCache fileHashCache = new StackedFileHashCache( ImmutableList.of( DefaultFileHashCache.createDefaultFileHashCache(rule.getProjectFilesystem()))); DefaultRuleKeyFactory factory = new DefaultRuleKeyFactory(0, fileHashCache, pathResolver, ruleFinder); return factory.build(rule); } }