blob: 8ea7f8a15597259092d91fd9a157450ec3fe39b5 [file] [log] [blame]
// 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 <string>
#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<internal::UserDataMinidumpStreamListEntry*>(
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<char*>(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<internal::UserDataMinidumpStreamListEntry*>(
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<char*>(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