blob: 8725c2ecd9d1397ad434eeba211d1208f47955e5 [file] [log] [blame]
// Copyright 2014 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 "tools/gn/functions.h"
#include <memory>
#include <utility>
#include "tools/gn/parse_tree.h"
#include "tools/gn/test_with_scope.h"
#include "tools/gn/value.h"
#include "util/test/test.h"
TEST(Functions, Defined) {
TestWithScope setup;
FunctionCallNode function_call;
Err err;
// Test an undefined identifier.
Token undefined_token(Location(), Token::IDENTIFIER, "undef");
ListNode args_list_identifier_undefined;
args_list_identifier_undefined.append_item(
std::make_unique<IdentifierNode>(undefined_token));
Value result = functions::RunDefined(setup.scope(), &function_call,
&args_list_identifier_undefined, &err);
ASSERT_EQ(Value::BOOLEAN, result.type());
EXPECT_FALSE(result.boolean_value());
// Define a value that's itself a scope value.
const char kDef[] = "def"; // Defined variable name.
setup.scope()->SetValue(
kDef, Value(nullptr, std::make_unique<Scope>(setup.scope())), nullptr);
// Test the defined identifier.
Token defined_token(Location(), Token::IDENTIFIER, kDef);
ListNode args_list_identifier_defined;
args_list_identifier_defined.append_item(
std::make_unique<IdentifierNode>(defined_token));
result = functions::RunDefined(setup.scope(), &function_call,
&args_list_identifier_defined, &err);
ASSERT_EQ(Value::BOOLEAN, result.type());
EXPECT_TRUE(result.boolean_value());
// Should also work by passing an accessor node so you can do
// "defined(def.foo)" to see if foo is defined on the def scope.
std::unique_ptr<AccessorNode> undef_accessor =
std::make_unique<AccessorNode>();
undef_accessor->set_base(defined_token);
undef_accessor->set_member(std::make_unique<IdentifierNode>(undefined_token));
ListNode args_list_accessor_defined;
args_list_accessor_defined.append_item(std::move(undef_accessor));
result = functions::RunDefined(setup.scope(), &function_call,
&args_list_accessor_defined, &err);
ASSERT_EQ(Value::BOOLEAN, result.type());
EXPECT_FALSE(result.boolean_value());
}
// Tests that an error is thrown when a {} is supplied to a function that
// doesn't take one.
TEST(Functions, FunctionsWithBlock) {
TestWithScope setup;
Err err;
// No scope to print() is OK.
TestParseInput print_no_scope("print(6)");
EXPECT_FALSE(print_no_scope.has_error());
Value result = print_no_scope.parsed()->Execute(setup.scope(), &err);
EXPECT_FALSE(err.has_error());
// Passing a scope should pass parsing (it doesn't know about what kind of
// function it is) and then throw an error during execution.
TestParseInput print_with_scope("print(foo) {}");
EXPECT_FALSE(print_with_scope.has_error());
result = print_with_scope.parsed()->Execute(setup.scope(), &err);
EXPECT_TRUE(err.has_error());
err = Err();
// defined() is a special function so test it separately.
TestParseInput defined_no_scope("defined(foo)");
EXPECT_FALSE(defined_no_scope.has_error());
result = defined_no_scope.parsed()->Execute(setup.scope(), &err);
EXPECT_FALSE(err.has_error());
// A block to defined should fail.
TestParseInput defined_with_scope("defined(foo) {}");
EXPECT_FALSE(defined_with_scope.has_error());
result = defined_with_scope.parsed()->Execute(setup.scope(), &err);
EXPECT_TRUE(err.has_error());
}
TEST(Functions, SplitList) {
TestWithScope setup;
TestParseInput input(
// Empty input with varying result items.
"out1 = split_list([], 1)\n"
"out2 = split_list([], 3)\n"
"print(\"empty = $out1 $out2\")\n"
// One item input.
"out3 = split_list([1], 1)\n"
"out4 = split_list([1], 2)\n"
"print(\"one = $out3 $out4\")\n"
// Multiple items.
"out5 = split_list([1, 2, 3, 4, 5, 6, 7, 8, 9], 2)\n"
"print(\"many = $out5\")\n"
// Rounding.
"out6 = split_list([1, 2, 3, 4, 5, 6], 4)\n"
"print(\"rounding = $out6\")\n");
ASSERT_FALSE(input.has_error());
Err err;
input.parsed()->Execute(setup.scope(), &err);
ASSERT_FALSE(err.has_error()) << err.message();
EXPECT_EQ(
"empty = [[]] [[], [], []]\n"
"one = [[1]] [[1], []]\n"
"many = [[1, 2, 3, 4, 5], [6, 7, 8, 9]]\n"
"rounding = [[1, 2], [3, 4], [5], [6]]\n",
setup.print_output());
}
TEST(Functions, StringReplace) {
TestWithScope setup;
TestParseInput input(
// Replace all occurrences of string.
"out1 = string_replace(\"abbcc\", \"b\", \"d\")\n"
"print(out1)\n"
// Replace only the first occurrence.
"out2 = string_replace(\"abbcc\", \"b\", \"d\", 1)\n"
"print(out2)\n"
// Duplicate string to be replaced.
"out3 = string_replace(\"abbcc\", \"b\", \"bb\")\n"
"print(out3)\n"
// Handle overlapping occurrences.
"out4 = string_replace(\"aaa\", \"aa\", \"b\")\n"
"print(out4)\n");
ASSERT_FALSE(input.has_error());
Err err;
input.parsed()->Execute(setup.scope(), &err);
ASSERT_FALSE(err.has_error()) << err.message();
EXPECT_EQ(
"addcc\n"
"adbcc\n"
"abbbbcc\n"
"ba\n",
setup.print_output());
}
TEST(Functions, DeclareArgs) {
TestWithScope setup;
Err err;
// It is not legal to read the value of an argument declared in a
// declare_args() from inside the call, but outside the call and in
// a separate call should work.
TestParseInput reading_from_same_call(R"(
declare_args() {
foo = true
bar = foo
})");
reading_from_same_call.parsed()->Execute(setup.scope(), &err);
ASSERT_TRUE(err.has_error());
TestParseInput reading_from_outside_call(R"(
declare_args() {
foo = true
}
bar = foo
assert(bar)
)");
err = Err();
reading_from_outside_call.parsed()->Execute(setup.scope(), &err);
ASSERT_FALSE(err.has_error());
TestParseInput reading_from_different_call(R"(
declare_args() {
foo = true
}
declare_args() {
bar = foo
}
assert(bar)
)");
err = Err();
TestWithScope setup2;
reading_from_different_call.parsed()->Execute(setup2.scope(), &err);
ASSERT_FALSE(err.has_error());
}
TEST(Functions, NotNeeded) {
TestWithScope setup;
TestParseInput input("not_needed({ a = 1 }, \"*\")");
ASSERT_FALSE(input.has_error());
Err err;
input.parsed()->Execute(setup.scope(), &err);
ASSERT_FALSE(err.has_error())
<< err.message() << err.location().Describe(true);
}