| // Copyright 2015 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 "net/ssl/ssl_client_session_cache.h" | 
 |  | 
 | #include "base/run_loop.h" | 
 | #include "base/strings/string_number_conversions.h" | 
 | #include "base/test/scoped_task_environment.h" | 
 | #include "base/test/simple_test_clock.h" | 
 | #include "base/time/time.h" | 
 | #include "base/trace_event/memory_allocator_dump.h" | 
 | #include "base/trace_event/process_memory_dump.h" | 
 | #include "base/trace_event/trace_event_argument.h" | 
 | #include "testing/gmock/include/gmock/gmock.h" | 
 | #include "testing/gtest/include/gtest/gtest.h" | 
 | #include "third_party/boringssl/src/include/openssl/ssl.h" | 
 |  | 
 | using testing::Contains; | 
 | using testing::Eq; | 
 | using testing::Field; | 
 | using testing::ByRef; | 
 |  | 
 | namespace net { | 
 |  | 
 | namespace { | 
 |  | 
 | std::unique_ptr<base::SimpleTestClock> MakeTestClock() { | 
 |   std::unique_ptr<base::SimpleTestClock> clock = | 
 |       std::make_unique<base::SimpleTestClock>(); | 
 |   // SimpleTestClock starts at the null base::Time which converts to and from | 
 |   // time_t confusingly. | 
 |   clock->SetNow(base::Time::FromTimeT(1000000000)); | 
 |   return clock; | 
 | } | 
 |  | 
 | class SSLClientSessionCacheTest : public testing::Test { | 
 |  public: | 
 |   SSLClientSessionCacheTest() : ssl_ctx_(SSL_CTX_new(TLS_method())) {} | 
 |  | 
 |  protected: | 
 |   bssl::UniquePtr<SSL_SESSION> NewSSLSession( | 
 |       uint16_t version = TLS1_2_VERSION) { | 
 |     SSL_SESSION* session = SSL_SESSION_new(ssl_ctx_.get()); | 
 |     if (!SSL_SESSION_set_protocol_version(session, version)) | 
 |       return nullptr; | 
 |     return bssl::UniquePtr<SSL_SESSION>(session); | 
 |   } | 
 |  | 
 |   bssl::UniquePtr<SSL_SESSION> MakeTestSession(base::Time now, | 
 |                                                base::TimeDelta timeout) { | 
 |     bssl::UniquePtr<SSL_SESSION> session = NewSSLSession(); | 
 |     SSL_SESSION_set_time(session.get(), now.ToTimeT()); | 
 |     SSL_SESSION_set_timeout(session.get(), timeout.InSeconds()); | 
 |     return session; | 
 |   } | 
 |  | 
 |  private: | 
 |   bssl::UniquePtr<SSL_CTX> ssl_ctx_; | 
 | }; | 
 |  | 
 | }  // namespace | 
 |  | 
 | // These tests rely on memory corruption detectors to verify that | 
 | // SSL_SESSION reference counts were correctly managed and no sessions | 
 | // leaked or were accessed after free. | 
 |  | 
 | // Test basic insertion and lookup operations. | 
 | TEST_F(SSLClientSessionCacheTest, Basic) { | 
 |   SSLClientSessionCache::Config config; | 
 |   SSLClientSessionCache cache(config); | 
 |  | 
 |   bssl::UniquePtr<SSL_SESSION> session1 = NewSSLSession(); | 
 |   bssl::UniquePtr<SSL_SESSION> session2 = NewSSLSession(); | 
 |   bssl::UniquePtr<SSL_SESSION> session3 = NewSSLSession(); | 
 |  | 
 |   EXPECT_EQ(nullptr, cache.Lookup("key1").get()); | 
 |   EXPECT_EQ(nullptr, cache.Lookup("key2").get()); | 
 |   EXPECT_EQ(0u, cache.size()); | 
 |  | 
 |   cache.Insert("key1", session1.get()); | 
 |   EXPECT_EQ(session1.get(), cache.Lookup("key1").get()); | 
 |   EXPECT_EQ(nullptr, cache.Lookup("key2").get()); | 
 |   EXPECT_EQ(1u, cache.size()); | 
 |  | 
 |   cache.Insert("key2", session2.get()); | 
 |   EXPECT_EQ(session1.get(), cache.Lookup("key1").get()); | 
 |   EXPECT_EQ(session2.get(), cache.Lookup("key2").get()); | 
 |   EXPECT_EQ(2u, cache.size()); | 
 |  | 
 |   cache.Insert("key1", session3.get()); | 
 |   EXPECT_EQ(session3.get(), cache.Lookup("key1").get()); | 
 |   EXPECT_EQ(session2.get(), cache.Lookup("key2").get()); | 
 |   EXPECT_EQ(2u, cache.size()); | 
 |  | 
 |   cache.Flush(); | 
 |   EXPECT_EQ(nullptr, cache.Lookup("key1").get()); | 
 |   EXPECT_EQ(nullptr, cache.Lookup("key2").get()); | 
 |   EXPECT_EQ(nullptr, cache.Lookup("key3").get()); | 
 |   EXPECT_EQ(0u, cache.size()); | 
 | } | 
 |  | 
 | // Test basic insertion and lookup operations with single-use sessions. | 
 | TEST_F(SSLClientSessionCacheTest, BasicSingleUse) { | 
 |   SSLClientSessionCache::Config config; | 
 |   SSLClientSessionCache cache(config); | 
 |  | 
 |   bssl::UniquePtr<SSL_SESSION> session1 = NewSSLSession(TLS1_3_VERSION); | 
 |   bssl::UniquePtr<SSL_SESSION> session2 = NewSSLSession(TLS1_3_VERSION); | 
 |   bssl::UniquePtr<SSL_SESSION> session3 = NewSSLSession(TLS1_3_VERSION); | 
 |  | 
 |   EXPECT_EQ(nullptr, cache.Lookup("key1").get()); | 
 |   EXPECT_EQ(nullptr, cache.Lookup("key2").get()); | 
 |   EXPECT_EQ(0u, cache.size()); | 
 |  | 
 |   cache.Insert("key1", session1.get()); | 
 |   EXPECT_EQ(session1.get(), cache.Lookup("key1").get()); | 
 |   EXPECT_EQ(nullptr, cache.Lookup("key2").get()); | 
 |   EXPECT_EQ(0u, cache.size()); | 
 |  | 
 |   EXPECT_EQ(nullptr, cache.Lookup("key1").get()); | 
 |  | 
 |   cache.Insert("key1", session1.get()); | 
 |   cache.Insert("key1", session1.get()); | 
 |   cache.Insert("key2", session2.get()); | 
 |  | 
 |   EXPECT_EQ(session1.get(), cache.Lookup("key1").get()); | 
 |   EXPECT_EQ(session2.get(), cache.Lookup("key2").get()); | 
 |   EXPECT_EQ(1u, cache.size()); | 
 |  | 
 |   EXPECT_EQ(session1.get(), cache.Lookup("key1").get()); | 
 |   EXPECT_EQ(nullptr, cache.Lookup("key2").get()); | 
 |  | 
 |   cache.Insert("key1", session1.get()); | 
 |   cache.Insert("key1", session3.get()); | 
 |   cache.Insert("key2", session2.get()); | 
 |   EXPECT_EQ(session3.get(), cache.Lookup("key1").get()); | 
 |   EXPECT_EQ(session1.get(), cache.Lookup("key1").get()); | 
 |   EXPECT_EQ(session2.get(), cache.Lookup("key2").get()); | 
 |   EXPECT_EQ(0u, cache.size()); | 
 |  | 
 |   cache.Flush(); | 
 |   EXPECT_EQ(nullptr, cache.Lookup("key1").get()); | 
 |   EXPECT_EQ(nullptr, cache.Lookup("key2").get()); | 
 |   EXPECT_EQ(nullptr, cache.Lookup("key3").get()); | 
 |   EXPECT_EQ(0u, cache.size()); | 
 |  | 
 |   cache.Insert("key1", session1.get()); | 
 |   cache.Insert("key1", session2.get()); | 
 |   cache.Insert("key1", session3.get()); | 
 |   EXPECT_EQ(session3.get(), cache.Lookup("key1").get()); | 
 |   EXPECT_EQ(session2.get(), cache.Lookup("key1").get()); | 
 |   EXPECT_EQ(nullptr, cache.Lookup("key1").get()); | 
 | } | 
 |  | 
 | // Test insertion and lookup operations with both single-use and reusable | 
 | // sessions. | 
 | TEST_F(SSLClientSessionCacheTest, MixedUse) { | 
 |   SSLClientSessionCache::Config config; | 
 |   SSLClientSessionCache cache(config); | 
 |  | 
 |   bssl::UniquePtr<SSL_SESSION> session_single = NewSSLSession(TLS1_3_VERSION); | 
 |   bssl::UniquePtr<SSL_SESSION> session_reuse = NewSSLSession(TLS1_2_VERSION); | 
 |  | 
 |   EXPECT_EQ(nullptr, cache.Lookup("key1").get()); | 
 |   EXPECT_EQ(0u, cache.size()); | 
 |  | 
 |   cache.Insert("key1", session_reuse.get()); | 
 |   EXPECT_EQ(session_reuse.get(), cache.Lookup("key1").get()); | 
 |   EXPECT_EQ(1u, cache.size()); | 
 |  | 
 |   cache.Insert("key1", session_single.get()); | 
 |   EXPECT_EQ(session_single.get(), cache.Lookup("key1").get()); | 
 |   EXPECT_EQ(nullptr, cache.Lookup("key1").get()); | 
 |   EXPECT_EQ(0u, cache.size()); | 
 |  | 
 |   EXPECT_EQ(nullptr, cache.Lookup("key2").get()); | 
 |   EXPECT_EQ(0u, cache.size()); | 
 |  | 
 |   cache.Insert("key2", session_single.get()); | 
 |   cache.Insert("key2", session_single.get()); | 
 |   EXPECT_EQ(1u, cache.size()); | 
 |  | 
 |   EXPECT_EQ(session_single.get(), cache.Lookup("key2").get()); | 
 |   EXPECT_EQ(session_single.get(), cache.Lookup("key2").get()); | 
 |   EXPECT_EQ(nullptr, cache.Lookup("key2").get()); | 
 |   EXPECT_EQ(0u, cache.size()); | 
 |  | 
 |   cache.Insert("key2", session_single.get()); | 
 |   cache.Insert("key2", session_reuse.get()); | 
 |   EXPECT_EQ(session_reuse.get(), cache.Lookup("key2").get()); | 
 |   EXPECT_EQ(session_reuse.get(), cache.Lookup("key2").get()); | 
 |   EXPECT_EQ(1u, cache.size()); | 
 | } | 
 |  | 
 | // Test that a session may be inserted at two different keys. This should never | 
 | // be necessary, but the API doesn't prohibit it. | 
 | TEST_F(SSLClientSessionCacheTest, DoubleInsert) { | 
 |   SSLClientSessionCache::Config config; | 
 |   SSLClientSessionCache cache(config); | 
 |  | 
 |   bssl::UniquePtr<SSL_SESSION> session = NewSSLSession(); | 
 |  | 
 |   EXPECT_EQ(nullptr, cache.Lookup("key1").get()); | 
 |   EXPECT_EQ(nullptr, cache.Lookup("key2").get()); | 
 |   EXPECT_EQ(0u, cache.size()); | 
 |  | 
 |   cache.Insert("key1", session.get()); | 
 |   EXPECT_EQ(session.get(), cache.Lookup("key1").get()); | 
 |   EXPECT_EQ(nullptr, cache.Lookup("key2").get()); | 
 |   EXPECT_EQ(1u, cache.size()); | 
 |  | 
 |   cache.Insert("key2", session.get()); | 
 |   EXPECT_EQ(session.get(), cache.Lookup("key1").get()); | 
 |   EXPECT_EQ(session.get(), cache.Lookup("key2").get()); | 
 |   EXPECT_EQ(2u, cache.size()); | 
 |  | 
 |   cache.Flush(); | 
 |   EXPECT_EQ(nullptr, cache.Lookup("key1").get()); | 
 |   EXPECT_EQ(nullptr, cache.Lookup("key2").get()); | 
 |   EXPECT_EQ(0u, cache.size()); | 
 | } | 
 |  | 
 | // Tests that the session cache's size is correctly bounded. | 
 | TEST_F(SSLClientSessionCacheTest, MaxEntries) { | 
 |   SSLClientSessionCache::Config config; | 
 |   config.max_entries = 3; | 
 |   SSLClientSessionCache cache(config); | 
 |  | 
 |   bssl::UniquePtr<SSL_SESSION> session1 = NewSSLSession(); | 
 |   bssl::UniquePtr<SSL_SESSION> session2 = NewSSLSession(); | 
 |   bssl::UniquePtr<SSL_SESSION> session3 = NewSSLSession(); | 
 |   bssl::UniquePtr<SSL_SESSION> session4 = NewSSLSession(); | 
 |  | 
 |   // Insert three entries. | 
 |   cache.Insert("key1", session1.get()); | 
 |   cache.Insert("key2", session2.get()); | 
 |   cache.Insert("key3", session3.get()); | 
 |   EXPECT_EQ(session1.get(), cache.Lookup("key1").get()); | 
 |   EXPECT_EQ(session2.get(), cache.Lookup("key2").get()); | 
 |   EXPECT_EQ(session3.get(), cache.Lookup("key3").get()); | 
 |   EXPECT_EQ(3u, cache.size()); | 
 |  | 
 |   // On insertion of a fourth, the first is removed. | 
 |   cache.Insert("key4", session4.get()); | 
 |   EXPECT_EQ(nullptr, cache.Lookup("key1").get()); | 
 |   EXPECT_EQ(session4.get(), cache.Lookup("key4").get()); | 
 |   EXPECT_EQ(session3.get(), cache.Lookup("key3").get()); | 
 |   EXPECT_EQ(session2.get(), cache.Lookup("key2").get()); | 
 |   EXPECT_EQ(3u, cache.size()); | 
 |  | 
 |   // Despite being newest, the next to be removed is session4 as it was accessed | 
 |   // least. recently. | 
 |   cache.Insert("key1", session1.get()); | 
 |   EXPECT_EQ(session1.get(), cache.Lookup("key1").get()); | 
 |   EXPECT_EQ(session2.get(), cache.Lookup("key2").get()); | 
 |   EXPECT_EQ(session3.get(), cache.Lookup("key3").get()); | 
 |   EXPECT_EQ(nullptr, cache.Lookup("key4").get()); | 
 |   EXPECT_EQ(3u, cache.size()); | 
 | } | 
 |  | 
 | // Tests that session expiration works properly. | 
 | TEST_F(SSLClientSessionCacheTest, Expiration) { | 
 |   const size_t kNumEntries = 20; | 
 |   const size_t kExpirationCheckCount = 10; | 
 |   const base::TimeDelta kTimeout = base::TimeDelta::FromSeconds(1000); | 
 |  | 
 |   SSLClientSessionCache::Config config; | 
 |   config.expiration_check_count = kExpirationCheckCount; | 
 |   SSLClientSessionCache cache(config); | 
 |   std::unique_ptr<base::SimpleTestClock> clock = MakeTestClock(); | 
 |   cache.SetClockForTesting(clock.get()); | 
 |  | 
 |   // Add |kNumEntries - 1| entries. | 
 |   for (size_t i = 0; i < kNumEntries - 1; i++) { | 
 |     bssl::UniquePtr<SSL_SESSION> session = | 
 |         MakeTestSession(clock->Now(), kTimeout); | 
 |     cache.Insert(base::NumberToString(i), session.get()); | 
 |   } | 
 |   EXPECT_EQ(kNumEntries - 1, cache.size()); | 
 |  | 
 |   // Expire all the previous entries and insert one more entry. | 
 |   clock->Advance(kTimeout * 2); | 
 |   bssl::UniquePtr<SSL_SESSION> session = | 
 |       MakeTestSession(clock->Now(), kTimeout); | 
 |   cache.Insert("key", session.get()); | 
 |  | 
 |   // All entries are still in the cache. | 
 |   EXPECT_EQ(kNumEntries, cache.size()); | 
 |  | 
 |   // Perform one fewer lookup than needed to trigger the expiration check. This | 
 |   // shall not expire any session. | 
 |   for (size_t i = 0; i < kExpirationCheckCount - 1; i++) | 
 |     cache.Lookup("key"); | 
 |  | 
 |   // All entries are still in the cache. | 
 |   EXPECT_EQ(kNumEntries, cache.size()); | 
 |  | 
 |   // Perform one more lookup. This will expire all sessions but the last one. | 
 |   cache.Lookup("key"); | 
 |   EXPECT_EQ(1u, cache.size()); | 
 |   EXPECT_EQ(session.get(), cache.Lookup("key").get()); | 
 |   for (size_t i = 0; i < kNumEntries - 1; i++) { | 
 |     SCOPED_TRACE(i); | 
 |     EXPECT_EQ(nullptr, cache.Lookup(base::NumberToString(i))); | 
 |   } | 
 | } | 
 |  | 
 | // Tests that Lookup performs an expiration check before returning a cached | 
 | // session. | 
 | TEST_F(SSLClientSessionCacheTest, LookupExpirationCheck) { | 
 |   // kExpirationCheckCount is set to a suitably large number so the automated | 
 |   // pruning never triggers. | 
 |   const size_t kExpirationCheckCount = 1000; | 
 |   const base::TimeDelta kTimeout = base::TimeDelta::FromSeconds(1000); | 
 |  | 
 |   SSLClientSessionCache::Config config; | 
 |   config.expiration_check_count = kExpirationCheckCount; | 
 |   SSLClientSessionCache cache(config); | 
 |   std::unique_ptr<base::SimpleTestClock> clock = MakeTestClock(); | 
 |   cache.SetClockForTesting(clock.get()); | 
 |  | 
 |   // Insert an entry into the session cache. | 
 |   bssl::UniquePtr<SSL_SESSION> session = | 
 |       MakeTestSession(clock->Now(), kTimeout); | 
 |   cache.Insert("key", session.get()); | 
 |   EXPECT_EQ(session.get(), cache.Lookup("key").get()); | 
 |   EXPECT_EQ(1u, cache.size()); | 
 |  | 
 |   // Expire the session. | 
 |   clock->Advance(kTimeout * 2); | 
 |  | 
 |   // The entry has not been removed yet. | 
 |   EXPECT_EQ(1u, cache.size()); | 
 |  | 
 |   // But it will not be returned on lookup and gets pruned at that point. | 
 |   EXPECT_EQ(nullptr, cache.Lookup("key").get()); | 
 |   EXPECT_EQ(0u, cache.size()); | 
 |  | 
 |   // Re-inserting a session does not refresh the lifetime. The expiration | 
 |   // information in the session is used. | 
 |   cache.Insert("key", session.get()); | 
 |   EXPECT_EQ(nullptr, cache.Lookup("key").get()); | 
 |   EXPECT_EQ(0u, cache.size()); | 
 |  | 
 |   // Re-insert a fresh copy of the session. | 
 |   session = MakeTestSession(clock->Now(), kTimeout); | 
 |   cache.Insert("key", session.get()); | 
 |   EXPECT_EQ(session.get(), cache.Lookup("key").get()); | 
 |   EXPECT_EQ(1u, cache.size()); | 
 |  | 
 |   // Sessions also are treated as expired if the clock rewinds. | 
 |   clock->Advance(base::TimeDelta::FromSeconds(-1)); | 
 |   EXPECT_EQ(nullptr, cache.Lookup("key").get()); | 
 |   EXPECT_EQ(0u, cache.size()); | 
 | } | 
 |  | 
 | // Test that SSL cache is flushed on low memory notifications | 
 | TEST_F(SSLClientSessionCacheTest, TestFlushOnMemoryNotifications) { | 
 |   base::test::ScopedTaskEnvironment scoped_task_environment; | 
 |  | 
 |   // kExpirationCheckCount is set to a suitably large number so the automated | 
 |   // pruning never triggers. | 
 |   const size_t kExpirationCheckCount = 1000; | 
 |   const base::TimeDelta kTimeout = base::TimeDelta::FromSeconds(1000); | 
 |  | 
 |   SSLClientSessionCache::Config config; | 
 |   config.expiration_check_count = kExpirationCheckCount; | 
 |   SSLClientSessionCache cache(config); | 
 |   std::unique_ptr<base::SimpleTestClock> clock = MakeTestClock(); | 
 |   cache.SetClockForTesting(clock.get()); | 
 |  | 
 |   // Insert an entry into the session cache. | 
 |   bssl::UniquePtr<SSL_SESSION> session1 = | 
 |       MakeTestSession(clock->Now(), kTimeout); | 
 |   cache.Insert("key1", session1.get()); | 
 |   EXPECT_EQ(session1.get(), cache.Lookup("key1").get()); | 
 |   EXPECT_EQ(1u, cache.size()); | 
 |  | 
 |   // Expire the session. | 
 |   clock->Advance(kTimeout * 2); | 
 |   // Add one more session. | 
 |   bssl::UniquePtr<SSL_SESSION> session2 = | 
 |       MakeTestSession(clock->Now(), kTimeout); | 
 |   cache.Insert("key2", session2.get()); | 
 |   EXPECT_EQ(2u, cache.size()); | 
 |  | 
 |   // Fire a notification that will flush expired sessions. | 
 |   base::MemoryPressureListener::NotifyMemoryPressure( | 
 |       base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE); | 
 |   base::RunLoop().RunUntilIdle(); | 
 |  | 
 |   // Expired session's cache should be flushed. | 
 |   // Lookup returns nullptr, when cache entry not found. | 
 |   EXPECT_FALSE(cache.Lookup("key1")); | 
 |   EXPECT_TRUE(cache.Lookup("key2")); | 
 |   EXPECT_EQ(1u, cache.size()); | 
 |  | 
 |   // Fire notification that will flush everything. | 
 |   base::MemoryPressureListener::NotifyMemoryPressure( | 
 |       base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL); | 
 |   base::RunLoop().RunUntilIdle(); | 
 |   EXPECT_EQ(0u, cache.size()); | 
 | } | 
 |  | 
 | class SSLClientSessionCacheMemoryDumpTest | 
 |     : public SSLClientSessionCacheTest, | 
 |       public testing::WithParamInterface< | 
 |           base::trace_event::MemoryDumpLevelOfDetail> {}; | 
 |  | 
 | INSTANTIATE_TEST_CASE_P( | 
 |     /* no prefix */, | 
 |     SSLClientSessionCacheMemoryDumpTest, | 
 |     ::testing::Values(base::trace_event::MemoryDumpLevelOfDetail::DETAILED, | 
 |                       base::trace_event::MemoryDumpLevelOfDetail::BACKGROUND)); | 
 |  | 
 | // Basic test for dumping memory stats. | 
 | TEST_P(SSLClientSessionCacheMemoryDumpTest, TestDumpMemoryStats) { | 
 |   SSLClientSessionCache::Config config; | 
 |   SSLClientSessionCache cache(config); | 
 |  | 
 |   bssl::UniquePtr<SSL_SESSION> session1 = NewSSLSession(); | 
 |   bssl::UniquePtr<SSL_SESSION> session2 = NewSSLSession(); | 
 |   bssl::UniquePtr<SSL_SESSION> session3 = NewSSLSession(); | 
 |  | 
 |   // Insert three entries. | 
 |   cache.Insert("key1", session1.get()); | 
 |   cache.Insert("key2", session2.get()); | 
 |   cache.Insert("key3", session3.get()); | 
 |   EXPECT_EQ(session1.get(), cache.Lookup("key1").get()); | 
 |   EXPECT_EQ(session2.get(), cache.Lookup("key2").get()); | 
 |   EXPECT_EQ(session3.get(), cache.Lookup("key3").get()); | 
 |   EXPECT_EQ(3u, cache.size()); | 
 |  | 
 |   base::trace_event::MemoryDumpArgs dump_args = {GetParam()}; | 
 |   std::unique_ptr<base::trace_event::ProcessMemoryDump> process_memory_dump( | 
 |       new base::trace_event::ProcessMemoryDump(dump_args)); | 
 |   cache.DumpMemoryStats(process_memory_dump.get()); | 
 |  | 
 |   using Entry = base::trace_event::MemoryAllocatorDump::Entry; | 
 |   const base::trace_event::MemoryAllocatorDump* dump = | 
 |       process_memory_dump->GetAllocatorDump("net/ssl_session_cache"); | 
 |   ASSERT_NE(nullptr, dump); | 
 |   const std::vector<Entry>& entries = dump->entries(); | 
 |   EXPECT_THAT(entries, Contains(Field(&Entry::name, Eq("cert_count")))); | 
 |   EXPECT_THAT(entries, Contains(Field(&Entry::name, Eq("cert_size")))); | 
 |   EXPECT_THAT(entries, | 
 |               Contains(Field(&Entry::name, Eq("undeduped_cert_size")))); | 
 |   EXPECT_THAT(entries, | 
 |               Contains(Field(&Entry::name, Eq("undeduped_cert_count")))); | 
 |   EXPECT_THAT( | 
 |       entries, | 
 |       Contains(Field(&Entry::name, | 
 |                      Eq(base::trace_event::MemoryAllocatorDump::kNameSize)))); | 
 | } | 
 |  | 
 | }  // namespace net |