Index: components/tracing/core/trace_buffer_reader_unittest.cc |
diff --git a/components/tracing/core/trace_buffer_reader_unittest.cc b/components/tracing/core/trace_buffer_reader_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..8a4210b99104ac5185ed142412a20d1919be0a78 |
--- /dev/null |
+++ b/components/tracing/core/trace_buffer_reader_unittest.cc |
@@ -0,0 +1,227 @@ |
+// Copyright 2016 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 "components/tracing/core/trace_buffer_reader.h" |
+ |
+#include "base/macros.h" |
+#include "base/memory/ptr_util.h" |
+#include "components/tracing/core/string_interning.h" |
+#include "components/tracing/test/golden_protos/event.pb.h" |
+#include "components/tracing/test/golden_protos/events_chunk.pb.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+namespace tracing { |
+namespace v2 { |
+namespace { |
+ |
+namespace golden_protos = ::tracing::proto; |
+ |
+::testing::AssertionResult ValueEquals(const char* m_expr, |
+ const char* n_expr, |
+ const base::Value& m, |
+ const base::Value& n) { |
+ if (m.Equals(&n)) |
+ return ::testing::AssertionSuccess(); |
+ |
+ return ::testing::AssertionFailure() << "\nExpected:\n" |
+ << m << "\nActual:\n" |
+ << n; |
+} |
+ |
+void ParseChunk(TraceBufferReader* reader, const std::string& chunk) { |
+ reader->ParseChunk(reinterpret_cast<const uint8_t*>(chunk.data()), |
+ chunk.size()); |
+} |
+ |
+// Parse a zero length chunk and a chunk with no events. In both cases parsing |
+// should succeed but no event should be decoded. |
+TEST(TraceBufferReaderTest, EmptyChunk) { |
+ golden_protos::EventsChunk chunk; |
+ chunk.set_writer_id(1); |
+ chunk.set_seq_id(0); |
+ |
+ TraceBufferReader reader(TraceBufferReader::kBuildLegacyJSONEvents); |
+ std::string zero_len_chunk; |
+ ParseChunk(&reader, zero_len_chunk); |
+ ParseChunk(&reader, chunk.SerializeAsString()); |
+ ASSERT_EQ(0u, reader.legacy_events()->GetSize()); |
+} |
+ |
+TEST(TraceBufferReaderTest, OneChunkOneEvent) { |
+ golden_protos::Event evt; |
+ evt.set_type(golden_protos::Event::COMPLETE); |
+ evt.set_name_str("copied_str_name"); |
+ auto interned_cat_name = InternString("interned_category_name"); |
+ evt.set_category_id(interned_cat_name); |
+ evt.set_timestamp(42); |
+ evt.set_thread_timestamp(0xf1f2f3f4); |
+ |
+ golden_protos::EventsChunk chunk; |
+ chunk.set_writer_id(1); |
+ chunk.set_seq_id(0); |
+ chunk.add_events(evt.SerializeAsString()); |
+ |
+ TraceBufferReader reader(TraceBufferReader::kBuildLegacyJSONEvents | |
+ TraceBufferReader::kBuildInternedStringsTable); |
+ ParseChunk(&reader, chunk.SerializeAsString()); |
+ ASSERT_EQ(1u, reader.legacy_events()->GetSize()); |
+ base::DictionaryValue* decoded_evt = nullptr; |
+ ASSERT_TRUE(reader.legacy_events()->GetDictionary(0, &decoded_evt)); |
+ |
+ base::DictionaryValue expected_value; |
+ expected_value.SetString("ph", "X"); |
+ expected_value.SetString("name", "copied_str_name"); |
+ expected_value.SetString("cat", "interned_category_name"); |
+ expected_value.SetInteger("ts", 42); |
+ expected_value.SetInteger("tts", static_cast<int>(0xf1f2f3f4)); |
+ EXPECT_PRED_FORMAT2(ValueEquals, expected_value, *decoded_evt); |
+ |
+ ASSERT_EQ(1u, reader.interned_strings().count(interned_cat_name)); |
+ EXPECT_EQ("interned_category_name", |
+ reader.interned_strings().at(interned_cat_name)); |
+} |
+ |
+TEST(TraceBufferReaderTest, OneChunkManyEvents) { |
+ golden_protos::EventsChunk chunk; |
+ chunk.set_writer_id(1); |
+ chunk.set_seq_id(42); |
+ const char* kCatNames[] = {"cat1", "cat2", "cat3", "cat4"}; |
+ const size_t kNumEvents = 100; |
+ |
+ for (size_t i = 0; i < kNumEvents; ++i) { |
+ golden_protos::Event evt; |
+ evt.set_type(golden_protos::Event::COMPLETE); |
+ evt.set_name_str("copied_str_name"); |
+ evt.set_category_id(InternString(kCatNames[i % arraysize(kCatNames)])); |
+ evt.set_timestamp(0xff000000 + i); |
+ chunk.add_events(evt.SerializeAsString()); |
+ } |
+ |
+ TraceBufferReader reader(TraceBufferReader::kBuildLegacyJSONEvents | |
+ TraceBufferReader::kBuildInternedStringsTable); |
+ ParseChunk(&reader, chunk.SerializeAsString()); |
+ ASSERT_EQ(kNumEvents, reader.legacy_events()->GetSize()); |
+ for (size_t i = 0; i < kNumEvents; ++i) { |
+ base::DictionaryValue* decoded_evt = nullptr; |
+ ASSERT_TRUE(reader.legacy_events()->GetDictionary(i, &decoded_evt)); |
+ base::DictionaryValue expected_value; |
+ expected_value.SetString("ph", "X"); |
+ expected_value.SetString("name", "copied_str_name"); |
+ expected_value.SetString("cat", kCatNames[i % arraysize(kCatNames)]); |
+ expected_value.SetInteger("ts", static_cast<int>(0xff000000 + i)); |
+ ASSERT_PRED_FORMAT2(ValueEquals, expected_value, *decoded_evt); |
+ } |
+} |
+ |
+// Test the decoding events that are fragmented across multiple chunks. |
+TEST(TraceBufferReaderTest, FragmentedEvents) { |
+ golden_protos::Event evt[3]; |
+ for (size_t i = 0; i < arraysize(evt); ++i) { |
+ evt[i].set_type(golden_protos::Event::COMPLETE); |
+ evt[i].set_name_str("copied_str_name"); |
+ evt[i].set_category_id(InternString("interned_category_name")); |
+ evt[i].set_timestamp(i); |
+ } |
+ |
+ // The 2nd event (evt[1]) will be split into three fragments and distributed |
+ // over all three chunks. |
+ const size_t evt1_size = evt[1].ByteSize(); |
+ DCHECK_GT(evt1_size, 8u); |
+ std::unique_ptr<uint8_t[]> serialized_evt1(new uint8_t[evt1_size]); |
+ evt[1].SerializeToArray(serialized_evt1.get(), evt1_size); |
+ |
+ // Create three chunks and split the three events as follows: |
+ // chunk[0]: evt[0] (full), evt[1] (fragment 1/3) |
+ // chunk[1]: evt[1] (fragment 2/3) |
+ // chunk[2]: evt[1] (fragment 3/3), evt[2] (full) |
+ |
+ golden_protos::EventsChunk chunk[3]; |
+ for (size_t i = 0; i < arraysize(chunk); ++i) { |
+ chunk[i].set_writer_id(42); |
+ chunk[i].set_seq_id(i); |
+ } |
+ |
+ chunk[0].set_first_event_continues_from_prev_chunk(false); |
+ chunk[0].set_last_event_continues_on_next_chunk(true); |
+ chunk[0].add_events(evt[0].SerializeAsString()); |
+ chunk[0].add_events(&serialized_evt1[0], 4); |
+ |
+ chunk[1].set_first_event_continues_from_prev_chunk(true); |
+ chunk[1].set_last_event_continues_on_next_chunk(true); |
+ chunk[1].add_events(&serialized_evt1[4], 4); |
+ |
+ chunk[2].set_first_event_continues_from_prev_chunk(true); |
+ chunk[2].add_events(&serialized_evt1[8], evt1_size - 8); |
+ chunk[2].add_events(evt[2].SerializeAsString()); |
+ |
+ TraceBufferReader reader(TraceBufferReader::kBuildLegacyJSONEvents | |
+ TraceBufferReader::kBuildInternedStringsTable); |
+ for (size_t i = 0; i < arraysize(chunk); ++i) |
+ ParseChunk(&reader, chunk[i].SerializeAsString()); |
+ |
+ ASSERT_EQ(arraysize(evt), reader.legacy_events()->GetSize()); |
+ for (size_t i = 0; i < arraysize(evt); ++i) { |
+ base::DictionaryValue* decoded_evt = nullptr; |
+ ASSERT_TRUE(reader.legacy_events()->GetDictionary(i, &decoded_evt)); |
+ |
+ base::DictionaryValue expected_value; |
+ expected_value.SetString("ph", "X"); |
+ expected_value.SetString("name", "copied_str_name"); |
+ expected_value.SetString("cat", "interned_category_name"); |
+ expected_value.SetInteger("ts", static_cast<int>(i)); |
+ ASSERT_PRED_FORMAT2(ValueEquals, expected_value, *decoded_evt); |
+ } |
+} |
+ |
+// Test that the fragment reconstruction logic discards fragments if one of |
+// the chunks is missing. |
+TEST(TraceBufferReaderTest, FragmentedEventsWithMissingChunks) { |
+ golden_protos::Event evt[2]; |
+ for (size_t i = 0; i < arraysize(evt); ++i) { |
+ evt[i].set_type(golden_protos::Event::METADATA); |
+ evt[i].set_timestamp(i); |
+ } |
+ |
+ golden_protos::EventsChunk chunk[2]; |
+ for (size_t i = 0; i < arraysize(chunk); ++i) { |
+ chunk[i].set_writer_id(42); |
+ chunk[i].set_seq_id(1 + i); |
+ } |
+ |
+ // The 2nd event (evt[1]) will be split into two fragments. |
+ const size_t evt1_size = evt[1].ByteSize(); |
+ DCHECK_GT(evt1_size, 2u); |
+ std::unique_ptr<uint8_t[]> serialized_evt1(new uint8_t[evt1_size]); |
+ evt[1].SerializeToArray(serialized_evt1.get(), evt1_size); |
+ |
+ chunk[0].set_first_event_continues_from_prev_chunk(true); |
+ chunk[0].set_last_event_continues_on_next_chunk(true); |
+ chunk[0].add_events("should be discarded because seq id 0 is not seen."); |
+ chunk[0].add_events(evt[0].SerializeAsString()); |
+ chunk[0].add_events(&serialized_evt1[0], 2); |
+ |
+ chunk[1].set_first_event_continues_from_prev_chunk(true); |
+ chunk[1].set_last_event_continues_on_next_chunk(true); |
+ chunk[1].add_events(&serialized_evt1[0] + 2, evt1_size - 2); |
+ chunk[1].add_events("should be discarded because seq id 2 is not seen."); |
+ |
+ TraceBufferReader reader(TraceBufferReader::kBuildLegacyJSONEvents); |
+ ParseChunk(&reader, chunk[0].SerializeAsString()); |
+ ParseChunk(&reader, chunk[1].SerializeAsString()); |
+ |
+ ASSERT_EQ(arraysize(evt), reader.legacy_events()->GetSize()); |
+ for (size_t i = 0; i < arraysize(evt); ++i) { |
+ base::DictionaryValue* decoded_evt = nullptr; |
+ ASSERT_TRUE(reader.legacy_events()->GetDictionary(i, &decoded_evt)); |
+ |
+ base::DictionaryValue expected_value; |
+ expected_value.SetString("ph", "M"); |
+ expected_value.SetInteger("ts", static_cast<int>(i)); |
+ ASSERT_PRED_FORMAT2(ValueEquals, expected_value, *decoded_evt); |
+ } |
+} |
+ |
+} // namespace |
+} // namespace v2 |
+} // namespace tracing |