| // Copyright 2026 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 <algorithm> |
| |
| #include "base/files/file_util.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "gn/filesystem_utils.h" |
| #include "gn/functions.h" |
| #include "gn/input_file.h" |
| #include "gn/parse_tree.h" |
| #include "gn/test_with_scheduler.h" |
| #include "gn/test_with_scope.h" |
| #include "util/test/test.h" |
| |
| class ExpandDirectoryTest : public TestWithScheduler { |
| protected: |
| ExpandDirectoryTest() { |
| CHECK(temp_dir_.CreateUniqueTempDir()); |
| setup.build_settings()->SetRootPath(temp_dir_.GetPath()); |
| } |
| |
| base::ScopedTempDir temp_dir_; |
| TestWithScope setup; |
| |
| std::unique_ptr<FunctionCallNode> SetupDefaultDirAndFunction( |
| InputFile* input_file, |
| base::FilePath dir) { |
| auto file1 = dir.AppendASCII("file1.txt"); |
| auto file2 = dir.AppendASCII("file2.txt"); |
| auto sub_dir = dir.AppendASCII("sub"); |
| auto file3 = sub_dir.AppendASCII("file3.txt"); |
| |
| EXPECT_TRUE(base::CreateDirectory(sub_dir)); |
| EXPECT_TRUE(WriteFile(file1, "content1", nullptr)); |
| EXPECT_TRUE(WriteFile(file2, "content2", nullptr)); |
| EXPECT_TRUE(WriteFile(file3, "content3", nullptr)); |
| |
| Location location(input_file, 1, 1); |
| Token token(location, Token::IDENTIFIER, "expand_directory"); |
| auto function = std::make_unique<FunctionCallNode>(); |
| function->set_function(token); |
| |
| auto args = std::make_unique<ListNode>(); |
| args->set_begin_token(token); |
| args->set_end(std::make_unique<EndNode>(token)); |
| function->set_args(std::move(args)); |
| |
| auto allowlist = std::make_unique<SourceFileSet>(); |
| allowlist->insert(input_file->name()); |
| setup.build_settings()->set_expand_directory_allowlist( |
| std::move(allowlist)); |
| return function; |
| } |
| }; |
| |
| TEST_F(ExpandDirectoryTest, Recursive) { |
| auto dir_path = temp_dir_.GetPath().AppendASCII("foo").AppendASCII("bar"); |
| auto input_file = InputFile(SourceFile("//BUILD.gn")); |
| auto function = SetupDefaultDirAndFunction(&input_file, dir_path); |
| |
| Err err; |
| Value result = functions::RunExpandDirectory( |
| setup.scope(), function.get(), |
| {Value(nullptr, "//foo/bar"), Value(nullptr, true)}, &err); |
| ASSERT_SUCCESS(err); |
| |
| ASSERT_EQ(result.type(), Value::LIST); |
| ASSERT_EQ(result.list_value().size(), 3); |
| EXPECT_EQ(result.list_value()[0].string_value(), "//foo/bar/file1.txt"); |
| EXPECT_EQ(result.list_value()[1].string_value(), "//foo/bar/file2.txt"); |
| EXPECT_EQ(result.list_value()[2].string_value(), "//foo/bar/sub/file3.txt"); |
| |
| std::vector<base::FilePath> deps = scheduler().GetGenDependencies(); |
| EXPECT_TRUE(std::ranges::find(deps, dir_path) != deps.end()) |
| << FilePathToUTF8(dir_path); |
| auto sub = dir_path.AppendASCII("sub"); |
| EXPECT_TRUE(std::ranges::find(deps, sub) != deps.end()) |
| << FilePathToUTF8(sub); |
| } |
| |
| TEST_F(ExpandDirectoryTest, NonRecursive) { |
| auto dir_path = temp_dir_.GetPath().AppendASCII("foo").AppendASCII("bar"); |
| auto input_file = InputFile(SourceFile("//foo/BUILD.gn")); |
| auto function = SetupDefaultDirAndFunction(&input_file, dir_path); |
| setup.scope()->set_source_dir(SourceDir("//foo/")); |
| |
| Err err; |
| Value result = functions::RunExpandDirectory( |
| setup.scope(), function.get(), |
| {Value(nullptr, "bar"), Value(nullptr, false)}, &err); |
| ASSERT_SUCCESS(err); |
| ASSERT_EQ(result.type(), Value::LIST); |
| ASSERT_EQ(result.list_value().size(), 2); |
| EXPECT_EQ(result.list_value()[0].string_value(), "//foo/bar/file1.txt"); |
| EXPECT_EQ(result.list_value()[1].string_value(), "//foo/bar/file2.txt"); |
| |
| std::vector<base::FilePath> deps = scheduler().GetGenDependencies(); |
| EXPECT_TRUE(std::ranges::find(deps, dir_path) != deps.end()); |
| EXPECT_TRUE(std::ranges::find(deps, dir_path.AppendASCII("sub")) == |
| deps.end()); |
| } |
| |
| TEST_F(ExpandDirectoryTest, EmptyDir) { |
| std::string dir_str = FilePathToUTF8(temp_dir_.GetPath()); |
| |
| FunctionCallNode function; |
| Err err; |
| Value result = functions::RunExpandDirectory( |
| setup.scope(), &function, {Value(nullptr, dir_str), Value(nullptr, true)}, |
| &err); |
| ASSERT_SUCCESS(err); |
| ASSERT_EQ(result.type(), Value::LIST); |
| ASSERT_EQ(result.list_value().size(), 0); |
| } |
| |
| TEST_F(ExpandDirectoryTest, NonExistentDir) { |
| base::FilePath non_existent = temp_dir_.GetPath().AppendASCII("non_existent"); |
| |
| FunctionCallNode function; |
| Err err; |
| Value result = functions::RunExpandDirectory( |
| setup.scope(), &function, |
| {Value(nullptr, FilePathToUTF8(non_existent)), Value(nullptr, true)}, |
| &err); |
| EXPECT_TRUE(err.has_error()); |
| } |
| |
| TEST_F(ExpandDirectoryTest, Allowlist) { |
| InputFile input_file(SourceFile("//BUILD.gn")); |
| Location location(&input_file, 1, 1); |
| Token token(location, Token::IDENTIFIER, "expand_directory"); |
| FunctionCallNode function; |
| function.set_function(token); |
| |
| auto args = std::make_unique<ListNode>(); |
| args->set_begin_token(token); |
| args->set_end(std::make_unique<EndNode>(token)); |
| function.set_args(std::move(args)); |
| |
| // No allowlist |
| { |
| Err err; |
| Value result = functions::RunExpandDirectory( |
| setup.scope(), &function, |
| {Value(nullptr, FilePathToUTF8(temp_dir_.GetPath())), |
| Value(nullptr, true)}, |
| &err); |
| EXPECT_TRUE(err.has_error()); |
| } |
| |
| // Empty allowlist |
| auto allowlist_owned = std::make_unique<SourceFileSet>(); |
| auto allowlist = allowlist_owned.get(); |
| setup.build_settings()->set_expand_directory_allowlist( |
| std::move(allowlist_owned)); |
| allowlist->insert(SourceFile("//foo.gni")); |
| { |
| Err err; |
| Value result = functions::RunExpandDirectory( |
| setup.scope(), &function, |
| {Value(nullptr, FilePathToUTF8(temp_dir_.GetPath())), |
| Value(nullptr, true)}, |
| &err); |
| EXPECT_TRUE(err.has_error()); |
| } |
| } |