| // 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 "gn/functions.h" | |
| #include <memory> | |
| #include <utility> | |
| #include "gn/parse_tree.h" | |
| #include "gn/test_with_scope.h" | |
| #include "gn/value.h" | |
| #include "util/test/test.h" | |
| TEST(Functions, Assert) { | |
| TestWithScope setup; | |
| // Verify cases where the assertion passes. | |
| std::vector<std::string> assert_pass_examples = { | |
| R"gn(assert(true))gn", | |
| R"gn(assert(true, "This message is ignored for passed assertions."))gn", | |
| }; | |
| for (const auto& assert_pass_example : assert_pass_examples) { | |
| TestParseInput input(assert_pass_example); | |
| ASSERT_SUCCESS(input); | |
| Err err; | |
| input.parsed()->Execute(setup.scope(), &err); | |
| ASSERT_FALSE(err.has_error()) << assert_pass_example; | |
| } | |
| // Verify case where the assertion fails, with no message. | |
| { | |
| TestParseInput input("assert(false)"); | |
| ASSERT_SUCCESS(input); | |
| Err err; | |
| input.parsed()->Execute(setup.scope(), &err); | |
| ASSERT_TRUE(err.has_error()); | |
| ASSERT_EQ(err.message(), "Assertion failed."); | |
| } | |
| // Verify case where the assertion fails, with a message. | |
| { | |
| TestParseInput input("assert(false, \"What failed\")"); | |
| ASSERT_SUCCESS(input); | |
| Err err; | |
| input.parsed()->Execute(setup.scope(), &err); | |
| ASSERT_TRUE(err.has_error()); | |
| ASSERT_EQ(err.message(), "Assertion failed."); | |
| ASSERT_EQ(err.help_text(), "What failed"); | |
| } | |
| // Verify usage errors are detected. | |
| std::vector<std::string> bad_usage_examples = { | |
| // Number of arguments. | |
| R"gn(assert())gn", | |
| R"gn(assert(1, 2, 3))gn", | |
| // Argument types. | |
| R"gn(assert(1))gn", | |
| R"gn(assert("oops"))gn", | |
| R"gn(assert(true, 1))gn", | |
| R"gn(assert(true, []))gn", | |
| }; | |
| for (const auto& bad_usage_example : bad_usage_examples) { | |
| TestParseInput input(bad_usage_example); | |
| ASSERT_SUCCESS(input); | |
| Err err; | |
| input.parsed()->Execute(setup.scope(), &err); | |
| ASSERT_TRUE(err.has_error()) << bad_usage_example; | |
| // We are checking for usage errors, not assertion failures. | |
| ASSERT_NE(err.message(), "Assertion failed.") << bad_usage_example; | |
| } | |
| } | |
| 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()); | |
| // Should also work by pasing an accessor node so you can do | |
| // "defined(def["foo"])" to see if foo is defined on the def scope. | |
| std::unique_ptr<AccessorNode> subscript_accessor = | |
| std::make_unique<AccessorNode>(); | |
| subscript_accessor->set_base(defined_token); | |
| subscript_accessor->set_subscript( | |
| std::make_unique<LiteralNode>(Token(Location(), Token::STRING, "foo"))); | |
| ListNode args_list_subscript_accessor_defined; | |
| args_list_subscript_accessor_defined.append_item( | |
| std::move(subscript_accessor)); | |
| result = functions::RunDefined(setup.scope(), &function_call, | |
| &args_list_subscript_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_SUCCESS(err); | |
| // 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_SUCCESS(err); | |
| // 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_SUCCESS(input); | |
| Err err; | |
| input.parsed()->Execute(setup.scope(), &err); | |
| ASSERT_SUCCESS(err); | |
| 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, StringHash) { | |
| TestWithScope setup; | |
| // Verify output when string_hash() is called correctly. | |
| { | |
| TestParseInput input(R"gn( | |
| print("<" + string_hash("abc") + ">") | |
| # Empty string | |
| print("<" + string_hash("") + ">") | |
| )gn"); | |
| ASSERT_SUCCESS(input); | |
| Err err; | |
| input.parsed()->Execute(setup.scope(), &err); | |
| ASSERT_SUCCESS(err); | |
| EXPECT_EQ( | |
| "<ba7816bf>\n" | |
| "<e3b0c442>\n", | |
| setup.print_output()) | |
| << setup.print_output(); | |
| } | |
| // Verify usage errors are detected. | |
| std::vector<std::string> bad_usage_examples = { | |
| // Number of arguments. | |
| R"gn(string_hash())gn", | |
| R"gn(string_hash(1,2))gn", | |
| // Argument type. | |
| R"gn(string_hash(1))gn", | |
| R"gn(string_hash(["oops"]))gn", | |
| }; | |
| for (const auto& bad_usage_example : bad_usage_examples) { | |
| TestParseInput input(bad_usage_example); | |
| ASSERT_SUCCESS(input); | |
| Err err; | |
| input.parsed()->Execute(setup.scope(), &err); | |
| ASSERT_TRUE(err.has_error()) << bad_usage_example; | |
| } | |
| } | |
| TEST(Functions, StringJoin) { | |
| TestWithScope setup; | |
| // Verify outputs when string_join() is called correctly. | |
| { | |
| TestParseInput input(R"gn( | |
| # No elements in the list and empty separator. | |
| print("<" + string_join("", []) + ">") | |
| # No elements in the list. | |
| print("<" + string_join(" ", []) + ">") | |
| # One element in the list. | |
| print(string_join("|", ["a"])) | |
| # Multiple elements in the list. | |
| print(string_join(" ", ["a", "b", "c"])) | |
| # Multi-character separator. | |
| print(string_join("-.", ["a", "b", "c"])) | |
| # Empty separator. | |
| print(string_join("", ["x", "y", "z"])) | |
| # Empty string list elements. | |
| print(string_join("x", ["", "", ""])) | |
| # Empty string list elements and separator | |
| print(string_join("", ["", "", ""])) | |
| )gn"); | |
| ASSERT_SUCCESS(input); | |
| Err err; | |
| input.parsed()->Execute(setup.scope(), &err); | |
| ASSERT_SUCCESS(err); | |
| EXPECT_EQ( | |
| "<>\n" | |
| "<>\n" | |
| "a\n" | |
| "a b c\n" | |
| "a-.b-.c\n" | |
| "xyz\n" | |
| "xx\n" | |
| "\n", | |
| setup.print_output()) | |
| << setup.print_output(); | |
| } | |
| // Verify usage errors are detected. | |
| std::vector<std::string> bad_usage_examples = { | |
| // Number of arguments. | |
| R"gn(string_join())gn", | |
| R"gn(string_join(["oops"]))gn", | |
| R"gn(string_join("kk", [], "oops"))gn", | |
| // Argument types. | |
| R"gn(string_join(1, []))gn", | |
| R"gn(string_join("kk", "oops"))gn", | |
| R"gn(string_join(["oops"], []))gn", | |
| // Non-string elements in list of strings. | |
| R"gn(string_join("kk", [1]))gn", | |
| R"gn(string_join("kk", ["hello", 1]))gn", | |
| R"gn(string_join("kk", ["hello", []]))gn", | |
| }; | |
| for (const auto& bad_usage_example : bad_usage_examples) { | |
| TestParseInput input(bad_usage_example); | |
| ASSERT_SUCCESS(input); | |
| Err err; | |
| input.parsed()->Execute(setup.scope(), &err); | |
| ASSERT_TRUE(err.has_error()) << bad_usage_example; | |
| } | |
| } | |
| 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_SUCCESS(input); | |
| Err err; | |
| input.parsed()->Execute(setup.scope(), &err); | |
| ASSERT_SUCCESS(err); | |
| EXPECT_EQ( | |
| "addcc\n" | |
| "adbcc\n" | |
| "abbbbcc\n" | |
| "ba\n", | |
| setup.print_output()); | |
| } | |
| TEST(Functions, StringSplit) { | |
| TestWithScope setup; | |
| // Verify outputs when string_join() is called correctly. | |
| { | |
| TestParseInput input(R"gn( | |
| # Split on all whitespace: empty string. | |
| print(string_split("")) | |
| # Split on all whitespace: string is only whitespace | |
| print(string_split(" ")) | |
| # Split on all whitespace: leading, trailing, runs; one element. | |
| print(string_split("hello")) | |
| print(string_split(" hello")) | |
| print(string_split(" hello ")) | |
| print(string_split("hello ")) | |
| # Split on all whitespace: leading, trailing, runs; multiple elements. | |
| print(string_split("a b")) # Pre-stripped | |
| print(string_split(" a b")) # Leading whitespace | |
| print(string_split(" a b ")) # Leading & trailing whitespace | |
| print(string_split("a b ")) # Trailing whitespace | |
| print(string_split("a b ")) # Whitespace run between words | |
| print(string_split(" a b cc ddd")) # More & multi-character elements | |
| # Split on string. | |
| print(string_split("", "|")) # Empty string | |
| print(string_split("|", "|")) # Only a separator | |
| print(string_split("||", "|")) # Only separators | |
| print(string_split("ab", "|")) # String is missing separator | |
| print(string_split("a|b", "|")) # Two elements | |
| print(string_split("|a|b", "|")) # Leading separator | |
| print(string_split("a|b|", "|")) # Trailing separator | |
| print(string_split("||x", "|")) # Leading consecutive separators | |
| print(string_split("x||", "|")) # Trailing consecutive separators | |
| print(string_split("a|bb|ccc", "|")) # Multiple elements | |
| print(string_split(".x.x.x.", ".x.")) # Self-overlapping separators 1 | |
| print(string_split("x.x.x.", ".x.")) # Self-overlapping separators 2 | |
| )gn"); | |
| ASSERT_SUCCESS(input); | |
| Err err; | |
| input.parsed()->Execute(setup.scope(), &err); | |
| ASSERT_SUCCESS(err); | |
| EXPECT_EQ( | |
| // Split on all whitespace: empty string. | |
| "[]\n" | |
| // Split on all whitespace: string is only whitespace. | |
| "[]\n" | |
| // Split on all whitespace: leading, trailing, runs; one element. | |
| "[\"hello\"]\n" | |
| "[\"hello\"]\n" | |
| "[\"hello\"]\n" | |
| "[\"hello\"]\n" | |
| // Split on all whitespace: leading, trailing, runs; multiple elements. | |
| "[\"a\", \"b\"]\n" | |
| "[\"a\", \"b\"]\n" | |
| "[\"a\", \"b\"]\n" | |
| "[\"a\", \"b\"]\n" | |
| "[\"a\", \"b\"]\n" | |
| "[\"a\", \"b\", \"cc\", \"ddd\"]\n" | |
| // Split on string. | |
| "[\"\"]\n" // Empty string (like Python) | |
| "[\"\", \"\"]\n" // Only a separator | |
| "[\"\", \"\", \"\"]\n" // Only separators | |
| "[\"ab\"]\n" // String is missing separator | |
| "[\"a\", \"b\"]\n" // Two elements | |
| "[\"\", \"a\", \"b\"]\n" // Leading | |
| "[\"a\", \"b\", \"\"]\n" // Trailing | |
| "[\"\", \"\", \"x\"]\n" // Leading consecutive separators | |
| "[\"x\", \"\", \"\"]\n" // Trailing consecutive separators | |
| "[\"a\", \"bb\", \"ccc\"]\n" // Multiple elements | |
| "[\"\", \"x\", \"\"]\n" // Self-overlapping separators 1 | |
| "[\"x\", \"x.\"]\n" // Self-overlapping separators 2 | |
| , | |
| setup.print_output()) | |
| << setup.print_output(); | |
| } | |
| // Verify usage errors are detected. | |
| std::vector<std::string> bad_usage_examples = { | |
| // Number of arguments. | |
| R"gn(string_split())gn", | |
| R"gn(string_split("a", "b", "c"))gn", | |
| // Argument types. | |
| R"gn(string_split(1))gn", | |
| R"gn(string_split(["oops"]))gn", | |
| R"gn(string_split("kk", 1))gn", | |
| R"gn(string_split("kk", ["oops"]))gn", | |
| // Empty separator argument. | |
| R"gn(string_split("kk", ""))gn", | |
| }; | |
| for (const auto& bad_usage_example : bad_usage_examples) { | |
| TestParseInput input(bad_usage_example); | |
| ASSERT_SUCCESS(input); | |
| Err err; | |
| input.parsed()->Execute(setup.scope(), &err); | |
| ASSERT_TRUE(err.has_error()) << bad_usage_example; | |
| } | |
| } | |
| 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_SUCCESS(err); | |
| 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_SUCCESS(err); | |
| } | |
| TEST(Functions, NotNeeded) { | |
| TestWithScope setup; | |
| TestParseInput input("not_needed({ a = 1 }, \"*\")"); | |
| ASSERT_SUCCESS(input); | |
| Err err; | |
| input.parsed()->Execute(setup.scope(), &err); | |
| ASSERT_FALSE(err.has_error()) | |
| << err.message() << err.location().Describe(true); | |
| } | |
| TEST(Template, PrintStackTraceWithOneTemplate) { | |
| TestWithScope setup; | |
| TestParseInput input( | |
| "template(\"foo\") {\n" | |
| " print(target_name)\n" | |
| " print(invoker.foo_value)\n" | |
| " print_stack_trace()\n" | |
| "}\n" | |
| "foo(\"lala\") {\n" | |
| " foo_value = 42\n" | |
| "}"); | |
| ASSERT_SUCCESS(input); | |
| Err err; | |
| input.parsed()->Execute(setup.scope(), &err); | |
| ASSERT_SUCCESS(err); | |
| EXPECT_EQ( | |
| "lala\n" | |
| "42\n" | |
| "print_stack_trace() initiated at: //test:4 using: " | |
| "//toolchain:default\n" | |
| " foo(\"lala\") //test:6\n" | |
| " print_stack_trace() //test:4\n", | |
| setup.print_output()); | |
| } | |
| TEST(Template, PrintStackTraceWithNoTemplates) { | |
| TestWithScope setup; | |
| TestParseInput input("print_stack_trace()\n"); | |
| ASSERT_SUCCESS(input); | |
| Err err; | |
| input.parsed()->Execute(setup.scope(), &err); | |
| ASSERT_FALSE(err.has_error()) << err.message() << "\n\n" << err.help_text(); | |
| EXPECT_EQ( | |
| "print_stack_trace() initiated at: //test:1 using: " | |
| "//toolchain:default\n" | |
| " print_stack_trace() //test:1\n", | |
| setup.print_output()); | |
| } | |
| TEST(Template, PrintStackTraceWithNestedTemplates) { | |
| TestWithScope setup; | |
| TestParseInput input( | |
| "template(\"foo\") {\n" | |
| " print(target_name)\n" | |
| " print(invoker.foo_value)\n" | |
| " print_stack_trace()\n" | |
| "}\n" | |
| "template(\"baz\") {\n" | |
| " foo(\"${target_name}.foo\") {\n" | |
| " foo_value = invoker.bar\n" | |
| " }\n" | |
| "}\n" | |
| "baz(\"lala\") {\n" | |
| " bar = 42\n" | |
| "}"); | |
| ASSERT_SUCCESS(input); | |
| Err err; | |
| input.parsed()->Execute(setup.scope(), &err); | |
| ASSERT_FALSE(err.has_error()) << err.message() << "\n\n" << err.help_text(); | |
| EXPECT_EQ( | |
| "lala.foo\n" | |
| "42\n" | |
| "print_stack_trace() initiated at: //test:4 using: " | |
| "//toolchain:default\n" | |
| " baz(\"lala\") //test:11\n" | |
| " foo(\"lala.foo\") //test:7\n" | |
| " print_stack_trace() //test:4\n", | |
| setup.print_output()); | |
| } | |
| TEST(Template, PrintStackTraceWithNonTemplateScopes) { | |
| TestWithScope setup; | |
| TestParseInput input( | |
| "template(\"foo\") {\n" | |
| " print(target_name)\n" | |
| " if (defined(invoker.foo_value)) {\n" | |
| " print(invoker.foo_value)\n" | |
| " print_stack_trace()\n" | |
| " }\n" | |
| "}\n" | |
| "template(\"baz\") {\n" | |
| " foo(\"${target_name}.foo\") {\n" | |
| " foo_value = invoker.bar\n" | |
| " }\n" | |
| "}\n" | |
| "baz(\"lala\") {\n" | |
| " bar = 42\n" | |
| "}"); | |
| ASSERT_SUCCESS(input); | |
| Err err; | |
| input.parsed()->Execute(setup.scope(), &err); | |
| ASSERT_FALSE(err.has_error()) << err.message() << "\n\n" << err.help_text(); | |
| EXPECT_EQ( | |
| "lala.foo\n" | |
| "42\n" | |
| "print_stack_trace() initiated at: //test:5 using: " | |
| "//toolchain:default\n" | |
| " baz(\"lala\") //test:13\n" | |
| " foo(\"lala.foo\") //test:9\n" | |
| " print_stack_trace() //test:5\n", | |
| setup.print_output()); | |
| } | |
| TEST(Template, PrintStackTraceWithNonTemplateScopesBetweenTemplateInvocations) { | |
| TestWithScope setup; | |
| TestParseInput input( | |
| "template(\"foo\") {\n" | |
| " print(target_name)\n" | |
| " if (defined(invoker.foo_value)) {\n" | |
| " print(invoker.foo_value)\n" | |
| " print_stack_trace()\n" | |
| " }\n" | |
| "}\n" | |
| "template(\"baz\") {\n" | |
| " if (invoker.bar == 42) {\n" | |
| " foo(\"${target_name}.foo\") {\n" | |
| " foo_value = invoker.bar\n" | |
| " }\n" | |
| " }\n" | |
| "}\n" | |
| "baz(\"lala\") {\n" | |
| " bar = 42\n" | |
| "}"); | |
| ASSERT_SUCCESS(input); | |
| Err err; | |
| input.parsed()->Execute(setup.scope(), &err); | |
| ASSERT_FALSE(err.has_error()) << err.message() << "\n\n" << err.help_text(); | |
| EXPECT_EQ( | |
| "lala.foo\n" | |
| "42\n" | |
| "print_stack_trace() initiated at: //test:5 using: " | |
| "//toolchain:default\n" | |
| " baz(\"lala\") //test:15\n" | |
| " foo(\"lala.foo\") //test:10\n" | |
| " print_stack_trace() //test:5\n", | |
| setup.print_output()); | |
| } | |
| TEST(Template, PrintStackTraceWithTemplateDefinedWithinATemplate) { | |
| TestWithScope setup; | |
| TestParseInput input( | |
| "template(\"foo\") {\n" | |
| " print(target_name)\n" | |
| " if (defined(invoker.foo_value)) {\n" | |
| " template(\"foo_internal\") {" | |
| " print(target_name)\n" | |
| " print(invoker.foo_internal_value)\n" | |
| " print_stack_trace()\n" | |
| " }\n" | |
| " foo_internal(target_name+\".internal\") {" | |
| " foo_internal_value = invoker.foo_value\n" | |
| " }\n" | |
| " }\n" | |
| "}\n" | |
| "template(\"baz\") {\n" | |
| " if (invoker.bar == 42) {\n" | |
| " foo(\"${target_name}.foo\") {\n" | |
| " foo_value = invoker.bar\n" | |
| " }\n" | |
| " }\n" | |
| "}\n" | |
| "baz(\"lala\") {\n" | |
| " bar = 42\n" | |
| "}"); | |
| ASSERT_SUCCESS(input); | |
| Err err; | |
| input.parsed()->Execute(setup.scope(), &err); | |
| ASSERT_FALSE(err.has_error()) << err.message() << "\n\n" << err.help_text(); | |
| EXPECT_EQ( | |
| "lala.foo\n" | |
| "lala.foo.internal\n" | |
| "42\n" | |
| "print_stack_trace() initiated at: //test:6 using: " | |
| "//toolchain:default\n" | |
| " baz(\"lala\") //test:19\n" | |
| " foo(\"lala.foo\") //test:14\n" | |
| " foo_internal(\"lala.foo.internal\") //test:8\n" | |
| " print_stack_trace() //test:6\n", | |
| setup.print_output()); | |
| } |