// Copyright 2014 The Crashpad Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "minidump/minidump_writable.h" #include #include #include "base/basictypes.h" #include "gtest/gtest.h" #include "util/file/string_file_writer.h" namespace crashpad { namespace test { namespace { class BaseTestMinidumpWritable : public crashpad::internal::MinidumpWritable { public: BaseTestMinidumpWritable() : MinidumpWritable(), children_(), expected_offset_(-1), alignment_(0), phase_(kPhaseEarly), has_alignment_(false), has_phase_(false), verified_(false) {} ~BaseTestMinidumpWritable() { EXPECT_TRUE(verified_); } void SetAlignment(size_t alignment) { alignment_ = alignment; has_alignment_ = true; } void AddChild(BaseTestMinidumpWritable* child) { children_.push_back(child); } void SetPhaseLate() { phase_ = kPhaseLate; has_phase_ = true; } void Verify() { verified_ = true; EXPECT_EQ(kStateWritten, state()); for (BaseTestMinidumpWritable* child : children_) { child->Verify(); } } protected: virtual bool Freeze() override { EXPECT_EQ(kStateMutable, state()); bool rv = MinidumpWritable::Freeze(); EXPECT_TRUE(rv); EXPECT_EQ(kStateFrozen, state()); return rv; } virtual size_t Alignment() override { EXPECT_GE(state(), kStateFrozen); return has_alignment_ ? alignment_ : MinidumpWritable::Alignment(); } virtual std::vector Children() override { EXPECT_GE(state(), kStateFrozen); if (!children_.empty()) { std::vector children; for (BaseTestMinidumpWritable* child : children_) { children.push_back(child); } return children; } return MinidumpWritable::Children(); } virtual Phase WritePhase() override { return has_phase_ ? phase_ : MinidumpWritable::Phase(); } virtual bool WillWriteAtOffsetImpl(off_t offset) override { EXPECT_EQ(state(), kStateFrozen); expected_offset_ = offset; bool rv = MinidumpWritable::WillWriteAtOffsetImpl(offset); EXPECT_TRUE(rv); return rv; } virtual bool WriteObject(FileWriterInterface* file_writer) override { EXPECT_EQ(state(), kStateWritable); EXPECT_EQ(expected_offset_, file_writer->Seek(0, SEEK_CUR)); // Subclasses must override this. return false; } private: std::vector children_; off_t expected_offset_; size_t alignment_; Phase phase_; bool has_alignment_; bool has_phase_; bool verified_; DISALLOW_COPY_AND_ASSIGN(BaseTestMinidumpWritable); }; class TestStringMinidumpWritable final : public BaseTestMinidumpWritable { public: TestStringMinidumpWritable() : BaseTestMinidumpWritable(), data_() {} ~TestStringMinidumpWritable() {} void SetData(const std::string& string) { data_ = string; } protected: virtual size_t SizeOfObject() override { EXPECT_GE(state(), kStateFrozen); return data_.size(); } virtual bool WriteObject(FileWriterInterface* file_writer) override { BaseTestMinidumpWritable::WriteObject(file_writer); bool rv = file_writer->Write(&data_[0], data_.size()); EXPECT_TRUE(rv); return rv; } private: std::string data_; DISALLOW_COPY_AND_ASSIGN(TestStringMinidumpWritable); }; TEST(MinidumpWritable, MinidumpWritable) { StringFileWriter writer; { SCOPED_TRACE("empty"); writer.Reset(); TestStringMinidumpWritable string_writable; EXPECT_TRUE(string_writable.WriteEverything(&writer)); EXPECT_TRUE(writer.string().empty()); string_writable.Verify(); } { SCOPED_TRACE("childless"); writer.Reset(); TestStringMinidumpWritable string_writable; string_writable.SetData("a"); EXPECT_TRUE(string_writable.WriteEverything(&writer)); EXPECT_EQ(1u, writer.string().size()); EXPECT_EQ("a", writer.string()); string_writable.Verify(); } { SCOPED_TRACE("parent-child"); writer.Reset(); TestStringMinidumpWritable parent; parent.SetData("b"); TestStringMinidumpWritable child; child.SetData("c"); parent.AddChild(&child); EXPECT_TRUE(parent.WriteEverything(&writer)); EXPECT_EQ(5u, writer.string().size()); EXPECT_EQ(std::string("b\0\0\0c", 5), writer.string()); parent.Verify(); } { SCOPED_TRACE("base alignment 2"); writer.Reset(); TestStringMinidumpWritable parent; parent.SetData("de"); TestStringMinidumpWritable child; child.SetData("f"); parent.AddChild(&child); EXPECT_TRUE(parent.WriteEverything(&writer)); EXPECT_EQ(5u, writer.string().size()); EXPECT_EQ(std::string("de\0\0f", 5), writer.string()); parent.Verify(); } { SCOPED_TRACE("base alignment 3"); writer.Reset(); TestStringMinidumpWritable parent; parent.SetData("ghi"); TestStringMinidumpWritable child; child.SetData("j"); parent.AddChild(&child); EXPECT_TRUE(parent.WriteEverything(&writer)); EXPECT_EQ(5u, writer.string().size()); EXPECT_EQ(std::string("ghi\0j", 5), writer.string()); parent.Verify(); } { SCOPED_TRACE("base alignment 4"); writer.Reset(); TestStringMinidumpWritable parent; parent.SetData("klmn"); TestStringMinidumpWritable child; child.SetData("o"); parent.AddChild(&child); EXPECT_TRUE(parent.WriteEverything(&writer)); EXPECT_EQ(5u, writer.string().size()); EXPECT_EQ("klmno", writer.string()); parent.Verify(); } { SCOPED_TRACE("base alignment 5"); writer.Reset(); TestStringMinidumpWritable parent; parent.SetData("pqrst"); TestStringMinidumpWritable child; child.SetData("u"); parent.AddChild(&child); EXPECT_TRUE(parent.WriteEverything(&writer)); EXPECT_EQ(9u, writer.string().size()); EXPECT_EQ(std::string("pqrst\0\0\0u", 9), writer.string()); parent.Verify(); } { SCOPED_TRACE("two children"); writer.Reset(); TestStringMinidumpWritable parent; parent.SetData("parent"); TestStringMinidumpWritable child_0; child_0.SetData("child_0"); parent.AddChild(&child_0); TestStringMinidumpWritable child_1; child_1.SetData("child_1"); parent.AddChild(&child_1); EXPECT_TRUE(parent.WriteEverything(&writer)); EXPECT_EQ(23u, writer.string().size()); EXPECT_EQ(std::string("parent\0\0child_0\0child_1", 23), writer.string()); parent.Verify(); } { SCOPED_TRACE("grandchild"); writer.Reset(); TestStringMinidumpWritable parent; parent.SetData("parent"); TestStringMinidumpWritable child; child.SetData("child"); parent.AddChild(&child); TestStringMinidumpWritable grandchild; grandchild.SetData("grandchild"); child.AddChild(&grandchild); EXPECT_TRUE(parent.WriteEverything(&writer)); EXPECT_EQ(26u, writer.string().size()); EXPECT_EQ(std::string("parent\0\0child\0\0\0grandchild", 26), writer.string()); parent.Verify(); } { SCOPED_TRACE("grandchild with empty parent"); writer.Reset(); TestStringMinidumpWritable parent; TestStringMinidumpWritable child; child.SetData("child"); parent.AddChild(&child); TestStringMinidumpWritable grandchild; grandchild.SetData("grandchild"); child.AddChild(&grandchild); EXPECT_TRUE(parent.WriteEverything(&writer)); EXPECT_EQ(18u, writer.string().size()); EXPECT_EQ(std::string("child\0\0\0grandchild", 18), writer.string()); parent.Verify(); } { SCOPED_TRACE("grandchild with empty child"); writer.Reset(); TestStringMinidumpWritable parent; parent.SetData("parent"); TestStringMinidumpWritable child; parent.AddChild(&child); TestStringMinidumpWritable grandchild; grandchild.SetData("grandchild"); child.AddChild(&grandchild); EXPECT_TRUE(parent.WriteEverything(&writer)); EXPECT_EQ(18u, writer.string().size()); EXPECT_EQ(std::string("parent\0\0grandchild", 18), writer.string()); parent.Verify(); } { SCOPED_TRACE("grandchild with empty grandchild"); writer.Reset(); TestStringMinidumpWritable parent; parent.SetData("parent"); TestStringMinidumpWritable child; child.SetData("child"); parent.AddChild(&child); TestStringMinidumpWritable grandchild; child.AddChild(&grandchild); EXPECT_TRUE(parent.WriteEverything(&writer)); EXPECT_EQ(13u, writer.string().size()); EXPECT_EQ(std::string("parent\0\0child", 13), writer.string()); parent.Verify(); } { SCOPED_TRACE("grandchild with late-phase grandchild"); writer.Reset(); TestStringMinidumpWritable parent; parent.SetData("parent"); TestStringMinidumpWritable child; child.SetData("child"); parent.AddChild(&child); TestStringMinidumpWritable grandchild; grandchild.SetData("grandchild"); grandchild.SetPhaseLate(); child.AddChild(&grandchild); EXPECT_TRUE(parent.WriteEverything(&writer)); EXPECT_EQ(26u, writer.string().size()); EXPECT_EQ(std::string("parent\0\0child\0\0\0grandchild", 26), writer.string()); parent.Verify(); } { SCOPED_TRACE("grandchild with late-phase child"); writer.Reset(); TestStringMinidumpWritable parent; parent.SetData("parent"); TestStringMinidumpWritable child; child.SetData("child"); child.SetPhaseLate(); parent.AddChild(&child); TestStringMinidumpWritable grandchild; grandchild.SetData("grandchild"); child.AddChild(&grandchild); EXPECT_TRUE(parent.WriteEverything(&writer)); EXPECT_EQ(25u, writer.string().size()); EXPECT_EQ(std::string("parent\0\0grandchild\0\0child", 25), writer.string()); parent.Verify(); } { SCOPED_TRACE("family tree"); writer.Reset(); TestStringMinidumpWritable parent; parent.SetData("P.."); TestStringMinidumpWritable child_0; child_0.SetData("C0."); parent.AddChild(&child_0); TestStringMinidumpWritable child_1; child_1.SetData("C1."); parent.AddChild(&child_1); TestStringMinidumpWritable grandchild_00; grandchild_00.SetData("G00"); child_0.AddChild(&grandchild_00); TestStringMinidumpWritable grandchild_01; grandchild_01.SetData("G01"); child_0.AddChild(&grandchild_01); TestStringMinidumpWritable grandchild_10; grandchild_10.SetData("G10"); child_1.AddChild(&grandchild_10); TestStringMinidumpWritable grandchild_11; grandchild_11.SetData("G11"); child_1.AddChild(&grandchild_11); EXPECT_TRUE(parent.WriteEverything(&writer)); EXPECT_EQ(27u, writer.string().size()); EXPECT_EQ(std::string("P..\0C0.\0G00\0G01\0C1.\0G10\0G11", 27), writer.string()); parent.Verify(); } { SCOPED_TRACE("family tree with C0 late"); writer.Reset(); TestStringMinidumpWritable parent; parent.SetData("P.."); TestStringMinidumpWritable child_0; child_0.SetData("C0."); child_0.SetPhaseLate(); parent.AddChild(&child_0); TestStringMinidumpWritable child_1; child_1.SetData("C1."); parent.AddChild(&child_1); TestStringMinidumpWritable grandchild_00; grandchild_00.SetData("G00"); child_0.AddChild(&grandchild_00); TestStringMinidumpWritable grandchild_01; grandchild_01.SetData("G01"); child_0.AddChild(&grandchild_01); TestStringMinidumpWritable grandchild_10; grandchild_10.SetData("G10"); child_1.AddChild(&grandchild_10); TestStringMinidumpWritable grandchild_11; grandchild_11.SetData("G11"); child_1.AddChild(&grandchild_11); EXPECT_TRUE(parent.WriteEverything(&writer)); EXPECT_EQ(27u, writer.string().size()); EXPECT_EQ(std::string("P..\0G00\0G01\0C1.\0G10\0G11\0C0.", 27), writer.string()); parent.Verify(); } { SCOPED_TRACE("family tree with G0 late"); writer.Reset(); TestStringMinidumpWritable parent; parent.SetData("P.."); TestStringMinidumpWritable child_0; child_0.SetData("C0."); parent.AddChild(&child_0); TestStringMinidumpWritable child_1; child_1.SetData("C1."); parent.AddChild(&child_1); TestStringMinidumpWritable grandchild_00; grandchild_00.SetData("G00"); grandchild_00.SetPhaseLate(); child_0.AddChild(&grandchild_00); TestStringMinidumpWritable grandchild_01; grandchild_01.SetData("G01"); grandchild_01.SetPhaseLate(); child_0.AddChild(&grandchild_01); TestStringMinidumpWritable grandchild_10; grandchild_10.SetData("G10"); child_1.AddChild(&grandchild_10); TestStringMinidumpWritable grandchild_11; grandchild_11.SetData("G11"); child_1.AddChild(&grandchild_11); EXPECT_TRUE(parent.WriteEverything(&writer)); EXPECT_EQ(27u, writer.string().size()); EXPECT_EQ(std::string("P..\0C0.\0C1.\0G10\0G11\0G00\0G01", 27), writer.string()); parent.Verify(); } { SCOPED_TRACE("align 1"); writer.Reset(); TestStringMinidumpWritable parent; parent.SetData("p"); TestStringMinidumpWritable child; child.SetData("c"); child.SetAlignment(1); parent.AddChild(&child); EXPECT_TRUE(parent.WriteEverything(&writer)); EXPECT_EQ(2u, writer.string().size()); EXPECT_EQ("pc", writer.string()); parent.Verify(); } { SCOPED_TRACE("align 2"); writer.Reset(); TestStringMinidumpWritable parent; parent.SetData("p"); TestStringMinidumpWritable child; child.SetData("c"); child.SetAlignment(2); parent.AddChild(&child); EXPECT_TRUE(parent.WriteEverything(&writer)); EXPECT_EQ(3u, writer.string().size()); EXPECT_EQ(std::string("p\0c", 3), writer.string()); parent.Verify(); } } class TestRVAMinidumpWritable final : public BaseTestMinidumpWritable { public: TestRVAMinidumpWritable() : BaseTestMinidumpWritable(), rva_() {} ~TestRVAMinidumpWritable() {} void SetRVA(MinidumpWritable* other) { other->RegisterRVA(&rva_); } protected: virtual size_t SizeOfObject() override { EXPECT_GE(state(), kStateFrozen); return sizeof(rva_); } virtual bool WriteObject(FileWriterInterface* file_writer) override { BaseTestMinidumpWritable::WriteObject(file_writer); EXPECT_TRUE(file_writer->Write(&rva_, sizeof(rva_))); return true; } private: RVA rva_; DISALLOW_COPY_AND_ASSIGN(TestRVAMinidumpWritable); }; RVA RVAAtIndex(const std::string& string, size_t index) { return *reinterpret_cast(&string[index * sizeof(RVA)]); } TEST(MinidumpWritable, RVA) { StringFileWriter writer; { SCOPED_TRACE("unset"); writer.Reset(); TestRVAMinidumpWritable rva_writable; EXPECT_TRUE(rva_writable.WriteEverything(&writer)); ASSERT_EQ(sizeof(RVA), writer.string().size()); EXPECT_EQ(0 * sizeof(RVA), RVAAtIndex(writer.string(), 0)); rva_writable.Verify(); } { SCOPED_TRACE("self"); writer.Reset(); TestRVAMinidumpWritable rva_writable; rva_writable.SetRVA(&rva_writable); EXPECT_TRUE(rva_writable.WriteEverything(&writer)); ASSERT_EQ(sizeof(RVA), writer.string().size()); EXPECT_EQ(0 * sizeof(RVA), RVAAtIndex(writer.string(), 0)); rva_writable.Verify(); } { SCOPED_TRACE("parent-child self"); writer.Reset(); TestRVAMinidumpWritable parent; parent.SetRVA(&parent); TestRVAMinidumpWritable child; child.SetRVA(&child); parent.AddChild(&child); EXPECT_TRUE(parent.WriteEverything(&writer)); ASSERT_EQ(2 * sizeof(RVA), writer.string().size()); EXPECT_EQ(0 * sizeof(RVA), RVAAtIndex(writer.string(), 0)); EXPECT_EQ(1 * sizeof(RVA), RVAAtIndex(writer.string(), 1)); parent.Verify(); } { SCOPED_TRACE("parent-child only"); writer.Reset(); TestRVAMinidumpWritable parent; TestRVAMinidumpWritable child; parent.SetRVA(&child); parent.AddChild(&child); EXPECT_TRUE(parent.WriteEverything(&writer)); ASSERT_EQ(2 * sizeof(RVA), writer.string().size()); EXPECT_EQ(1 * sizeof(RVA), RVAAtIndex(writer.string(), 0)); EXPECT_EQ(0 * sizeof(RVA), RVAAtIndex(writer.string(), 1)); parent.Verify(); } { SCOPED_TRACE("parent-child circular"); writer.Reset(); TestRVAMinidumpWritable parent; TestRVAMinidumpWritable child; parent.SetRVA(&child); child.SetRVA(&parent); parent.AddChild(&child); EXPECT_TRUE(parent.WriteEverything(&writer)); ASSERT_EQ(2 * sizeof(RVA), writer.string().size()); EXPECT_EQ(1 * sizeof(RVA), RVAAtIndex(writer.string(), 0)); EXPECT_EQ(0 * sizeof(RVA), RVAAtIndex(writer.string(), 1)); parent.Verify(); } { SCOPED_TRACE("grandchildren"); writer.Reset(); TestRVAMinidumpWritable parent; TestRVAMinidumpWritable child; parent.SetRVA(&child); parent.AddChild(&child); TestRVAMinidumpWritable grandchild_0; grandchild_0.SetRVA(&child); child.AddChild(&grandchild_0); TestRVAMinidumpWritable grandchild_1; grandchild_1.SetRVA(&child); child.AddChild(&grandchild_1); TestRVAMinidumpWritable grandchild_2; grandchild_2.SetRVA(&child); child.AddChild(&grandchild_2); EXPECT_TRUE(parent.WriteEverything(&writer)); ASSERT_EQ(5 * sizeof(RVA), writer.string().size()); EXPECT_EQ(1 * sizeof(RVA), RVAAtIndex(writer.string(), 0)); EXPECT_EQ(0 * sizeof(RVA), RVAAtIndex(writer.string(), 1)); EXPECT_EQ(1 * sizeof(RVA), RVAAtIndex(writer.string(), 2)); EXPECT_EQ(1 * sizeof(RVA), RVAAtIndex(writer.string(), 3)); EXPECT_EQ(1 * sizeof(RVA), RVAAtIndex(writer.string(), 4)); parent.Verify(); } } class TestLocationDescriptorMinidumpWritable final : public BaseTestMinidumpWritable { public: TestLocationDescriptorMinidumpWritable() : BaseTestMinidumpWritable(), location_descriptor_(), string_() {} ~TestLocationDescriptorMinidumpWritable() {} void SetLocationDescriptor(MinidumpWritable* other) { other->RegisterLocationDescriptor(&location_descriptor_); } void SetString(const std::string& string) { string_ = string; } protected: virtual size_t SizeOfObject() override { EXPECT_GE(state(), kStateFrozen); // NUL-terminate. return sizeof(location_descriptor_) + string_.size() + 1; } virtual bool WriteObject(FileWriterInterface* file_writer) override { BaseTestMinidumpWritable::WriteObject(file_writer); WritableIoVec iov; iov.iov_base = &location_descriptor_; iov.iov_len = sizeof(location_descriptor_); std::vector iovecs(1, iov); // NUL-terminate. iov.iov_base = &string_[0]; iov.iov_len = string_.size() + 1; iovecs.push_back(iov); EXPECT_TRUE(file_writer->WriteIoVec(&iovecs)); return true; } private: MINIDUMP_LOCATION_DESCRIPTOR location_descriptor_; std::string string_; DISALLOW_COPY_AND_ASSIGN(TestLocationDescriptorMinidumpWritable); }; struct LocationDescriptorAndData { MINIDUMP_LOCATION_DESCRIPTOR location_descriptor; char string[1]; }; const LocationDescriptorAndData* LDDAtIndex(const std::string& string, size_t index) { return reinterpret_cast(&string[index]); } TEST(MinidumpWritable, LocationDescriptor) { StringFileWriter writer; { SCOPED_TRACE("unset"); writer.Reset(); TestLocationDescriptorMinidumpWritable location_descriptor_writable; EXPECT_TRUE(location_descriptor_writable.WriteEverything(&writer)); ASSERT_EQ(9u, writer.string().size()); const LocationDescriptorAndData* ldd = LDDAtIndex(writer.string(), 0); EXPECT_EQ(0u, ldd->location_descriptor.DataSize); EXPECT_EQ(0u, ldd->location_descriptor.Rva); location_descriptor_writable.Verify(); } { SCOPED_TRACE("self"); writer.Reset(); TestLocationDescriptorMinidumpWritable location_descriptor_writable; location_descriptor_writable.SetLocationDescriptor( &location_descriptor_writable); EXPECT_TRUE(location_descriptor_writable.WriteEverything(&writer)); ASSERT_EQ(9u, writer.string().size()); const LocationDescriptorAndData* ldd = LDDAtIndex(writer.string(), 0); EXPECT_EQ(9u, ldd->location_descriptor.DataSize); EXPECT_EQ(0u, ldd->location_descriptor.Rva); location_descriptor_writable.Verify(); } { SCOPED_TRACE("self with data"); writer.Reset(); TestLocationDescriptorMinidumpWritable location_descriptor_writable; location_descriptor_writable.SetLocationDescriptor( &location_descriptor_writable); location_descriptor_writable.SetString("zz"); EXPECT_TRUE(location_descriptor_writable.WriteEverything(&writer)); ASSERT_EQ(11u, writer.string().size()); const LocationDescriptorAndData* ldd = LDDAtIndex(writer.string(), 0); EXPECT_EQ(11u, ldd->location_descriptor.DataSize); EXPECT_EQ(0u, ldd->location_descriptor.Rva); EXPECT_STREQ("zz", ldd->string); location_descriptor_writable.Verify(); } { SCOPED_TRACE("parent-child self"); writer.Reset(); TestLocationDescriptorMinidumpWritable parent; parent.SetLocationDescriptor(&parent); parent.SetString("yy"); TestLocationDescriptorMinidumpWritable child; child.SetLocationDescriptor(&child); child.SetString("x"); parent.AddChild(&child); EXPECT_TRUE(parent.WriteEverything(&writer)); ASSERT_EQ(22u, writer.string().size()); const LocationDescriptorAndData* ldd = LDDAtIndex(writer.string(), 0); EXPECT_EQ(11u, ldd->location_descriptor.DataSize); EXPECT_EQ(0u, ldd->location_descriptor.Rva); EXPECT_STREQ("yy", ldd->string); ldd = LDDAtIndex(writer.string(), 12); EXPECT_EQ(10u, ldd->location_descriptor.DataSize); EXPECT_EQ(12u, ldd->location_descriptor.Rva); EXPECT_STREQ("x", ldd->string); parent.Verify(); } { SCOPED_TRACE("parent-child only"); writer.Reset(); TestLocationDescriptorMinidumpWritable parent; TestLocationDescriptorMinidumpWritable child; parent.SetLocationDescriptor(&child); parent.SetString("www"); child.SetString("vv"); parent.AddChild(&child); EXPECT_TRUE(parent.WriteEverything(&writer)); ASSERT_EQ(23u, writer.string().size()); const LocationDescriptorAndData* ldd = LDDAtIndex(writer.string(), 0); EXPECT_EQ(11u, ldd->location_descriptor.DataSize); EXPECT_EQ(12u, ldd->location_descriptor.Rva); EXPECT_STREQ("www", ldd->string); ldd = LDDAtIndex(writer.string(), 12); EXPECT_EQ(0u, ldd->location_descriptor.DataSize); EXPECT_EQ(0u, ldd->location_descriptor.Rva); EXPECT_STREQ("vv", ldd->string); parent.Verify(); } { SCOPED_TRACE("parent-child circular"); writer.Reset(); TestLocationDescriptorMinidumpWritable parent; TestLocationDescriptorMinidumpWritable child; parent.SetLocationDescriptor(&child); parent.SetString("uuuu"); child.SetLocationDescriptor(&parent); child.SetString("tttt"); parent.AddChild(&child); EXPECT_TRUE(parent.WriteEverything(&writer)); ASSERT_EQ(29u, writer.string().size()); const LocationDescriptorAndData* ldd = LDDAtIndex(writer.string(), 0); EXPECT_EQ(13u, ldd->location_descriptor.DataSize); EXPECT_EQ(16u, ldd->location_descriptor.Rva); EXPECT_STREQ("uuuu", ldd->string); ldd = LDDAtIndex(writer.string(), 16); EXPECT_EQ(13u, ldd->location_descriptor.DataSize); EXPECT_EQ(0u, ldd->location_descriptor.Rva); EXPECT_STREQ("tttt", ldd->string); parent.Verify(); } { SCOPED_TRACE("grandchildren"); writer.Reset(); TestLocationDescriptorMinidumpWritable parent; TestLocationDescriptorMinidumpWritable child; parent.SetLocationDescriptor(&child); parent.SetString("s"); parent.AddChild(&child); child.SetString("r"); TestLocationDescriptorMinidumpWritable grandchild_0; grandchild_0.SetLocationDescriptor(&child); grandchild_0.SetString("q"); child.AddChild(&grandchild_0); TestLocationDescriptorMinidumpWritable grandchild_1; grandchild_1.SetLocationDescriptor(&child); grandchild_1.SetString("p"); child.AddChild(&grandchild_1); TestLocationDescriptorMinidumpWritable grandchild_2; grandchild_2.SetLocationDescriptor(&child); grandchild_2.SetString("o"); child.AddChild(&grandchild_2); EXPECT_TRUE(parent.WriteEverything(&writer)); ASSERT_EQ(58u, writer.string().size()); const LocationDescriptorAndData* ldd = LDDAtIndex(writer.string(), 0); EXPECT_EQ(10u, ldd->location_descriptor.DataSize); EXPECT_EQ(12u, ldd->location_descriptor.Rva); EXPECT_STREQ("s", ldd->string); ldd = LDDAtIndex(writer.string(), 12); EXPECT_EQ(0u, ldd->location_descriptor.DataSize); EXPECT_EQ(0u, ldd->location_descriptor.Rva); EXPECT_STREQ("r", ldd->string); ldd = LDDAtIndex(writer.string(), 24); EXPECT_EQ(10u, ldd->location_descriptor.DataSize); EXPECT_EQ(12u, ldd->location_descriptor.Rva); EXPECT_STREQ("q", ldd->string); ldd = LDDAtIndex(writer.string(), 36); EXPECT_EQ(10u, ldd->location_descriptor.DataSize); EXPECT_EQ(12u, ldd->location_descriptor.Rva); EXPECT_STREQ("p", ldd->string); ldd = LDDAtIndex(writer.string(), 48); EXPECT_EQ(10u, ldd->location_descriptor.DataSize); EXPECT_EQ(12u, ldd->location_descriptor.Rva); EXPECT_STREQ("o", ldd->string); parent.Verify(); } } } // namespace } // namespace test } // namespace crashpad