| // Copyright 2018 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/trace_event/trace_arguments.h" |
| |
| #include <gtest/gtest.h> |
| #include <limits> |
| #include <string> |
| |
| #include "base/memory/raw_ptr.h" |
| |
| namespace base { |
| namespace trace_event { |
| |
| namespace { |
| |
| // Simple convertable that holds a string to append to the trace, |
| // and can also write to a boolean flag on destruction. |
| class MyConvertable : public ConvertableToTraceFormat { |
| public: |
| MyConvertable(const char* text, bool* destroy_flag = nullptr) |
| : text_(text), destroy_flag_(destroy_flag) {} |
| ~MyConvertable() override { |
| if (destroy_flag_) |
| *destroy_flag_ = true; |
| } |
| void AppendAsTraceFormat(std::string* out) const override { *out += text_; } |
| const char* text() const { return text_; } |
| |
| private: |
| const char* text_; |
| raw_ptr<bool> destroy_flag_; |
| }; |
| |
| } // namespace |
| |
| TEST(TraceArguments, StringStorageDefaultConstruction) { |
| StringStorage storage; |
| EXPECT_TRUE(storage.empty()); |
| EXPECT_FALSE(storage.data()); |
| EXPECT_EQ(0U, storage.size()); |
| } |
| |
| TEST(TraceArguments, StringStorageConstructionWithSize) { |
| const size_t kSize = 128; |
| StringStorage storage(kSize); |
| EXPECT_FALSE(storage.empty()); |
| EXPECT_TRUE(storage.data()); |
| EXPECT_EQ(kSize, storage.size()); |
| EXPECT_EQ(storage.data(), storage.begin()); |
| EXPECT_EQ(storage.data() + kSize, storage.end()); |
| } |
| |
| TEST(TraceArguments, StringStorageReset) { |
| StringStorage storage(128); |
| EXPECT_FALSE(storage.empty()); |
| |
| storage.Reset(); |
| EXPECT_TRUE(storage.empty()); |
| EXPECT_FALSE(storage.data()); |
| EXPECT_EQ(0u, storage.size()); |
| } |
| |
| TEST(TraceArguments, StringStorageResetWithSize) { |
| StringStorage storage; |
| EXPECT_TRUE(storage.empty()); |
| |
| const size_t kSize = 128; |
| storage.Reset(kSize); |
| EXPECT_FALSE(storage.empty()); |
| EXPECT_TRUE(storage.data()); |
| EXPECT_EQ(kSize, storage.size()); |
| EXPECT_EQ(storage.data(), storage.begin()); |
| EXPECT_EQ(storage.data() + kSize, storage.end()); |
| } |
| |
| TEST(TraceArguments, StringStorageEstimateTraceMemoryOverhead) { |
| StringStorage storage; |
| EXPECT_EQ(0u, storage.EstimateTraceMemoryOverhead()); |
| |
| const size_t kSize = 128; |
| storage.Reset(kSize); |
| EXPECT_EQ(sizeof(size_t) + kSize, storage.EstimateTraceMemoryOverhead()); |
| } |
| |
| static void CheckJSONFor(TraceValue v, char type, const char* expected) { |
| std::string out; |
| v.AppendAsJSON(type, &out); |
| EXPECT_STREQ(expected, out.c_str()); |
| } |
| |
| static void CheckStringFor(TraceValue v, char type, const char* expected) { |
| std::string out; |
| v.AppendAsString(type, &out); |
| EXPECT_STREQ(expected, out.c_str()); |
| } |
| |
| TEST(TraceArguments, TraceValueAppend) { |
| TraceValue v; |
| |
| v.Init(-1024); |
| CheckJSONFor(v, TRACE_VALUE_TYPE_INT, "-1024"); |
| CheckStringFor(v, TRACE_VALUE_TYPE_INT, "-1024"); |
| v.Init(1024ULL); |
| CheckJSONFor(v, TRACE_VALUE_TYPE_UINT, "1024"); |
| CheckStringFor(v, TRACE_VALUE_TYPE_UINT, "1024"); |
| v.Init(3.1415926535); |
| CheckJSONFor(v, TRACE_VALUE_TYPE_DOUBLE, "3.1415926535"); |
| CheckStringFor(v, TRACE_VALUE_TYPE_DOUBLE, "3.1415926535"); |
| v.Init(2.0); |
| CheckJSONFor(v, TRACE_VALUE_TYPE_DOUBLE, "2.0"); |
| CheckStringFor(v, TRACE_VALUE_TYPE_DOUBLE, "2.0"); |
| v.Init(0.5); |
| CheckJSONFor(v, TRACE_VALUE_TYPE_DOUBLE, "0.5"); |
| CheckStringFor(v, TRACE_VALUE_TYPE_DOUBLE, "0.5"); |
| v.Init(-0.5); |
| CheckJSONFor(v, TRACE_VALUE_TYPE_DOUBLE, "-0.5"); |
| CheckStringFor(v, TRACE_VALUE_TYPE_DOUBLE, "-0.5"); |
| v.Init(std::numeric_limits<double>::quiet_NaN()); |
| CheckJSONFor(v, TRACE_VALUE_TYPE_DOUBLE, "\"NaN\""); |
| CheckStringFor(v, TRACE_VALUE_TYPE_DOUBLE, "NaN"); |
| v.Init(std::numeric_limits<double>::quiet_NaN()); |
| CheckJSONFor(v, TRACE_VALUE_TYPE_DOUBLE, "\"NaN\""); |
| CheckStringFor(v, TRACE_VALUE_TYPE_DOUBLE, "NaN"); |
| v.Init(std::numeric_limits<double>::infinity()); |
| CheckJSONFor(v, TRACE_VALUE_TYPE_DOUBLE, "\"Infinity\""); |
| CheckStringFor(v, TRACE_VALUE_TYPE_DOUBLE, "Infinity"); |
| v.Init(-std::numeric_limits<double>::infinity()); |
| CheckJSONFor(v, TRACE_VALUE_TYPE_DOUBLE, "\"-Infinity\""); |
| CheckStringFor(v, TRACE_VALUE_TYPE_DOUBLE, "-Infinity"); |
| v.Init(true); |
| CheckJSONFor(v, TRACE_VALUE_TYPE_BOOL, "true"); |
| CheckStringFor(v, TRACE_VALUE_TYPE_BOOL, "true"); |
| v.Init(false); |
| CheckJSONFor(v, TRACE_VALUE_TYPE_BOOL, "false"); |
| CheckStringFor(v, TRACE_VALUE_TYPE_BOOL, "false"); |
| v.Init("Some \"nice\" String"); |
| CheckJSONFor(v, TRACE_VALUE_TYPE_STRING, "\"Some \\\"nice\\\" String\""); |
| CheckStringFor(v, TRACE_VALUE_TYPE_STRING, "Some \"nice\" String"); |
| CheckJSONFor(v, TRACE_VALUE_TYPE_COPY_STRING, "\"Some \\\"nice\\\" String\""); |
| CheckStringFor(v, TRACE_VALUE_TYPE_COPY_STRING, "Some \"nice\" String"); |
| |
| int* p = nullptr; |
| v.Init(static_cast<void*>(p)); |
| CheckJSONFor(v, TRACE_VALUE_TYPE_POINTER, "\"0x0\""); |
| CheckStringFor(v, TRACE_VALUE_TYPE_POINTER, "0x0"); |
| |
| const char kText[] = "Hello World"; |
| bool destroy_flag = false; |
| TraceArguments args("arg1", |
| std::make_unique<MyConvertable>(kText, &destroy_flag)); |
| |
| CheckJSONFor(std::move(args.values()[0]), args.types()[0], kText); |
| CheckStringFor(std::move(args.values()[0]), args.types()[0], kText); |
| } |
| |
| TEST(TraceArguments, DefaultConstruction) { |
| TraceArguments args; |
| EXPECT_EQ(0U, args.size()); |
| } |
| |
| TEST(TraceArguments, ConstructorSingleInteger) { |
| TraceArguments args("foo_int", int(10)); |
| EXPECT_EQ(1U, args.size()); |
| EXPECT_EQ(TRACE_VALUE_TYPE_INT, args.types()[0]); |
| EXPECT_STREQ("foo_int", args.names()[0]); |
| EXPECT_EQ(10, args.values()[0].as_int); |
| } |
| |
| TEST(TraceArguments, ConstructorSingleFloat) { |
| TraceArguments args("foo_pi", float(3.1415)); |
| double expected = float(3.1415); |
| EXPECT_EQ(1U, args.size()); |
| EXPECT_EQ(TRACE_VALUE_TYPE_DOUBLE, args.types()[0]); |
| EXPECT_STREQ("foo_pi", args.names()[0]); |
| EXPECT_EQ(expected, args.values()[0].as_double); |
| } |
| |
| TEST(TraceArguments, ConstructorSingleNoCopyString) { |
| const char kText[] = "Persistent string"; |
| TraceArguments args("foo_cstring", kText); |
| EXPECT_EQ(1U, args.size()); |
| EXPECT_EQ(TRACE_VALUE_TYPE_STRING, args.types()[0]); |
| EXPECT_STREQ("foo_cstring", args.names()[0]); |
| EXPECT_EQ(kText, args.values()[0].as_string); |
| } |
| |
| TEST(TraceArguments, ConstructorSingleStdString) { |
| std::string text = "Non-persistent string"; |
| TraceArguments args("foo_stdstring", text); |
| EXPECT_EQ(1U, args.size()); |
| EXPECT_EQ(TRACE_VALUE_TYPE_COPY_STRING, args.types()[0]); |
| EXPECT_STREQ("foo_stdstring", args.names()[0]); |
| EXPECT_EQ(text.c_str(), args.values()[0].as_string); |
| } |
| |
| TEST(TraceArguments, ConstructorSingleTraceStringWithCopy) { |
| const char kText[] = "Persistent string #2"; |
| TraceArguments args("foo_tracestring", TraceStringWithCopy(kText)); |
| EXPECT_EQ(1U, args.size()); |
| EXPECT_EQ(TRACE_VALUE_TYPE_COPY_STRING, args.types()[0]); |
| EXPECT_STREQ("foo_tracestring", args.names()[0]); |
| EXPECT_EQ(kText, args.values()[0].as_string); |
| } |
| |
| TEST(TraceArguments, ConstructorSinglePointer) { |
| bool destroy_flag = false; |
| { |
| // Simple class that can set a boolean flag on destruction. |
| class Foo { |
| public: |
| Foo(bool* destroy_flag) : destroy_flag_(destroy_flag) {} |
| ~Foo() { |
| if (destroy_flag_) |
| *destroy_flag_ = true; |
| } |
| |
| private: |
| raw_ptr<bool> destroy_flag_; |
| }; |
| auto foo = std::make_unique<Foo>(&destroy_flag); |
| EXPECT_FALSE(destroy_flag); |
| // This test also verifies that the object is not destroyed by the |
| // TraceArguments destructor. This should only be possible for |
| // TRACE_VALUE_TYPE_CONVERTABLE instances. |
| { |
| TraceArguments args("foo_pointer", static_cast<void*>(foo.get())); |
| EXPECT_EQ(1U, args.size()); |
| EXPECT_EQ(TRACE_VALUE_TYPE_POINTER, args.types()[0]); |
| EXPECT_STREQ("foo_pointer", args.names()[0]); |
| EXPECT_EQ(foo.get(), args.values()[0].as_pointer); |
| EXPECT_FALSE(destroy_flag); |
| } // Calls TraceArguments destructor. |
| EXPECT_FALSE(destroy_flag); |
| } // Calls Foo destructor. |
| EXPECT_TRUE(destroy_flag); |
| } |
| |
| TEST(TraceArguments, ConstructorSingleConvertable) { |
| bool destroy_flag = false; |
| const char kText[] = "Text for MyConvertable instance"; |
| MyConvertable* ptr = new MyConvertable(kText, &destroy_flag); |
| |
| // This test also verifies that the MyConvertable instance is properly |
| // destroyed when the TraceArguments destructor is called. |
| EXPECT_FALSE(destroy_flag); |
| { |
| TraceArguments args("foo_convertable", std::unique_ptr<MyConvertable>(ptr)); |
| EXPECT_EQ(1U, args.size()); |
| EXPECT_EQ(TRACE_VALUE_TYPE_CONVERTABLE, args.types()[0]); |
| EXPECT_STREQ("foo_convertable", args.names()[0]); |
| EXPECT_EQ(ptr, args.values()[0].as_convertable); |
| EXPECT_FALSE(destroy_flag); |
| } // Calls TraceArguments destructor. |
| EXPECT_TRUE(destroy_flag); |
| } |
| |
| TEST(TraceArguments, ConstructorWithTwoArguments) { |
| const char kText1[] = "First argument"; |
| const char kText2[] = "Second argument"; |
| bool destroy_flag = false; |
| |
| { |
| MyConvertable* ptr = new MyConvertable(kText2, &destroy_flag); |
| TraceArguments args1("foo_arg1_cstring", kText1, "foo_arg2_convertable", |
| std::unique_ptr<MyConvertable>(ptr)); |
| EXPECT_EQ(2U, args1.size()); |
| EXPECT_STREQ("foo_arg1_cstring", args1.names()[0]); |
| EXPECT_STREQ("foo_arg2_convertable", args1.names()[1]); |
| EXPECT_EQ(TRACE_VALUE_TYPE_STRING, args1.types()[0]); |
| EXPECT_EQ(TRACE_VALUE_TYPE_CONVERTABLE, args1.types()[1]); |
| EXPECT_EQ(kText1, args1.values()[0].as_string); |
| EXPECT_EQ(ptr, args1.values()[1].as_convertable); |
| EXPECT_FALSE(destroy_flag); |
| } // calls |args1| destructor. Should delete |ptr|. |
| EXPECT_TRUE(destroy_flag); |
| } |
| |
| TEST(TraceArguments, ConstructorLegacyNoConvertables) { |
| const char* const kNames[3] = {"legacy_arg1", "legacy_arg2", "legacy_arg3"}; |
| const unsigned char kTypes[3] = { |
| TRACE_VALUE_TYPE_INT, |
| TRACE_VALUE_TYPE_STRING, |
| TRACE_VALUE_TYPE_POINTER, |
| }; |
| static const char kText[] = "Some text"; |
| const unsigned long long kValues[3] = { |
| 1000042ULL, |
| reinterpret_cast<unsigned long long>(kText), |
| reinterpret_cast<unsigned long long>(kText + 2), |
| }; |
| TraceArguments args(3, kNames, kTypes, kValues); |
| // Check that only the first kMaxSize arguments are taken! |
| EXPECT_EQ(2U, args.size()); |
| EXPECT_STREQ(kNames[0], args.names()[0]); |
| EXPECT_STREQ(kNames[1], args.names()[1]); |
| EXPECT_EQ(TRACE_VALUE_TYPE_INT, args.types()[0]); |
| EXPECT_EQ(TRACE_VALUE_TYPE_STRING, args.types()[1]); |
| EXPECT_EQ(kValues[0], args.values()[0].as_uint); |
| EXPECT_EQ(kText, args.values()[1].as_string); |
| } |
| |
| TEST(TraceArguments, ConstructorLegacyWithConvertables) { |
| const char* const kNames[3] = {"legacy_arg1", "legacy_arg2", "legacy_arg3"}; |
| const unsigned char kTypes[3] = { |
| TRACE_VALUE_TYPE_CONVERTABLE, |
| TRACE_VALUE_TYPE_CONVERTABLE, |
| TRACE_VALUE_TYPE_CONVERTABLE, |
| }; |
| std::unique_ptr<MyConvertable> convertables[3] = { |
| std::make_unique<MyConvertable>("First one"), |
| std::make_unique<MyConvertable>("Second one"), |
| std::make_unique<MyConvertable>("Third one"), |
| }; |
| TraceArguments args(3, kNames, kTypes, nullptr, convertables); |
| // Check that only the first kMaxSize arguments are taken! |
| EXPECT_EQ(2U, args.size()); |
| EXPECT_STREQ(kNames[0], args.names()[0]); |
| EXPECT_STREQ(kNames[1], args.names()[1]); |
| EXPECT_EQ(TRACE_VALUE_TYPE_CONVERTABLE, args.types()[0]); |
| EXPECT_EQ(TRACE_VALUE_TYPE_CONVERTABLE, args.types()[1]); |
| // Check that only the first two items were moved to |args|. |
| EXPECT_FALSE(convertables[0].get()); |
| EXPECT_FALSE(convertables[1].get()); |
| EXPECT_TRUE(convertables[2].get()); |
| } |
| |
| TEST(TraceArguments, MoveConstruction) { |
| const char kText1[] = "First argument"; |
| const char kText2[] = "Second argument"; |
| bool destroy_flag = false; |
| |
| { |
| MyConvertable* ptr = new MyConvertable(kText2, &destroy_flag); |
| TraceArguments args1("foo_arg1_cstring", kText1, "foo_arg2_convertable", |
| std::unique_ptr<MyConvertable>(ptr)); |
| EXPECT_EQ(2U, args1.size()); |
| EXPECT_STREQ("foo_arg1_cstring", args1.names()[0]); |
| EXPECT_STREQ("foo_arg2_convertable", args1.names()[1]); |
| EXPECT_EQ(TRACE_VALUE_TYPE_STRING, args1.types()[0]); |
| EXPECT_EQ(TRACE_VALUE_TYPE_CONVERTABLE, args1.types()[1]); |
| EXPECT_EQ(kText1, args1.values()[0].as_string); |
| EXPECT_EQ(ptr, args1.values()[1].as_convertable); |
| |
| { |
| TraceArguments args2(std::move(args1)); |
| EXPECT_FALSE(destroy_flag); |
| |
| // |args1| is now empty. |
| EXPECT_EQ(0U, args1.size()); |
| |
| // Check that everything was transferred to |args2|. |
| EXPECT_EQ(2U, args2.size()); |
| EXPECT_STREQ("foo_arg1_cstring", args2.names()[0]); |
| EXPECT_STREQ("foo_arg2_convertable", args2.names()[1]); |
| EXPECT_EQ(TRACE_VALUE_TYPE_STRING, args2.types()[0]); |
| EXPECT_EQ(TRACE_VALUE_TYPE_CONVERTABLE, args2.types()[1]); |
| EXPECT_EQ(kText1, args2.values()[0].as_string); |
| EXPECT_EQ(ptr, args2.values()[1].as_convertable); |
| } // Calls |args2| destructor. Should delete |ptr|. |
| EXPECT_TRUE(destroy_flag); |
| destroy_flag = false; |
| } // Calls |args1| destructor. Should not delete |ptr|. |
| EXPECT_FALSE(destroy_flag); |
| } |
| |
| TEST(TraceArguments, MoveAssignment) { |
| const char kText1[] = "First argument"; |
| const char kText2[] = "Second argument"; |
| bool destroy_flag = false; |
| |
| { |
| MyConvertable* ptr = new MyConvertable(kText2, &destroy_flag); |
| TraceArguments args1("foo_arg1_cstring", kText1, "foo_arg2_convertable", |
| std::unique_ptr<MyConvertable>(ptr)); |
| EXPECT_EQ(2U, args1.size()); |
| EXPECT_STREQ("foo_arg1_cstring", args1.names()[0]); |
| EXPECT_STREQ("foo_arg2_convertable", args1.names()[1]); |
| EXPECT_EQ(TRACE_VALUE_TYPE_STRING, args1.types()[0]); |
| EXPECT_EQ(TRACE_VALUE_TYPE_CONVERTABLE, args1.types()[1]); |
| EXPECT_EQ(kText1, args1.values()[0].as_string); |
| EXPECT_EQ(ptr, args1.values()[1].as_convertable); |
| |
| { |
| TraceArguments args2; |
| |
| args2 = std::move(args1); |
| EXPECT_FALSE(destroy_flag); |
| |
| // |args1| is now empty. |
| EXPECT_EQ(0U, args1.size()); |
| |
| // Check that everything was transferred to |args2|. |
| EXPECT_EQ(2U, args2.size()); |
| EXPECT_STREQ("foo_arg1_cstring", args2.names()[0]); |
| EXPECT_STREQ("foo_arg2_convertable", args2.names()[1]); |
| EXPECT_EQ(TRACE_VALUE_TYPE_STRING, args2.types()[0]); |
| EXPECT_EQ(TRACE_VALUE_TYPE_CONVERTABLE, args2.types()[1]); |
| EXPECT_EQ(kText1, args2.values()[0].as_string); |
| EXPECT_EQ(ptr, args2.values()[1].as_convertable); |
| } // Calls |args2| destructor. Should delete |ptr|. |
| EXPECT_TRUE(destroy_flag); |
| destroy_flag = false; |
| } // Calls |args1| destructor. Should not delete |ptr|. |
| EXPECT_FALSE(destroy_flag); |
| } |
| |
| TEST(TraceArguments, Reset) { |
| bool destroy_flag = false; |
| { |
| TraceArguments args( |
| "foo_arg1", "Hello", "foo_arg2", |
| std::make_unique<MyConvertable>("World", &destroy_flag)); |
| |
| EXPECT_EQ(2U, args.size()); |
| EXPECT_FALSE(destroy_flag); |
| args.Reset(); |
| EXPECT_EQ(0U, args.size()); |
| EXPECT_TRUE(destroy_flag); |
| destroy_flag = false; |
| } // Calls |args| destructor. Should not delete twice. |
| EXPECT_FALSE(destroy_flag); |
| } |
| |
| TEST(TraceArguments, CopyStringsTo_NoStrings) { |
| StringStorage storage; |
| |
| TraceArguments args("arg1", 10, "arg2", 42); |
| args.CopyStringsTo(&storage, false, nullptr, nullptr); |
| EXPECT_TRUE(storage.empty()); |
| EXPECT_EQ(0U, storage.size()); |
| } |
| |
| TEST(TraceArguments, CopyStringsTo_OnlyArgs) { |
| StringStorage storage; |
| |
| TraceArguments args("arg1", TraceStringWithCopy("Hello"), "arg2", |
| TraceStringWithCopy("World")); |
| |
| const char kExtra1[] = "extra1"; |
| const char kExtra2[] = "extra2"; |
| const char* extra1 = kExtra1; |
| const char* extra2 = kExtra2; |
| |
| // Types should be copyable strings. |
| EXPECT_EQ(TRACE_VALUE_TYPE_COPY_STRING, args.types()[0]); |
| EXPECT_EQ(TRACE_VALUE_TYPE_COPY_STRING, args.types()[1]); |
| |
| args.CopyStringsTo(&storage, false, &extra1, &extra2); |
| |
| // Storage should be allocated. |
| EXPECT_TRUE(storage.data()); |
| EXPECT_NE(0U, storage.size()); |
| |
| // Types should not be changed. |
| EXPECT_EQ(TRACE_VALUE_TYPE_COPY_STRING, args.types()[0]); |
| EXPECT_EQ(TRACE_VALUE_TYPE_COPY_STRING, args.types()[1]); |
| |
| // names should not be copied. |
| EXPECT_FALSE(storage.Contains(args.names()[0])); |
| EXPECT_FALSE(storage.Contains(args.names()[1])); |
| EXPECT_STREQ("arg1", args.names()[0]); |
| EXPECT_STREQ("arg2", args.names()[1]); |
| |
| // strings should be copied. |
| EXPECT_TRUE(storage.Contains(args.values()[0].as_string)); |
| EXPECT_TRUE(storage.Contains(args.values()[1].as_string)); |
| EXPECT_STREQ("Hello", args.values()[0].as_string); |
| EXPECT_STREQ("World", args.values()[1].as_string); |
| |
| // |extra1| and |extra2| should not be copied. |
| EXPECT_EQ(kExtra1, extra1); |
| EXPECT_EQ(kExtra2, extra2); |
| } |
| |
| TEST(TraceArguments, CopyStringsTo_Everything) { |
| StringStorage storage; |
| |
| TraceArguments args("arg1", "Hello", "arg2", "World"); |
| const char kExtra1[] = "extra1"; |
| const char kExtra2[] = "extra2"; |
| const char* extra1 = kExtra1; |
| const char* extra2 = kExtra2; |
| |
| // Types should be normal strings. |
| EXPECT_EQ(TRACE_VALUE_TYPE_STRING, args.types()[0]); |
| EXPECT_EQ(TRACE_VALUE_TYPE_STRING, args.types()[1]); |
| |
| args.CopyStringsTo(&storage, true, &extra1, &extra2); |
| |
| // Storage should be allocated. |
| EXPECT_TRUE(storage.data()); |
| EXPECT_NE(0U, storage.size()); |
| |
| // Types should be changed to copyable strings. |
| EXPECT_EQ(TRACE_VALUE_TYPE_COPY_STRING, args.types()[0]); |
| EXPECT_EQ(TRACE_VALUE_TYPE_COPY_STRING, args.types()[1]); |
| |
| // names should be copied. |
| EXPECT_TRUE(storage.Contains(args.names()[0])); |
| EXPECT_TRUE(storage.Contains(args.names()[1])); |
| EXPECT_STREQ("arg1", args.names()[0]); |
| EXPECT_STREQ("arg2", args.names()[1]); |
| |
| // strings should be copied. |
| EXPECT_TRUE(storage.Contains(args.values()[0].as_string)); |
| EXPECT_TRUE(storage.Contains(args.values()[1].as_string)); |
| EXPECT_STREQ("Hello", args.values()[0].as_string); |
| EXPECT_STREQ("World", args.values()[1].as_string); |
| |
| // |extra1| and |extra2| should be copied. |
| EXPECT_NE(kExtra1, extra1); |
| EXPECT_NE(kExtra2, extra2); |
| EXPECT_TRUE(storage.Contains(extra1)); |
| EXPECT_TRUE(storage.Contains(extra2)); |
| EXPECT_STREQ(kExtra1, extra1); |
| EXPECT_STREQ(kExtra2, extra2); |
| } |
| |
| } // namespace trace_event |
| } // namespace base |