|  | // 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 |