blob: eca3743c07b7f29868472e4f3681b8e0372a2b6c [file] [log] [blame]
// Copyright 2017 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 "base/trace_event/heap_profiler_string_deduplicator.h"
#include <memory>
#include <string>
#include "base/memory/ptr_util.h"
#include "base/trace_event/trace_event_argument.h"
#include "base/values.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
namespace trace_event {
namespace {
// Calls StringDeduplicator::SerializeIncrementally() and returns ListValue
// with serialized entries.
std::unique_ptr<ListValue> SerializeEntriesIncrementally(
StringDeduplicator* dedup) {
TracedValue traced_value;
traced_value.BeginArray("");
dedup->SerializeIncrementally(&traced_value);
traced_value.EndArray();
auto base_value = traced_value.ToBaseValue();
DictionaryValue* dictionary;
std::unique_ptr<Value> entries;
if (!base_value->GetAsDictionary(&dictionary) ||
!dictionary->Remove("", &entries)) {
return nullptr;
}
return ListValue::From(std::move(entries));
}
struct StringMapping {
const int id;
const char* const string;
};
std::unique_ptr<ListValue> SerializeMappingsAsEntries(
std::initializer_list<StringMapping> mappings) {
auto entries = MakeUnique<ListValue>();
for (const auto& mapping : mappings) {
auto entry = MakeUnique<DictionaryValue>();
entry->SetInteger("id", mapping.id);
entry->SetString("string", mapping.string);
entries->Append(std::move(entry));
}
return entries;
}
void ExpectIncrementalEntries(StringDeduplicator* dedup,
std::initializer_list<StringMapping> mappings) {
auto entries = SerializeEntriesIncrementally(dedup);
ASSERT_TRUE(entries);
auto expected_entries = SerializeMappingsAsEntries(mappings);
ASSERT_TRUE(expected_entries->Equals(entries.get()))
<< "expected_entries = " << *expected_entries << "entries = " << *entries;
}
} // namespace
TEST(StringDeduplicatorTest, ImplicitId0) {
StringDeduplicator dedup;
// NULL string is mapped to an implicitly added ID #0.
ExpectIncrementalEntries(&dedup, {{0, "[null]"}});
ASSERT_EQ(0, dedup.Insert(nullptr));
// Even though ID #0 is serialized as "[null]", it's distinct from
// explicitly added "[null]" string.
ASSERT_EQ(1, dedup.Insert("[null]"));
ExpectIncrementalEntries(&dedup, {{1, "[null]"}});
}
TEST(StringDeduplicatorTest, Deduplicate) {
StringDeduplicator dedup;
ASSERT_EQ(1, dedup.Insert("foo"));
ASSERT_EQ(2, dedup.Insert("bar"));
ASSERT_EQ(3, dedup.Insert("baz"));
// Inserting again should return the same IDs.
ASSERT_EQ(2, dedup.Insert("bar"));
ASSERT_EQ(1, dedup.Insert("foo"));
ASSERT_EQ(3, dedup.Insert("baz"));
}
TEST(StringDeduplicatorTest, InsertCopies) {
StringDeduplicator dedup;
std::string string = "foo";
ASSERT_EQ(1, dedup.Insert(string));
// StringDeduplicatorTest::Insert() takes StringPiece, which implicitly
// constructs from both const char* and std::string. Check that Insert()
// actually copies string data, and doesn't simply copy StringPieces.
string = "???";
ASSERT_EQ(1, dedup.Insert("foo"));
}
TEST(StringDeduplicatorTest, SerializeIncrementally) {
StringDeduplicator dedup;
ASSERT_EQ(1, dedup.Insert("foo"));
ASSERT_EQ(2, dedup.Insert("bar"));
ExpectIncrementalEntries(&dedup, {{0, "[null]"}, {1, "foo"}, {2, "bar"}});
ASSERT_EQ(2, dedup.Insert("bar"));
ASSERT_EQ(3, dedup.Insert("baz"));
ExpectIncrementalEntries(&dedup, {{3, "baz"}});
ExpectIncrementalEntries(&dedup, {});
}
} // namespace trace_event
} // namespace base