// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "gn/target.h"

#include <memory>
#include <utility>

#include "gn/build_settings.h"
#include "gn/config.h"
#include "gn/resolved_target_data.h"
#include "gn/scheduler.h"
#include "gn/settings.h"
#include "gn/test_with_scheduler.h"
#include "gn/test_with_scope.h"
#include "gn/toolchain.h"
#include "util/test/test.h"

namespace {

// Asserts that the current global scheduler has a single unknown generated
// file with the given name from the given target.
void AssertSchedulerHasOneUnknownFileMatching(const Target* target,
                                              const SourceFile& file) {
  auto unknown = g_scheduler->GetUnknownGeneratedInputs();
  ASSERT_EQ(1u, unknown.size());  // Should be one unknown file.
  auto found = unknown.find(file);
  ASSERT_TRUE(found != unknown.end()) << file.value();
  EXPECT_TRUE(target == found->second)
      << "Target doesn't match. Expected\n  "
      << target->label().GetUserVisibleName(false) << "\nBut got\n  "
      << found->second->label().GetUserVisibleName(false);
}

}  // namespace

using TargetTest = TestWithScheduler;

// Test all_dependent_configs and public_config inheritance.
TEST_F(TargetTest, DependentConfigs) {
  TestWithScope setup;
  Err err;

  // Set up a dependency chain of a -> b -> c
  TestTarget a(setup, "//foo:a", Target::EXECUTABLE);
  TestTarget b(setup, "//foo:b", Target::STATIC_LIBRARY);
  TestTarget c(setup, "//foo:c", Target::STATIC_LIBRARY);
  a.private_deps().push_back(LabelTargetPair(&b));
  b.private_deps().push_back(LabelTargetPair(&c));

  // Normal non-inherited config.
  Config config(setup.settings(), Label(SourceDir("//foo/"), "config"));
  config.visibility().SetPublic();
  ASSERT_TRUE(config.OnResolved(&err));
  c.configs().push_back(LabelConfigPair(&config));

  // All dependent config.
  Config all(setup.settings(), Label(SourceDir("//foo/"), "all"));
  all.visibility().SetPublic();
  ASSERT_TRUE(all.OnResolved(&err));
  c.all_dependent_configs().push_back(LabelConfigPair(&all));

  // Direct dependent config.
  Config direct(setup.settings(), Label(SourceDir("//foo/"), "direct"));
  direct.visibility().SetPublic();
  ASSERT_TRUE(direct.OnResolved(&err));
  c.public_configs().push_back(LabelConfigPair(&direct));

  ASSERT_TRUE(c.OnResolved(&err));
  ASSERT_TRUE(b.OnResolved(&err));
  ASSERT_TRUE(a.OnResolved(&err));

  // B should have gotten both dependent configs from C.
  ASSERT_EQ(2u, b.configs().size());
  EXPECT_EQ(&all, b.configs()[0].ptr);
  EXPECT_EQ(&direct, b.configs()[1].ptr);
  ASSERT_EQ(1u, b.all_dependent_configs().size());
  EXPECT_EQ(&all, b.all_dependent_configs()[0].ptr);

  // A should have just gotten the "all" dependent config from C.
  ASSERT_EQ(1u, a.configs().size());
  EXPECT_EQ(&all, a.configs()[0].ptr);
  EXPECT_EQ(&all, a.all_dependent_configs()[0].ptr);

  // Making an an alternate A and B with B forwarding the direct dependents.
  TestTarget a_fwd(setup, "//foo:a_fwd", Target::EXECUTABLE);
  TestTarget b_fwd(setup, "//foo:b_fwd", Target::STATIC_LIBRARY);
  a_fwd.private_deps().push_back(LabelTargetPair(&b_fwd));
  b_fwd.private_deps().push_back(LabelTargetPair(&c));

  ASSERT_TRUE(b_fwd.OnResolved(&err));
  ASSERT_TRUE(a_fwd.OnResolved(&err));

  // A_fwd should now have both configs.
  ASSERT_EQ(1u, a_fwd.configs().size());
  EXPECT_EQ(&all, a_fwd.configs()[0].ptr);
  ASSERT_EQ(1u, a_fwd.all_dependent_configs().size());
  EXPECT_EQ(&all, a_fwd.all_dependent_configs()[0].ptr);
}

// Tests that dependent configs don't propagate between toolchains.
TEST_F(TargetTest, NoDependentConfigsBetweenToolchains) {
  TestWithScope setup;
  Err err;

  // Create another toolchain.
  Toolchain other_toolchain(setup.settings(),
                            Label(SourceDir("//other/"), "toolchain"));
  TestWithScope::SetupToolchain(&other_toolchain);

  // Set up a dependency chain of |a| -> |b| -> |c| where |a| has a different
  // toolchain.
  Target a(setup.settings(),
           Label(SourceDir("//foo/"), "a", other_toolchain.label().dir(),
                 other_toolchain.label().name()));
  a.set_output_type(Target::EXECUTABLE);
  EXPECT_TRUE(a.SetToolchain(&other_toolchain, &err));
  TestTarget b(setup, "//foo:b", Target::EXECUTABLE);
  TestTarget c(setup, "//foo:c", Target::SOURCE_SET);
  a.private_deps().push_back(LabelTargetPair(&b));
  b.private_deps().push_back(LabelTargetPair(&c));

  // All dependent config.
  Config all_dependent(setup.settings(), Label(SourceDir("//foo/"), "all"));
  all_dependent.visibility().SetPublic();
  ASSERT_TRUE(all_dependent.OnResolved(&err));
  c.all_dependent_configs().push_back(LabelConfigPair(&all_dependent));

  // Public config.
  Config public_config(setup.settings(), Label(SourceDir("//foo/"), "public"));
  public_config.visibility().SetPublic();
  ASSERT_TRUE(public_config.OnResolved(&err));
  c.public_configs().push_back(LabelConfigPair(&public_config));

  // Another public config.
  Config public_config2(setup.settings(),
                        Label(SourceDir("//foo/"), "public2"));
  public_config2.visibility().SetPublic();
  ASSERT_TRUE(public_config2.OnResolved(&err));
  b.public_configs().push_back(LabelConfigPair(&public_config2));

  ASSERT_TRUE(c.OnResolved(&err));
  ASSERT_TRUE(b.OnResolved(&err));
  ASSERT_TRUE(a.OnResolved(&err));

  // B should have gotten the configs from C.
  ASSERT_EQ(3u, b.configs().size());
  EXPECT_EQ(&public_config2, b.configs()[0].ptr);
  EXPECT_EQ(&all_dependent, b.configs()[1].ptr);
  EXPECT_EQ(&public_config, b.configs()[2].ptr);
  ASSERT_EQ(1u, b.all_dependent_configs().size());
  EXPECT_EQ(&all_dependent, b.all_dependent_configs()[0].ptr);

  // A should not have gotten any configs from B or C.
  ASSERT_EQ(0u, a.configs().size());
  ASSERT_EQ(0u, a.all_dependent_configs().size());
}

// Tests that dependent configs propagate between toolchains if
// propagates_configs is set.
TEST_F(TargetTest, DependentConfigsBetweenToolchainsWhenSet) {
  TestWithScope setup;
  Err err;

  // Create another toolchain.
  Toolchain other_toolchain(setup.settings(),
                            Label(SourceDir("//other/"), "toolchain"));
  TestWithScope::SetupToolchain(&other_toolchain);
  other_toolchain.set_propagates_configs(true);

  // Set up a dependency chain of |a| -> |b| where |b| has a different
  // toolchain (with propagate_configs set).
  TestTarget a(setup, "//foo:a", Target::EXECUTABLE);
  Target b(setup.settings(),
           Label(SourceDir("//foo/"), "b", other_toolchain.label().dir(),
                 other_toolchain.label().name()));
  b.visibility().SetPublic();
  b.set_output_type(Target::SHARED_LIBRARY);
  EXPECT_TRUE(b.SetToolchain(&other_toolchain, &err));
  a.private_deps().push_back(LabelTargetPair(&b));

  // All dependent config.
  Config all_dependent(setup.settings(), Label(SourceDir("//foo/"), "all"));
  all_dependent.visibility().SetPublic();
  ASSERT_TRUE(all_dependent.OnResolved(&err));
  b.all_dependent_configs().push_back(LabelConfigPair(&all_dependent));

  // Public config.
  Config public_config(setup.settings(), Label(SourceDir("//foo/"), "public"));
  public_config.visibility().SetPublic();
  ASSERT_TRUE(public_config.OnResolved(&err));
  b.public_configs().push_back(LabelConfigPair(&public_config));

  ASSERT_TRUE(b.OnResolved(&err));
  ASSERT_TRUE(a.OnResolved(&err));

  // A should have gotten the configs from B.
  ASSERT_EQ(2u, a.configs().size());
  EXPECT_EQ(&all_dependent, a.configs()[0].ptr);
  EXPECT_EQ(&public_config, a.configs()[1].ptr);
  ASSERT_EQ(1u, a.all_dependent_configs().size());
  EXPECT_EQ(&all_dependent, a.all_dependent_configs()[0].ptr);
}

TEST_F(TargetTest, GetComputedOutputName) {
  TestWithScope setup;
  Err err;

  // Basic target with no prefix (executable type tool in the TestWithScope has
  // no prefix) or output name.
  TestTarget basic(setup, "//foo:bar", Target::EXECUTABLE);
  ASSERT_TRUE(basic.OnResolved(&err));
  EXPECT_EQ("bar", basic.GetComputedOutputName());

  // Target with no prefix but an output name.
  TestTarget with_name(setup, "//foo:bar", Target::EXECUTABLE);
  with_name.set_output_name("myoutput");
  ASSERT_TRUE(with_name.OnResolved(&err));
  EXPECT_EQ("myoutput", with_name.GetComputedOutputName());

  // Target with a "lib" prefix (the static library tool in the TestWithScope
  // should specify a "lib" output prefix).
  TestTarget with_prefix(setup, "//foo:bar", Target::STATIC_LIBRARY);
  ASSERT_TRUE(with_prefix.OnResolved(&err));
  EXPECT_EQ("libbar", with_prefix.GetComputedOutputName());

  // Target with a "lib" prefix that already has it applied. The prefix should
  // not duplicate something already in the target name.
  TestTarget dup_prefix(setup, "//foo:bar", Target::STATIC_LIBRARY);
  dup_prefix.set_output_name("libbar");
  ASSERT_TRUE(dup_prefix.OnResolved(&err));
  EXPECT_EQ("libbar", dup_prefix.GetComputedOutputName());

  // Target with an output prefix override should not have a prefix.
  TestTarget override_prefix(setup, "//foo:bar", Target::SHARED_LIBRARY);
  override_prefix.set_output_prefix_override(true);
  ASSERT_TRUE(dup_prefix.OnResolved(&err));
  EXPECT_EQ("bar", override_prefix.GetComputedOutputName());
}

// Test visibility failure case.
TEST_F(TargetTest, VisibilityFails) {
  TestWithScope setup;
  Err err;

  TestTarget b(setup, "//private:b", Target::STATIC_LIBRARY);
  b.visibility().SetPrivate(b.label().dir());
  ASSERT_TRUE(b.OnResolved(&err));

  // Make a target depending on "b". The dependency must have an origin to mark
  // it as user-set so we check visibility. This check should fail.
  TestTarget a(setup, "//app:a", Target::EXECUTABLE);
  a.private_deps().push_back(LabelTargetPair(&b));
  IdentifierNode origin;  // Dummy origin.
  a.private_deps()[0].origin = &origin;
  ASSERT_FALSE(a.OnResolved(&err));
}

// Test config visibility failure cases.
TEST_F(TargetTest, VisibilityConfigFails) {
  TestWithScope setup;
  Err err;

  Label config_label(SourceDir("//a/"), "config");
  Config config(setup.settings(), config_label);
  config.visibility().SetPrivate(config.label().dir());
  ASSERT_TRUE(config.OnResolved(&err));

  // Make a target using configs. This should fail.
  TestTarget a(setup, "//app:a", Target::EXECUTABLE);
  a.configs().push_back(LabelConfigPair(&config));
  ASSERT_FALSE(a.OnResolved(&err));

  // A target using public_configs should also fail.
  TestTarget b(setup, "//app:b", Target::EXECUTABLE);
  b.public_configs().push_back(LabelConfigPair(&config));
  ASSERT_FALSE(b.OnResolved(&err));

  // A target using all_dependent_configs should fail as well.
  TestTarget c(setup, "//app:c", Target::EXECUTABLE);
  c.all_dependent_configs().push_back(LabelConfigPair(&config));
  ASSERT_FALSE(c.OnResolved(&err));
}

// Test Config -> Group -> A where the config is group is visible from A but
// the config isn't, and the config is visible from the group.
TEST_F(TargetTest, VisibilityConfigGroup) {
  TestWithScope setup;
  Err err;

  Label config_label(SourceDir("//a/"), "config");
  Config config(setup.settings(), config_label);
  config.visibility().SetPrivate(config.label().dir());
  ASSERT_TRUE(config.OnResolved(&err));

  // Make a target using the config in the same directory.
  TestTarget a(setup, "//a:a", Target::GROUP);
  a.public_configs().push_back(LabelConfigPair(&config));
  ASSERT_TRUE(a.OnResolved(&err));

  // A target depending on a should be okay.
  TestTarget b(setup, "//app:b", Target::EXECUTABLE);
  b.private_deps().push_back(LabelTargetPair(&a));
  ASSERT_TRUE(b.OnResolved(&err));
}

// Test visibility with a single data_dep.
TEST_F(TargetTest, VisibilityDatadeps) {
  TestWithScope setup;
  Err err;

  TestTarget b(setup, "//public:b", Target::STATIC_LIBRARY);
  ASSERT_TRUE(b.OnResolved(&err));

  // Make a target depending on "b". The dependency must have an origin to mark
  // it as user-set so we check visibility. This check should fail.
  TestTarget a(setup, "//app:a", Target::EXECUTABLE);
  a.data_deps().push_back(LabelTargetPair(&b));
  IdentifierNode origin;  // Dummy origin.
  a.data_deps()[0].origin = &origin;
  ASSERT_TRUE(a.OnResolved(&err)) << err.help_text();
}

// Tests that A -> Group -> B where the group is visible from A but B isn't,
// passes visibility even though the group's deps get expanded into A.
TEST_F(TargetTest, VisibilityGroup) {
  TestWithScope setup;
  Err err;

  IdentifierNode origin;  // Dummy origin.

  // B has private visibility. This lets the group see it since the group is in
  // the same directory.
  TestTarget b(setup, "//private:b", Target::STATIC_LIBRARY);
  b.visibility().SetPrivate(b.label().dir());
  ASSERT_TRUE(b.OnResolved(&err));

  // The group has public visibility and depends on b.
  TestTarget g(setup, "//public:g", Target::GROUP);
  g.private_deps().push_back(LabelTargetPair(&b));
  g.private_deps()[0].origin = &origin;
  ASSERT_TRUE(b.OnResolved(&err));

  // Make a target depending on "g". This should succeed.
  TestTarget a(setup, "//app:a", Target::EXECUTABLE);
  a.private_deps().push_back(LabelTargetPair(&g));
  a.private_deps()[0].origin = &origin;
  ASSERT_TRUE(a.OnResolved(&err));
}

// Verifies that only testonly targets can depend on other testonly targets.
// Many of the above dependency checking cases covered the non-testonly
// case.
TEST_F(TargetTest, Testonly) {
  TestWithScope setup;
  Err err;

  // "testlib" is a test-only library.
  TestTarget testlib(setup, "//test:testlib", Target::STATIC_LIBRARY);
  testlib.set_testonly(true);
  ASSERT_TRUE(testlib.OnResolved(&err));

  // "test" is a test-only executable depending on testlib, this is OK.
  TestTarget test(setup, "//test:test", Target::EXECUTABLE);
  test.set_testonly(true);
  test.private_deps().push_back(LabelTargetPair(&testlib));
  ASSERT_TRUE(test.OnResolved(&err));

  // "product" is a non-test depending on testlib. This should fail.
  TestTarget product(setup, "//app:product", Target::EXECUTABLE);
  product.set_testonly(false);
  product.private_deps().push_back(LabelTargetPair(&testlib));
  ASSERT_FALSE(product.OnResolved(&err));
}

// Configs can be testonly too.
// Repeat the testonly test with a config.
TEST_F(TargetTest, TestonlyConfig) {
  TestWithScope setup;
  Err err;

  // "testconfig" is a test-only config.
  Config testconfig(setup.settings(), Label(SourceDir("//test/"), "config"));
  testconfig.set_testonly(true);
  testconfig.visibility().SetPublic();
  ASSERT_TRUE(testconfig.OnResolved(&err));

  // "test" is a test-only executable that uses testconfig, this is OK.
  TestTarget test(setup, "//test:test", Target::EXECUTABLE);
  test.set_testonly(true);
  test.configs().push_back(LabelConfigPair(&testconfig));
  ASSERT_TRUE(test.OnResolved(&err));

  // "product" is a non-test that uses testconfig. This should fail.
  TestTarget product(setup, "//app:product", Target::EXECUTABLE);
  product.set_testonly(false);
  product.configs().push_back(LabelConfigPair(&testconfig));
  ASSERT_FALSE(product.OnResolved(&err));
}

TEST_F(TargetTest, PublicConfigs) {
  TestWithScope setup;
  Err err;

  Label pub_config_label(SourceDir("//a/"), "pubconfig");
  Config pub_config(setup.settings(), pub_config_label);
  pub_config.visibility().SetPublic();
  LibFile lib_name("testlib");
  pub_config.own_values().libs().push_back(lib_name);
  ASSERT_TRUE(pub_config.OnResolved(&err));

  // This is the destination target that has a public config.
  TestTarget dest(setup, "//a:a", Target::SOURCE_SET);
  dest.public_configs().push_back(LabelConfigPair(&pub_config));
  ASSERT_TRUE(dest.OnResolved(&err));

  // This target has a public dependency on dest.
  TestTarget pub(setup, "//a:pub", Target::SOURCE_SET);
  pub.public_deps().push_back(LabelTargetPair(&dest));
  ASSERT_TRUE(pub.OnResolved(&err));

  // Depending on the target with the public dependency should forward dest's
  // to the current target.
  TestTarget dep_on_pub(setup, "//a:dop", Target::SOURCE_SET);
  dep_on_pub.private_deps().push_back(LabelTargetPair(&pub));
  ASSERT_TRUE(dep_on_pub.OnResolved(&err));
  ASSERT_EQ(1u, dep_on_pub.configs().size());
  EXPECT_EQ(&pub_config, dep_on_pub.configs()[0].ptr);

  // Libs have special handling, check that they were forwarded from the
  // public config to all_libs.
  ResolvedTargetData resolved;
  const auto& dep_on_pub_all_libs = resolved.GetLinkedLibraries(&dep_on_pub);
  ASSERT_EQ(1u, dep_on_pub_all_libs.size());
  ASSERT_EQ(lib_name, dep_on_pub_all_libs[0]);

  // This target has a private dependency on dest for forwards configs.
  TestTarget forward(setup, "//a:f", Target::SOURCE_SET);
  forward.private_deps().push_back(LabelTargetPair(&dest));
  ASSERT_TRUE(forward.OnResolved(&err));
}

// Tests that configs are ordered properly between local and pulled ones.
TEST_F(TargetTest, ConfigOrdering) {
  TestWithScope setup;
  Err err;

  // Make Dep1. It has all_dependent_configs and public_configs.
  TestTarget dep1(setup, "//:dep1", Target::SOURCE_SET);
  Label dep1_all_config_label(SourceDir("//"), "dep1_all_config");
  Config dep1_all_config(setup.settings(), dep1_all_config_label);
  dep1_all_config.visibility().SetPublic();
  ASSERT_TRUE(dep1_all_config.OnResolved(&err));
  dep1.all_dependent_configs().push_back(LabelConfigPair(&dep1_all_config));

  Label dep1_public_config_label(SourceDir("//"), "dep1_public_config");
  Config dep1_public_config(setup.settings(), dep1_public_config_label);
  dep1_public_config.visibility().SetPublic();
  ASSERT_TRUE(dep1_public_config.OnResolved(&err));
  dep1.public_configs().push_back(LabelConfigPair(&dep1_public_config));
  ASSERT_TRUE(dep1.OnResolved(&err));

  // Make Dep2 with the same structure.
  TestTarget dep2(setup, "//:dep2", Target::SOURCE_SET);
  Label dep2_all_config_label(SourceDir("//"), "dep2_all_config");
  Config dep2_all_config(setup.settings(), dep2_all_config_label);
  dep2_all_config.visibility().SetPublic();
  ASSERT_TRUE(dep2_all_config.OnResolved(&err));
  dep2.all_dependent_configs().push_back(LabelConfigPair(&dep2_all_config));

  Label dep2_public_config_label(SourceDir("//"), "dep2_public_config");
  Config dep2_public_config(setup.settings(), dep2_public_config_label);
  dep2_public_config.visibility().SetPublic();
  ASSERT_TRUE(dep2_public_config.OnResolved(&err));
  dep2.public_configs().push_back(LabelConfigPair(&dep2_public_config));
  ASSERT_TRUE(dep2.OnResolved(&err));

  // This target depends on both previous targets.
  TestTarget target(setup, "//:foo", Target::SOURCE_SET);
  target.private_deps().push_back(LabelTargetPair(&dep1));
  target.private_deps().push_back(LabelTargetPair(&dep2));

  // It also has a private and public config.
  Label public_config_label(SourceDir("//"), "public");
  Config public_config(setup.settings(), public_config_label);
  public_config.visibility().SetPublic();
  ASSERT_TRUE(public_config.OnResolved(&err));
  target.public_configs().push_back(LabelConfigPair(&public_config));

  Label private_config_label(SourceDir("//"), "private");
  Config private_config(setup.settings(), private_config_label);
  private_config.visibility().SetPublic();
  ASSERT_TRUE(private_config.OnResolved(&err));
  target.configs().push_back(LabelConfigPair(&private_config));

  // Resolve to get the computed list of configs applying.
  ASSERT_TRUE(target.OnResolved(&err));
  const auto& computed = target.configs();

  // Order should be:
  // 1. local private
  // 2. local public
  // 3. inherited all dependent
  // 4. inherited public
  ASSERT_EQ(6u, computed.size());
  EXPECT_EQ(private_config_label, computed[0].label);
  EXPECT_EQ(public_config_label, computed[1].label);
  EXPECT_EQ(dep1_all_config_label, computed[2].label);
  EXPECT_EQ(dep2_all_config_label, computed[3].label);
  EXPECT_EQ(dep1_public_config_label, computed[4].label);
  EXPECT_EQ(dep2_public_config_label, computed[5].label);
}

// Tests that different link/depend outputs work for solink tools.
TEST_F(TargetTest, LinkAndDepOutputs) {
  TestWithScope setup;
  Err err;

  Toolchain toolchain(setup.settings(), Label(SourceDir("//tc/"), "tc"));

  std::unique_ptr<Tool> solink = Tool::CreateTool(CTool::kCToolSolink);
  CTool* solink_tool = solink->AsC();
  solink_tool->set_output_prefix("lib");
  solink_tool->set_default_output_extension(".so");

  const char kLinkPattern[] =
      "{{root_out_dir}}/{{target_output_name}}{{output_extension}}";
  SubstitutionPattern link_output =
      SubstitutionPattern::MakeForTest(kLinkPattern);
  solink_tool->set_link_output(link_output);

  const char kDependPattern[] =
      "{{root_out_dir}}/{{target_output_name}}{{output_extension}}.TOC";
  SubstitutionPattern depend_output =
      SubstitutionPattern::MakeForTest(kDependPattern);
  solink_tool->set_depend_output(depend_output);

  solink_tool->set_outputs(
      SubstitutionList::MakeForTest(kLinkPattern, kDependPattern));

  toolchain.SetTool(std::move(solink));

  Target target(setup.settings(), Label(SourceDir("//a/"), "a"));
  target.set_output_type(Target::SHARED_LIBRARY);
  target.SetToolchain(&toolchain);
  ASSERT_TRUE(target.OnResolved(&err));

  EXPECT_EQ("./liba.so", target.link_output_file().value());
  ASSERT_TRUE(target.has_dependency_output_file());
  EXPECT_EQ("./liba.so.TOC", target.dependency_output_file().value());

  ASSERT_EQ(1u, target.runtime_outputs().size());
  EXPECT_EQ("./liba.so", target.runtime_outputs()[0].value());
}

TEST_F(TargetTest, RustLinkAndDepOutputs) {
  TestWithScope setup;
  Err err;

  Toolchain toolchain(setup.settings(), Label(SourceDir("//tc/"), "tc"));

  std::unique_ptr<Tool> tool = Tool::CreateTool(RustTool::kRsToolDylib);
  RustTool* rust_tool = tool->AsRust();
  rust_tool->set_output_prefix("lib");
  rust_tool->set_default_output_extension(".so");

  const char kLinkPattern[] =
      "{{root_out_dir}}/{{target_output_name}}{{output_extension}}";
  SubstitutionPattern link_output =
      SubstitutionPattern::MakeForTest(kLinkPattern);
  rust_tool->set_link_output(link_output);

  const char kDependPattern[] =
      "{{root_out_dir}}/{{target_output_name}}{{output_extension}}.TOC";
  SubstitutionPattern depend_output =
      SubstitutionPattern::MakeForTest(kDependPattern);
  rust_tool->set_depend_output(depend_output);

  rust_tool->set_outputs(
      SubstitutionList::MakeForTest(kLinkPattern, kDependPattern));

  toolchain.SetTool(std::move(tool));

  Target target(setup.settings(), Label(SourceDir("//a/"), "a"));
  target.source_types_used().Set(SourceFile::SOURCE_RS);
  target.rust_values().set_crate_type(RustValues::CRATE_DYLIB);
  target.set_output_type(Target::SHARED_LIBRARY);
  target.SetToolchain(&toolchain);
  ASSERT_TRUE(target.OnResolved(&err));

  EXPECT_EQ("./liba.so", target.link_output_file().value());
  EXPECT_EQ("./liba.so.TOC", target.dependency_output_file().value());

  ASSERT_EQ(1u, target.runtime_outputs().size());
  EXPECT_EQ("./liba.so", target.runtime_outputs()[0].value());
}

// Tests that runtime_outputs works without an explicit link_output for
// solink tools.
//
// Also tests GetOutputsAsSourceFiles() for binaries (the setup is the same).
TEST_F(TargetTest, RuntimeOuputs) {
  TestWithScope setup;
  Err err;

  Toolchain toolchain(setup.settings(), Label(SourceDir("//tc/"), "tc"));

  std::unique_ptr<Tool> solink = Tool::CreateTool(CTool::kCToolSolink);
  CTool* solink_tool = solink->AsC();
  solink_tool->set_output_prefix("");
  solink_tool->set_default_output_extension(".dll");

  // Say the linker makes a DLL< an import library, and a symbol file we want
  // to treat as a runtime output.
  const char kLibPattern[] =
      "{{root_out_dir}}/{{target_output_name}}{{output_extension}}.lib";
  const char kDllPattern[] =
      "{{root_out_dir}}/{{target_output_name}}{{output_extension}}";
  const char kPdbPattern[] = "{{root_out_dir}}/{{target_output_name}}.pdb";
  SubstitutionPattern pdb_pattern =
      SubstitutionPattern::MakeForTest(kPdbPattern);

  solink_tool->set_outputs(
      SubstitutionList::MakeForTest(kLibPattern, kDllPattern, kPdbPattern));

  // Say we only want the DLL and symbol file treaded as runtime outputs.
  solink_tool->set_runtime_outputs(
      SubstitutionList::MakeForTest(kDllPattern, kPdbPattern));

  toolchain.SetTool(std::move(solink));

  Target target(setup.settings(), Label(SourceDir("//a/"), "a"));
  target.set_output_type(Target::SHARED_LIBRARY);
  target.SetToolchain(&toolchain);
  ASSERT_TRUE(target.OnResolved(&err));

  EXPECT_EQ("./a.dll.lib", target.link_output_file().value());
  ASSERT_TRUE(target.has_dependency_output_file());
  EXPECT_EQ("./a.dll.lib", target.dependency_output_file().value());

  ASSERT_EQ(2u, target.runtime_outputs().size());
  EXPECT_EQ("./a.dll", target.runtime_outputs()[0].value());
  EXPECT_EQ("./a.pdb", target.runtime_outputs()[1].value());

  // Test GetOutputsAsSourceFiles().
  std::vector<SourceFile> computed_outputs;
  EXPECT_TRUE(target.GetOutputsAsSourceFiles(LocationRange(), true,
                                             &computed_outputs, &err));
  ASSERT_EQ(3u, computed_outputs.size());
  EXPECT_EQ("//out/Debug/a.dll.lib", computed_outputs[0].value());
  EXPECT_EQ("//out/Debug/a.dll", computed_outputs[1].value());
  EXPECT_EQ("//out/Debug/a.pdb", computed_outputs[2].value());
}

TEST_F(TargetTest, RustRuntimeOuputs) {
  TestWithScope setup;
  Err err;

  Toolchain toolchain(setup.settings(), Label(SourceDir("//tc/"), "tc"));

  std::unique_ptr<Tool> tool = Tool::CreateTool(RustTool::kRsToolCDylib);
  RustTool* rust_tool = tool->AsRust();
  rust_tool->set_output_prefix("");
  rust_tool->set_default_output_extension(".dll");

  // Say the linker makes a DLL< an import library, and a symbol file we want
  // to treat as a runtime output.
  const char kLibPattern[] =
      "{{root_out_dir}}/{{target_output_name}}{{output_extension}}.lib";
  const char kDllPattern[] =
      "{{root_out_dir}}/{{target_output_name}}{{output_extension}}";
  const char kPdbPattern[] = "{{root_out_dir}}/{{target_output_name}}.pdb";
  SubstitutionPattern pdb_pattern =
      SubstitutionPattern::MakeForTest(kPdbPattern);

  rust_tool->set_outputs(
      SubstitutionList::MakeForTest(kLibPattern, kDllPattern, kPdbPattern));

  // Say we only want the DLL and symbol file treaded as runtime outputs.
  rust_tool->set_runtime_outputs(
      SubstitutionList::MakeForTest(kDllPattern, kPdbPattern));

  toolchain.SetTool(std::move(tool));

  Target target(setup.settings(), Label(SourceDir("//a/"), "a"));
  target.source_types_used().Set(SourceFile::SOURCE_RS);
  target.rust_values().set_crate_type(RustValues::CRATE_CDYLIB);
  target.set_output_type(Target::SHARED_LIBRARY);
  target.SetToolchain(&toolchain);
  ASSERT_TRUE(target.OnResolved(&err));

  EXPECT_EQ("./a.dll.lib", target.link_output_file().value());
  EXPECT_EQ("./a.dll.lib", target.dependency_output_file().value());

  ASSERT_EQ(2u, target.runtime_outputs().size());
  EXPECT_EQ("./a.dll", target.runtime_outputs()[0].value());
  EXPECT_EQ("./a.pdb", target.runtime_outputs()[1].value());

  // Test GetOutputsAsSourceFiles().
  std::vector<SourceFile> computed_outputs;
  EXPECT_TRUE(target.GetOutputsAsSourceFiles(LocationRange(), true,
                                             &computed_outputs, &err));
  ASSERT_EQ(3u, computed_outputs.size());
  EXPECT_EQ("//out/Debug/a.dll.lib", computed_outputs[0].value());
  EXPECT_EQ("//out/Debug/a.dll", computed_outputs[1].value());
  EXPECT_EQ("//out/Debug/a.pdb", computed_outputs[2].value());
}
// Tests Target::GetOutputFilesForSource for action_foreach targets (these, like
// copy targets, apply a pattern to the source file). Also tests
// GetOutputsAsSourceFiles() for action_foreach().
TEST_F(TargetTest, GetOutputFilesForSource_ActionForEach) {
  TestWithScope setup;

  TestTarget target(setup, "//a:a", Target::ACTION_FOREACH);
  target.sources().push_back(SourceFile("//a/source_file1.txt"));
  target.sources().push_back(SourceFile("//a/source_file2.txt"));
  target.action_values().outputs() =
      SubstitutionList::MakeForTest("//out/Debug/{{source_file_part}}.one",
                                    "//out/Debug/{{source_file_part}}.two");
  Err err;
  ASSERT_TRUE(target.OnResolved(&err));

  const char* computed_tool_type = nullptr;
  std::vector<OutputFile> output;
  bool result = target.GetOutputFilesForSource(SourceFile("//source/input.txt"),
                                               &computed_tool_type, &output);
  ASSERT_TRUE(result);

  // Outputs are relative to the build directory "//out/Debug/".
  ASSERT_EQ(2u, output.size());
  EXPECT_EQ("input.txt.one", output[0].value());
  EXPECT_EQ("input.txt.two", output[1].value());

  // Test GetOutputsAsSourceFiles(). It should give both outputs for each of the
  // two inputs.
  std::vector<SourceFile> computed_outputs;
  EXPECT_TRUE(target.GetOutputsAsSourceFiles(LocationRange(), true,
                                             &computed_outputs, &err));
  ASSERT_EQ(4u, computed_outputs.size());
  EXPECT_EQ("//out/Debug/source_file1.txt.one", computed_outputs[0].value());
  EXPECT_EQ("//out/Debug/source_file1.txt.two", computed_outputs[1].value());
  EXPECT_EQ("//out/Debug/source_file2.txt.one", computed_outputs[2].value());
  EXPECT_EQ("//out/Debug/source_file2.txt.two", computed_outputs[3].value());
}

// Tests Target::GetOutputFilesForSource for action targets (these just list the
// output of the action as the result of all possible inputs). This should also
// cover everything other than binary, action_foreach, and copy targets.
TEST_F(TargetTest, GetOutputFilesForSource_Action) {
  TestWithScope setup;

  TestTarget target(setup, "//a:a", Target::ACTION);
  target.sources().push_back(SourceFile("//a/source_file1.txt"));
  target.action_values().outputs() =
      SubstitutionList::MakeForTest("//out/Debug/one", "//out/Debug/two");
  Err err;
  ASSERT_TRUE(target.OnResolved(&err));

  const char* computed_tool_type = nullptr;
  std::vector<OutputFile> output;
  bool result = target.GetOutputFilesForSource(SourceFile("//source/input.txt"),
                                               &computed_tool_type, &output);
  ASSERT_TRUE(result);

  // Outputs are relative to the build directory "//out/Debug/".
  ASSERT_EQ(2u, output.size());
  EXPECT_EQ("one", output[0].value());
  EXPECT_EQ("two", output[1].value());

  // Test GetOutputsAsSourceFiles(). It should give the listed outputs.
  std::vector<SourceFile> computed_outputs;
  EXPECT_TRUE(target.GetOutputsAsSourceFiles(LocationRange(), true,
                                             &computed_outputs, &err));
  ASSERT_EQ(2u, computed_outputs.size());
  EXPECT_EQ("//out/Debug/one", computed_outputs[0].value());
  EXPECT_EQ("//out/Debug/two", computed_outputs[1].value());

  // Test that the copy target type behaves the same. This target requires only
  // one output.
  target.action_values().outputs() =
      SubstitutionList::MakeForTest("//out/Debug/one");
  target.set_output_type(Target::COPY_FILES);

  // Outputs are relative to the build directory "//out/Debug/".
  result = target.GetOutputFilesForSource(SourceFile("//source/input.txt"),
                                          &computed_tool_type, &output);
  ASSERT_TRUE(result);
  ASSERT_EQ(1u, output.size());
  EXPECT_EQ("one", output[0].value());

  // Test GetOutputsAsSourceFiles() for the copy case.
  EXPECT_TRUE(target.GetOutputsAsSourceFiles(LocationRange(), true,
                                             &computed_outputs, &err));
  ASSERT_EQ(1u, computed_outputs.size()) << computed_outputs.size();
  EXPECT_EQ("//out/Debug/one", computed_outputs[0].value());
}

TEST_F(TargetTest, HasRealInputs) {
  TestWithScope setup;
  Err err;
  // Action always have real inputs.
  TestTarget target_a(setup, "//a:a", Target::ACTION);
  ASSERT_TRUE(target_a.FillOutputFiles(&err));
  EXPECT_TRUE(target_a.HasRealInputs());

  // A target with no inputs and no deps has no real inputs.
  TestTarget target_b(setup, "//a:b", Target::GROUP);
  ASSERT_TRUE(target_b.FillOutputFiles(&err));
  EXPECT_FALSE(target_b.HasRealInputs());

  // A target with no inputs and one dep with real inputs has real inputs.
  target_b.private_deps().push_back(LabelTargetPair(&target_a));
  ASSERT_TRUE(target_b.FillOutputFiles(&err));
  EXPECT_TRUE(target_b.HasRealInputs());

  // A target with one input with no tool, and no deps, has no real inputs.
  TestTarget target_c(setup, "//a:c", Target::SOURCE_SET);
  target_c.config_values().inputs().push_back(SourceFile("//a/no_tool.txt"));
  ASSERT_TRUE(target_c.FillOutputFiles(&err));
  EXPECT_FALSE(target_c.HasRealInputs());  // One input with no tool, no deps.

  // The same, but with one dep without a dependency output.
  TestTarget target_d(setup, "//a:c2", Target::GROUP);
  target_c.private_deps().push_back(LabelTargetPair(&target_d));
  ASSERT_TRUE(target_c.FillOutputFiles(&err));
  EXPECT_FALSE(target_c.HasRealInputs());

  // The same, but with one dep with a dependency output.
  TestTarget target_e(setup, "//a:d", Target::EXECUTABLE);
  target_e.sources().push_back(SourceFile("//a/source.cc"));
  ASSERT_TRUE(target_e.FillOutputFiles(&err));
  EXPECT_TRUE(target_e.HasRealInputs());
  target_c.private_deps().push_back(LabelTargetPair(&target_e));
  ASSERT_TRUE(target_c.FillOutputFiles(&err));
  EXPECT_TRUE(target_c.HasRealInputs());
}

TEST_F(TargetTest, GeneratedInputs) {
  TestWithScope setup;
  Err err;

  SourceFile generated_file("//out/Debug/generated.cc");

  // This target has a generated input and no dependency makes it.
  TestTarget non_existent_generator(setup, "//foo:non_existent_generator",
                                    Target::EXECUTABLE);
  non_existent_generator.sources().push_back(generated_file);
  EXPECT_TRUE(non_existent_generator.OnResolved(&err)) << err.message();
  AssertSchedulerHasOneUnknownFileMatching(&non_existent_generator,
                                           generated_file);
  scheduler().ClearUnknownGeneratedInputsAndWrittenFiles();

  // Make a target that generates the file.
  TestTarget generator(setup, "//foo:generator", Target::ACTION);
  generator.action_values().outputs() =
      SubstitutionList::MakeForTest(generated_file.value().c_str());
  err = Err();
  EXPECT_TRUE(generator.OnResolved(&err)) << err.message();

  // A target that depends on the generator that uses the file as a source
  // should be OK. This uses a private dep (will be used later).
  TestTarget existent_generator(setup, "//foo:existent_generator",
                                Target::SHARED_LIBRARY);
  existent_generator.sources().push_back(generated_file);
  existent_generator.private_deps().push_back(LabelTargetPair(&generator));
  EXPECT_TRUE(existent_generator.OnResolved(&err)) << err.message();
  EXPECT_TRUE(scheduler().GetUnknownGeneratedInputs().empty());

  // A target that depends on the previous one should *not* be allowed to
  // use the generated file, because existent_generator used private deps.
  // This is:
  //    indirect_private --> existent_generator --[private]--> generator
  TestTarget indirect_private(setup, "//foo:indirect_private",
                              Target::EXECUTABLE);
  indirect_private.sources().push_back(generated_file);
  indirect_private.public_deps().push_back(
      LabelTargetPair(&existent_generator));
  EXPECT_TRUE(indirect_private.OnResolved(&err));
  AssertSchedulerHasOneUnknownFileMatching(&indirect_private, generated_file);
  scheduler().ClearUnknownGeneratedInputsAndWrittenFiles();

  // Now make a chain like the above but with all public deps, it should be OK.
  TestTarget existent_public(setup, "//foo:existent_public",
                             Target::SHARED_LIBRARY);
  existent_public.public_deps().push_back(LabelTargetPair(&generator));
  EXPECT_TRUE(existent_public.OnResolved(&err)) << err.message();
  TestTarget indirect_public(setup, "//foo:indirect_public",
                             Target::EXECUTABLE);
  indirect_public.sources().push_back(generated_file);
  indirect_public.public_deps().push_back(LabelTargetPair(&existent_public));
  EXPECT_TRUE(indirect_public.OnResolved(&err)) << err.message();
  EXPECT_TRUE(scheduler().GetUnknownGeneratedInputs().empty());
}

// This is sort of a Scheduler test, but is related to the above test more.
TEST_F(TargetTest, WriteFileGeneratedInputs) {
  TestWithScope setup;
  Err err;

  SourceFile generated_file("//out/Debug/generated.data");

  // This target has a generated input and no dependency makes it.
  TestTarget non_existent_generator(setup, "//foo:non_existent_generator",
                                    Target::EXECUTABLE);
  non_existent_generator.sources().push_back(generated_file);
  EXPECT_TRUE(non_existent_generator.OnResolved(&err));
  AssertSchedulerHasOneUnknownFileMatching(&non_existent_generator,
                                           generated_file);
  scheduler().ClearUnknownGeneratedInputsAndWrittenFiles();

  // This target has a generated file and we've decared we write it.
  TestTarget existent_generator(setup, "//foo:existent_generator",
                                Target::EXECUTABLE);
  existent_generator.sources().push_back(generated_file);
  EXPECT_TRUE(existent_generator.OnResolved(&err));
  scheduler().AddWrittenFile(generated_file);

  // Should be OK.
  EXPECT_TRUE(scheduler().GetUnknownGeneratedInputs().empty());
}

TEST_F(TargetTest, WriteRuntimeDepsGeneratedInputs) {
  TestWithScope setup;
  Err err;

  SourceFile source_file("//out/Debug/generated.runtime_deps");
  OutputFile output_file(setup.build_settings(), source_file);

  TestTarget generator(setup, "//foo:generator", Target::EXECUTABLE);
  generator.set_write_runtime_deps_output(output_file);
  g_scheduler->AddWriteRuntimeDepsTarget(&generator);

  TestTarget middle_data_dep(setup, "//foo:middle", Target::EXECUTABLE);
  middle_data_dep.data_deps().push_back(LabelTargetPair(&generator));

  // This target has a generated input and no dependency makes it.
  TestTarget dep_missing(setup, "//foo:no_dep", Target::EXECUTABLE);
  dep_missing.sources().push_back(source_file);
  EXPECT_TRUE(dep_missing.OnResolved(&err));
  AssertSchedulerHasOneUnknownFileMatching(&dep_missing, source_file);
  scheduler().ClearUnknownGeneratedInputsAndWrittenFiles();

  // This target has a generated file and we've directly dependended on it.
  TestTarget dep_present(setup, "//foo:with_dep", Target::EXECUTABLE);
  dep_present.sources().push_back(source_file);
  dep_present.private_deps().push_back(LabelTargetPair(&generator));
  EXPECT_TRUE(dep_present.OnResolved(&err));
  EXPECT_TRUE(scheduler().GetUnknownGeneratedInputs().empty());

  // This target has a generated file and we've indirectly dependended on it
  // via data_deps.
  TestTarget dep_indirect(setup, "//foo:with_dep", Target::EXECUTABLE);
  dep_indirect.sources().push_back(source_file);
  dep_indirect.data_deps().push_back(LabelTargetPair(&middle_data_dep));
  EXPECT_TRUE(dep_indirect.OnResolved(&err));
  AssertSchedulerHasOneUnknownFileMatching(&dep_indirect, source_file);
  scheduler().ClearUnknownGeneratedInputsAndWrittenFiles();

  // This target has a generated file and we've directly dependended on it
  // via data_deps.
  TestTarget data_dep_present(setup, "//foo:with_dep", Target::EXECUTABLE);
  data_dep_present.sources().push_back(source_file);
  data_dep_present.data_deps().push_back(LabelTargetPair(&generator));
  EXPECT_TRUE(data_dep_present.OnResolved(&err));
  EXPECT_TRUE(scheduler().GetUnknownGeneratedInputs().empty());
}

// Tests that intermediate object files generated by binary targets are also
// considered generated for the purposes of input checking. Above, we tested
// the failure cases for generated inputs, so here only test .o files that are
// present.
TEST_F(TargetTest, ObjectGeneratedInputs) {
  TestWithScope setup;
  Err err;

  // This target compiles the source.
  SourceFile source_file("//source.cc");
  TestTarget source_generator(setup, "//:source_target", Target::SOURCE_SET);
  source_generator.sources().push_back(source_file);
  EXPECT_TRUE(source_generator.OnResolved(&err));

  // This is the object file that the test toolchain generates for the source.
  SourceFile object_file("//out/Debug/obj/source_target.source.o");

  TestTarget final_target(setup, "//:final", Target::ACTION);
  final_target.config_values().inputs().push_back(object_file);
  EXPECT_TRUE(final_target.OnResolved(&err));

  AssertSchedulerHasOneUnknownFileMatching(&final_target, object_file);
}

TEST_F(TargetTest, ResolvePrecompiledHeaders) {
  TestWithScope setup;
  Err err;

  Target target(setup.settings(), Label(SourceDir("//foo/"), "bar",
                                        SourceDir("//toolchain/"), "default"));

  // Target with no settings, no configs, should be a no-op.
  EXPECT_TRUE(target.ResolvePrecompiledHeaders(&err));

  // Config with PCH values.
  Config config_1(
      setup.settings(),
      Label(SourceDir("//foo/"), "c1", SourceDir("//toolchain/"), "default"));
  std::string pch_1("pch.h");
  SourceFile pcs_1("//pcs.cc");
  config_1.own_values().set_precompiled_header(pch_1);
  config_1.own_values().set_precompiled_source(pcs_1);
  ASSERT_TRUE(config_1.OnResolved(&err));
  target.configs().push_back(LabelConfigPair(&config_1));

  // No PCH info specified on TargetTest, but the config specifies one, the
  // values should get copied to the target.
  EXPECT_TRUE(target.ResolvePrecompiledHeaders(&err));
  EXPECT_EQ(pch_1, target.config_values().precompiled_header());
  EXPECT_TRUE(target.config_values().precompiled_source() == pcs_1);

  // Now both target and config have matching PCH values. Resolving again
  // should be a no-op since they all match.
  EXPECT_TRUE(target.ResolvePrecompiledHeaders(&err));
  EXPECT_TRUE(target.config_values().precompiled_header() == pch_1);
  EXPECT_TRUE(target.config_values().precompiled_source() == pcs_1);

  // Second config with different PCH values.
  Config config_2(
      setup.settings(),
      Label(SourceDir("//foo/"), "c2", SourceDir("//toolchain/"), "default"));
  std::string pch_2("pch2.h");
  SourceFile pcs_2("//pcs2.cc");
  config_2.own_values().set_precompiled_header(pch_2);
  config_2.own_values().set_precompiled_source(pcs_2);
  ASSERT_TRUE(config_2.OnResolved(&err));
  target.configs().push_back(LabelConfigPair(&config_2));

  // This should be an error since they don't match.
  EXPECT_FALSE(target.ResolvePrecompiledHeaders(&err));

  // Make sure the proper labels are blamed.
  EXPECT_EQ(
      "The target //foo:bar\n"
      "has conflicting precompiled header settings.\n"
      "\n"
      "From //foo:bar\n"
      "  header: pch.h\n"
      "  source: //pcs.cc\n"
      "\n"
      "From //foo:c2\n"
      "  header: pch2.h\n"
      "  source: //pcs2.cc",
      err.help_text());
}

TEST_F(TargetTest, AssertNoDeps) {
  TestWithScope setup;
  Err err;

  // A target.
  TestTarget a(setup, "//a", Target::SHARED_LIBRARY);
  ASSERT_TRUE(a.OnResolved(&err));

  // B depends on A and has an assert_no_deps for a random dir.
  TestTarget b(setup, "//b", Target::SHARED_LIBRARY);
  b.private_deps().push_back(LabelTargetPair(&a));
  b.assert_no_deps().push_back(LabelPattern(LabelPattern::RECURSIVE_DIRECTORY,
                                            SourceDir("//disallowed/"),
                                            std::string(), Label()));
  ASSERT_TRUE(b.OnResolved(&err));

  LabelPattern disallow_a(LabelPattern::RECURSIVE_DIRECTORY, SourceDir("//a/"),
                          std::string(), Label());

  // C depends on B and disallows depending on A. This should fail.
  TestTarget c(setup, "//c", Target::EXECUTABLE);
  c.private_deps().push_back(LabelTargetPair(&b));
  c.assert_no_deps().push_back(disallow_a);
  ASSERT_FALSE(c.OnResolved(&err));

  // Validate the error message has the proper path.
  EXPECT_EQ(
      "//c:c has an assert_no_deps entry:\n"
      "  //a/*\n"
      "which fails for the dependency path:\n"
      "  //c:c ->\n"
      "  //b:b ->\n"
      "  //a:a",
      err.help_text());
  err = Err();

  // Add an intermediate executable with: exe -> b -> a
  TestTarget exe(setup, "//exe", Target::EXECUTABLE);
  exe.private_deps().push_back(LabelTargetPair(&b));
  ASSERT_TRUE(exe.OnResolved(&err));

  // D depends on the executable and disallows depending on A. Since there is
  // an intermediate executable, this should be OK.
  TestTarget d(setup, "//d", Target::EXECUTABLE);
  d.private_deps().push_back(LabelTargetPair(&exe));
  d.assert_no_deps().push_back(disallow_a);
  ASSERT_TRUE(d.OnResolved(&err));

  // A2 disallows depending on anything in its own directory, but the
  // assertions should not match the target itself so this should be OK.
  TestTarget a2(setup, "//a:a2", Target::EXECUTABLE);
  a2.assert_no_deps().push_back(disallow_a);
  ASSERT_TRUE(a2.OnResolved(&err));
}

TEST_F(TargetTest, PullRecursiveBundleData) {
  TestWithScope setup;
  Err err;

  // We have the following dependency graph:
  // A (create_bundle) -> B (bundle_data)
  //                  \-> C (create_bundle) -> D (bundle_data)
  //                  \-> E (group) -> F (bundle_data)
  //                               \-> B (bundle_data)
  TestTarget a(setup, "//foo:a", Target::CREATE_BUNDLE);
  TestTarget b(setup, "//foo:b", Target::BUNDLE_DATA);
  TestTarget c(setup, "//foo:c", Target::CREATE_BUNDLE);
  TestTarget d(setup, "//foo:d", Target::BUNDLE_DATA);
  TestTarget e(setup, "//foo:e", Target::GROUP);
  TestTarget f(setup, "//foo:f", Target::BUNDLE_DATA);
  a.public_deps().push_back(LabelTargetPair(&b));
  a.public_deps().push_back(LabelTargetPair(&c));
  a.public_deps().push_back(LabelTargetPair(&e));
  c.public_deps().push_back(LabelTargetPair(&d));
  e.public_deps().push_back(LabelTargetPair(&f));
  e.public_deps().push_back(LabelTargetPair(&b));

  a.bundle_data().root_dir() = SourceDir("//out/foo_a.bundle");
  a.bundle_data().resources_dir() = SourceDir("//out/foo_a.bundle/Resources");

  b.sources().push_back(SourceFile("//foo/b1.txt"));
  b.sources().push_back(SourceFile("//foo/b2.txt"));
  b.action_values().outputs() = SubstitutionList::MakeForTest(
      "{{bundle_resources_dir}}/{{source_file_part}}");
  ASSERT_TRUE(b.OnResolved(&err));

  c.bundle_data().root_dir() = SourceDir("//out/foo_c.bundle");
  c.bundle_data().resources_dir() = SourceDir("//out/foo_c.bundle/Resources");

  d.sources().push_back(SourceFile("//foo/d.txt"));
  d.action_values().outputs() = SubstitutionList::MakeForTest(
      "{{bundle_resources_dir}}/{{source_file_part}}");
  ASSERT_TRUE(d.OnResolved(&err));

  f.sources().push_back(SourceFile("//foo/f1.txt"));
  f.sources().push_back(SourceFile("//foo/f2.txt"));
  f.sources().push_back(SourceFile("//foo/f3.txt"));
  f.sources().push_back(
      SourceFile("//foo/Foo.xcassets/foo.imageset/Contents.json"));
  f.sources().push_back(
      SourceFile("//foo/Foo.xcassets/foo.imageset/FooEmpty-29.png"));
  f.sources().push_back(
      SourceFile("//foo/Foo.xcassets/foo.imageset/FooEmpty-29@2x.png"));
  f.sources().push_back(
      SourceFile("//foo/Foo.xcassets/foo.imageset/FooEmpty-29@3x.png"));
  f.sources().push_back(
      SourceFile("//foo/Foo.xcassets/file/with/no/known/pattern"));
  f.sources().push_back(
      SourceFile("//foo/Foo.xcassets/nested/bar.xcassets/my/file"));
  f.action_values().outputs() = SubstitutionList::MakeForTest(
      "{{bundle_resources_dir}}/{{source_file_part}}");
  ASSERT_TRUE(f.OnResolved(&err));

  ASSERT_TRUE(e.OnResolved(&err));
  ASSERT_TRUE(c.OnResolved(&err));
  ASSERT_TRUE(a.OnResolved(&err));

  // A gets its data from B and F.
  ASSERT_EQ(a.bundle_data().file_rules().size(), 2u);
  ASSERT_EQ(a.bundle_data().file_rules()[0].sources().size(), 2u);
  ASSERT_EQ(a.bundle_data().file_rules()[1].sources().size(), 3u);
  ASSERT_EQ(a.bundle_data().assets_catalog_sources().size(), 1u);
  ASSERT_EQ(a.bundle_data().forwarded_bundle_deps().size(), 2u);

  // C gets its data from D.
  ASSERT_EQ(c.bundle_data().file_rules().size(), 1u);
  ASSERT_EQ(c.bundle_data().file_rules()[0].sources().size(), 1u);
  ASSERT_EQ(c.bundle_data().forwarded_bundle_deps().size(), 1u);

  // E does not have any bundle_data information but gets a list of
  // forwarded_bundle_deps to propagate them during target resolution.
  ASSERT_TRUE(e.bundle_data().file_rules().empty());
  ASSERT_TRUE(e.bundle_data().assets_catalog_sources().empty());
  ASSERT_EQ(e.bundle_data().forwarded_bundle_deps().size(), 2u);
}

TEST(TargetTest, CollectMetadataNoRecurse) {
  TestWithScope setup;

  TestTarget one(setup, "//foo:one", Target::SOURCE_SET);
  Value a_expected(nullptr, Value::LIST);
  a_expected.list_value().push_back(Value(nullptr, "foo"));
  one.metadata().contents().insert(
      std::pair<std::string_view, Value>("a", a_expected));

  Value b_expected(nullptr, Value::LIST);
  b_expected.list_value().push_back(Value(nullptr, true));
  one.metadata().contents().insert(
      std::pair<std::string_view, Value>("b", b_expected));

  one.metadata().set_source_dir(SourceDir("/usr/home/files/"));

  std::vector<std::string> data_keys;
  data_keys.push_back("a");
  data_keys.push_back("b");

  std::vector<std::string> walk_keys;

  Err err;
  std::vector<Value> result;
  TargetSet targets;
  one.GetMetadata(data_keys, walk_keys, SourceDir(), false, &result, &targets,
                  &err);
  EXPECT_FALSE(err.has_error());

  std::vector<Value> expected;
  expected.push_back(Value(nullptr, "foo"));
  expected.push_back(Value(nullptr, true));
  EXPECT_EQ(result, expected);
}

TEST(TargetTest, CollectMetadataWithRecurse) {
  TestWithScope setup;

  TestTarget one(setup, "//foo:one", Target::SOURCE_SET);
  Value a_expected(nullptr, Value::LIST);
  a_expected.list_value().push_back(Value(nullptr, "foo"));
  one.metadata().contents().insert(
      std::pair<std::string_view, Value>("a", a_expected));

  Value b_expected(nullptr, Value::LIST);
  b_expected.list_value().push_back(Value(nullptr, true));
  one.metadata().contents().insert(
      std::pair<std::string_view, Value>("b", b_expected));

  TestTarget two(setup, "//foo:two", Target::SOURCE_SET);
  Value a_2_expected(nullptr, Value::LIST);
  a_2_expected.list_value().push_back(Value(nullptr, "bar"));
  two.metadata().contents().insert(
      std::pair<std::string_view, Value>("a", a_2_expected));

  one.public_deps().push_back(LabelTargetPair(&two));

  std::vector<std::string> data_keys;
  data_keys.push_back("a");
  data_keys.push_back("b");

  std::vector<std::string> walk_keys;

  Err err;
  std::vector<Value> result;
  TargetSet targets;
  one.GetMetadata(data_keys, walk_keys, SourceDir(), false, &result, &targets,
                  &err);
  EXPECT_FALSE(err.has_error());

  std::vector<Value> expected;
  expected.push_back(Value(nullptr, "bar"));
  expected.push_back(Value(nullptr, "foo"));
  expected.push_back(Value(nullptr, true));
  EXPECT_EQ(result, expected);
}

TEST(TargetTest, CollectMetadataWithRecurseHole) {
  TestWithScope setup;

  TestTarget one(setup, "//foo:one", Target::SOURCE_SET);
  Value a_expected(nullptr, Value::LIST);
  a_expected.list_value().push_back(Value(nullptr, "foo"));
  one.metadata().contents().insert(
      std::pair<std::string_view, Value>("a", a_expected));

  Value b_expected(nullptr, Value::LIST);
  b_expected.list_value().push_back(Value(nullptr, true));
  one.metadata().contents().insert(
      std::pair<std::string_view, Value>("b", b_expected));

  // Target two does not have metadata but depends on three
  // which does.
  TestTarget two(setup, "//foo:two", Target::SOURCE_SET);

  TestTarget three(setup, "//foo:three", Target::SOURCE_SET);
  Value a_3_expected(nullptr, Value::LIST);
  a_3_expected.list_value().push_back(Value(nullptr, "bar"));
  three.metadata().contents().insert(
      std::pair<std::string_view, Value>("a", a_3_expected));

  one.public_deps().push_back(LabelTargetPair(&two));
  two.public_deps().push_back(LabelTargetPair(&three));

  std::vector<std::string> data_keys;
  data_keys.push_back("a");
  data_keys.push_back("b");

  std::vector<std::string> walk_keys;

  Err err;
  std::vector<Value> result;
  TargetSet targets;
  one.GetMetadata(data_keys, walk_keys, SourceDir(), false, &result, &targets,
                  &err);
  EXPECT_FALSE(err.has_error());

  std::vector<Value> expected;
  expected.push_back(Value(nullptr, "bar"));
  expected.push_back(Value(nullptr, "foo"));
  expected.push_back(Value(nullptr, true));
  EXPECT_EQ(result, expected);
}

TEST(TargetTest, CollectMetadataWithBarrier) {
  TestWithScope setup;

  TestTarget one(setup, "//foo:one", Target::SOURCE_SET);
  Value a_expected(nullptr, Value::LIST);
  a_expected.list_value().push_back(Value(nullptr, "foo"));
  one.metadata().contents().insert(
      std::pair<std::string_view, Value>("a", a_expected));

  Value walk_expected(nullptr, Value::LIST);
  walk_expected.list_value().push_back(Value(nullptr, "two"));
  one.metadata().contents().insert(
      std::pair<std::string_view, Value>("walk", walk_expected));

  TestTarget two(setup, "//foo/two:two", Target::SOURCE_SET);
  Value a_2_expected(nullptr, Value::LIST);
  a_2_expected.list_value().push_back(Value(nullptr, "bar"));
  two.metadata().contents().insert(
      std::pair<std::string_view, Value>("a", a_2_expected));

  TestTarget three(setup, "//foo:three", Target::SOURCE_SET);
  Value a_3_expected(nullptr, Value::LIST);
  a_3_expected.list_value().push_back(Value(nullptr, "baz"));
  three.metadata().contents().insert(
      std::pair<std::string_view, Value>("a", a_3_expected));

  one.private_deps().push_back(LabelTargetPair(&two));
  one.public_deps().push_back(LabelTargetPair(&three));

  std::vector<std::string> data_keys;
  data_keys.push_back("a");

  std::vector<std::string> walk_keys;
  walk_keys.push_back("walk");

  Err err;
  std::vector<Value> result;
  TargetSet targets;
  one.GetMetadata(data_keys, walk_keys, SourceDir(), false, &result, &targets,
                  &err);
  EXPECT_FALSE(err.has_error()) << err.message();

  std::vector<Value> expected;
  expected.push_back(Value(nullptr, "bar"));
  expected.push_back(Value(nullptr, "foo"));
  EXPECT_EQ(result, expected) << result.size();
}

TEST(TargetTest, CollectMetadataWithError) {
  TestWithScope setup;

  TestTarget one(setup, "//foo:one", Target::SOURCE_SET);
  Value a_expected(nullptr, Value::LIST);
  a_expected.list_value().push_back(Value(nullptr, "foo"));
  one.metadata().contents().insert(
      std::pair<std::string_view, Value>("a", a_expected));

  Value walk_expected(nullptr, Value::LIST);
  walk_expected.list_value().push_back(Value(nullptr, "//foo:missing"));
  one.metadata().contents().insert(
      std::pair<std::string_view, Value>("walk", walk_expected));

  std::vector<std::string> data_keys;
  data_keys.push_back("a");

  std::vector<std::string> walk_keys;
  walk_keys.push_back("walk");

  Err err;
  std::vector<Value> result;
  TargetSet targets;
  one.GetMetadata(data_keys, walk_keys, SourceDir(), false, &result, &targets,
                  &err);
  EXPECT_TRUE(err.has_error());
  EXPECT_EQ(err.message(),
            "I was expecting //foo:missing(//toolchain:default) to be a "
            "dependency of //foo:one(//toolchain:default). "
            "Make sure it's included in the deps or data_deps, and that you've "
            "specified the appropriate toolchain.")
      << err.message();
}

TEST_F(TargetTest, WriteMetadataCollection) {
  TestWithScope setup;
  Err err;

  SourceFile source_file("//out/Debug/metadata.json");
  OutputFile output_file(setup.build_settings(), source_file);

  TestTarget generator(setup, "//foo:write", Target::GENERATED_FILE);
  generator.action_values().outputs() =
      SubstitutionList::MakeForTest("//out/Debug/metadata.json");
  EXPECT_TRUE(generator.OnResolved(&err));

  TestTarget middle_data_dep(setup, "//foo:middle", Target::EXECUTABLE);
  middle_data_dep.data_deps().push_back(LabelTargetPair(&generator));
  EXPECT_TRUE(middle_data_dep.OnResolved(&err));

  // This target has a generated metadata input and no dependency makes it.
  TestTarget dep_missing(setup, "//foo:no_dep", Target::EXECUTABLE);
  dep_missing.sources().push_back(source_file);
  EXPECT_TRUE(dep_missing.OnResolved(&err));
  AssertSchedulerHasOneUnknownFileMatching(&dep_missing, source_file);
  scheduler().ClearUnknownGeneratedInputsAndWrittenFiles();

  // This target has a generated file and we've directly dependended on it.
  TestTarget dep_present(setup, "//foo:with_dep", Target::EXECUTABLE);
  dep_present.sources().push_back(source_file);
  dep_present.private_deps().push_back(LabelTargetPair(&generator));
  EXPECT_TRUE(dep_present.OnResolved(&err));
  EXPECT_TRUE(scheduler().GetUnknownGeneratedInputs().empty());

  // This target has a generated file and we've indirectly dependended on it
  // via data_deps.
  TestTarget dep_indirect(setup, "//foo:indirect_dep", Target::EXECUTABLE);
  dep_indirect.sources().push_back(source_file);
  dep_indirect.data_deps().push_back(LabelTargetPair(&middle_data_dep));
  EXPECT_TRUE(dep_indirect.OnResolved(&err));
  AssertSchedulerHasOneUnknownFileMatching(&dep_indirect, source_file);
  scheduler().ClearUnknownGeneratedInputsAndWrittenFiles();

  // This target has a generated file and we've directly dependended on it
  // via data_deps.
  TestTarget data_dep_present(setup, "//foo:with_data_dep", Target::EXECUTABLE);
  data_dep_present.sources().push_back(source_file);
  data_dep_present.data_deps().push_back(LabelTargetPair(&generator));
  EXPECT_TRUE(data_dep_present.OnResolved(&err));
  EXPECT_TRUE(scheduler().GetUnknownGeneratedInputs().empty());
}

// Tests that modulemap files use the cxx_module tool.
TEST_F(TargetTest, ModuleMap) {
  TestWithScope setup;

  Toolchain toolchain(setup.settings(), Label(SourceDir("//tc/"), "tc"));

  std::unique_ptr<Tool> tool = Tool::CreateTool(CTool::kCToolCxxModule);
  CTool* cxx_module = tool->AsC();
  cxx_module->set_outputs(
      SubstitutionList::MakeForTest("{{source_file_part}}.pcm"));
  toolchain.SetTool(std::move(tool));

  Target target(setup.settings(), Label(SourceDir("//a/"), "a"));
  target.set_output_type(Target::SOURCE_SET);
  target.SetToolchain(&toolchain);
  Err err;
  ASSERT_TRUE(target.OnResolved(&err));

  const char* computed_tool_type = nullptr;
  std::vector<OutputFile> output;
  bool result = target.GetOutputFilesForSource(
      SourceFile("//source/input.modulemap"), &computed_tool_type, &output);
  ASSERT_TRUE(result);
  EXPECT_EQ(std::string("cxx_module"), std::string(computed_tool_type));

  // Outputs are relative to the build directory "//out/Debug/".
  ASSERT_EQ(1u, output.size());
  EXPECT_EQ("input.modulemap.pcm", output[0].value()) << output[0].value();
}

TEST(TargetTest, CollectMetadataWithValidation) {
  TestWithScope setup;

  TestTarget a(setup, "//foo:a", Target::SOURCE_SET);
  Value a_expected(nullptr, Value::LIST);
  a_expected.list_value().push_back(Value(nullptr, "foo"));
  a.metadata().contents().emplace("walk", a_expected);

  TestTarget b(setup, "//foo:b", Target::SOURCE_SET);
  Value b_expected(nullptr, Value::LIST);
  b_expected.list_value().push_back(Value(nullptr, "bar"));
  b.metadata().contents().emplace("walk", b_expected);

  a.validations().push_back(LabelTargetPair(&b));

  std::vector<std::string> data_keys = {"walk"};
  std::vector<std::string> walk_keys;
  std::vector<Value> result;
  TargetSet targets;
  Err err;
  a.GetMetadata(data_keys, walk_keys, SourceDir(), false, &result, &targets,
                &err);
  EXPECT_FALSE(err.has_error());

  std::vector<Value> expected = {Value(nullptr, "bar"), Value(nullptr, "foo")};
  EXPECT_EQ(result, expected);
}