// Copyright 2024 The Crashpad Authors // // 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 "client/crashpad_info.h" #include #include "gtest/gtest.h" namespace crashpad { namespace test { namespace { constexpr uint32_t kTestStreamType = 0x33333; class CrashpadInfoTest : public testing::Test { protected: CrashpadInfo& crashpad_info() { return crashpad_info_; } // Returns the current head of the list of streams in `crashpad_info_`. Note // that the returned pointer is invalidated if a stream is added or updated. internal::UserDataMinidumpStreamListEntry* GetCurrentHead() { return crashpad_info().GetUserDataMinidumpStreamHeadForTesting(); } // Returns a pointer to the next node in the list after the given `node`. internal::UserDataMinidumpStreamListEntry* GetNext( internal::UserDataMinidumpStreamListEntry* node) { return reinterpret_cast( node->next); } internal::UserDataMinidumpStreamListEntry* initial_head() { return initial_head_; } internal::UserDataMinidumpStreamListEntry* initial_tail() { return initial_tail_; } private: void SetUp() override { ASSERT_EQ(nullptr, GetCurrentHead()); // Create a simple test list with the structure // `initial_head_` -> `initial_tail_`. initial_tail_ = AddStream(0x11111, kInitialTailData); initial_head_ = AddStream(0x22222, kInitialHeadData); // Validate the list's contents. auto current = GetCurrentHead(); ASSERT_EQ(initial_head_, current); ASSERT_EQ(kInitialHeadData, reinterpret_cast(current->base_address)); current = GetNext(current); ASSERT_EQ(initial_tail_, current); ASSERT_EQ(nullptr, GetNext(current)); } void TearDown() override { // Free the list. The list lives until process exit in production, but must // be freed in tests as multiple tests run in the same process. auto current = GetCurrentHead(); while (current) { auto next = GetNext(current); delete current; current = next; } } internal::UserDataMinidumpStreamListEntry* AddStream(uint32_t stream_type, const char* data) { return reinterpret_cast( crashpad_info().AddUserDataMinidumpStream( stream_type, data, strlen(data))); } CrashpadInfo crashpad_info_; static constexpr char kInitialHeadData[] = "head"; static constexpr char kInitialTailData[] = "tail"; internal::UserDataMinidumpStreamListEntry* initial_head_ = nullptr; internal::UserDataMinidumpStreamListEntry* initial_tail_ = nullptr; }; // Tests that updating the head of the list updates the head pointer, the new // head contains the updated data, and the updated node points to the next node. TEST_F(CrashpadInfoTest, UpdateUserDataMinidumpStreamHead) { const std::string new_data = "this is a new string"; const auto new_entry = crashpad_info().UpdateUserDataMinidumpStream( initial_head(), kTestStreamType, new_data.data(), new_data.size()); const auto head = GetCurrentHead(); EXPECT_EQ(new_entry, head); EXPECT_EQ(new_data.data(), reinterpret_cast(head->base_address)); EXPECT_EQ(new_data.size(), head->size); EXPECT_EQ(kTestStreamType, head->stream_type); EXPECT_EQ(initial_tail(), GetNext(head)); } // Tests that updating the tail of the list results in a tail pointing to // nullptr, and that the node before the updated node points to it. TEST_F(CrashpadInfoTest, UpdateUserDataMinidumpStreamTail) { const std::string new_data = "new"; const auto new_entry = crashpad_info().UpdateUserDataMinidumpStream( initial_tail(), kTestStreamType, new_data.data(), new_data.size()); const auto tail = GetNext(GetCurrentHead()); EXPECT_EQ(new_entry, tail); EXPECT_EQ(nullptr, GetNext(tail)); } // Tests that the handle returned from updating an entry is usable for updating // the entry again. TEST_F(CrashpadInfoTest, UpdateUserDataMinidumpStreamMultipleTimes) { // Update the entry at the head; the updated entry should become the new head. const std::string new_data = "new"; const auto new_entry_1 = crashpad_info().UpdateUserDataMinidumpStream( initial_head(), kTestStreamType, new_data.data(), new_data.size()); EXPECT_EQ(new_entry_1, GetCurrentHead()); // Update the updated entry again; another new entry should replace it as // head. const auto new_entry_2 = crashpad_info().UpdateUserDataMinidumpStream( new_entry_1, kTestStreamType, new_data.data(), new_data.size()); EXPECT_NE(new_entry_1, new_entry_2); EXPECT_EQ(new_entry_2, GetCurrentHead()); EXPECT_EQ(initial_tail(), GetNext(GetCurrentHead())); } } // namespace } // namespace test } // namespace crashpad