| // Copyright 2013 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/bind.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/memory/scoped_ptr.h" |
| #include "base/test/sequenced_worker_pool_owner.h" |
| #include "content/browser/media/webrtc_identity_store.h" |
| #include "content/public/test/test_browser_thread_bundle.h" |
| #include "content/public/test/test_utils.h" |
| #include "net/base/net_errors.h" |
| #include "sql/connection.h" |
| #include "sql/test/test_helpers.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "url/gurl.h" |
| |
| namespace content { |
| |
| static const char kFakeOrigin[] = "http://foo.com"; |
| static const char kFakeIdentityName1[] = "name1"; |
| static const char kFakeIdentityName2[] = "name2"; |
| static const char kFakeCommonName1[] = "cname1"; |
| static const char kFakeCommonName2[] = "cname2"; |
| |
| static void OnRequestCompleted(bool* completed, |
| std::string* out_cert, |
| std::string* out_key, |
| int error, |
| const std::string& certificate, |
| const std::string& private_key) { |
| ASSERT_EQ(net::OK, error); |
| ASSERT_NE("", certificate); |
| ASSERT_NE("", private_key); |
| *completed = true; |
| *out_cert = certificate; |
| *out_key = private_key; |
| } |
| |
| class WebRtcIdentityStoreTest : public testing::Test { |
| public: |
| WebRtcIdentityStoreTest() |
| : browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP | |
| TestBrowserThreadBundle::REAL_DB_THREAD), |
| pool_owner_( |
| new base::SequencedWorkerPoolOwner(3, "WebRtcIdentityStoreTest")), |
| webrtc_identity_store_( |
| new WebRTCIdentityStore(base::FilePath(), NULL)) { |
| webrtc_identity_store_->SetTaskRunnerForTesting(pool_owner_->pool()); |
| } |
| |
| void SetValidityPeriod(base::TimeDelta validity_period) { |
| webrtc_identity_store_->SetValidityPeriodForTesting(validity_period); |
| } |
| |
| void RunUntilIdle() { |
| RunAllPendingInMessageLoop(BrowserThread::DB); |
| RunAllPendingInMessageLoop(BrowserThread::IO); |
| pool_owner_->pool()->FlushForTesting(); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| base::Closure RequestIdentityAndRunUtilIdle(const std::string& origin, |
| const std::string& identity_name, |
| const std::string& common_name, |
| bool* completed, |
| std::string* certificate, |
| std::string* private_key) { |
| base::Closure cancel_callback = webrtc_identity_store_->RequestIdentity( |
| GURL(origin), identity_name, common_name, |
| base::Bind(&OnRequestCompleted, completed, certificate, private_key), |
| true /* enable_cache */); |
| EXPECT_FALSE(cancel_callback.is_null()); |
| RunUntilIdle(); |
| return cancel_callback; |
| } |
| |
| void Restart(const base::FilePath& path) { |
| webrtc_identity_store_ = new WebRTCIdentityStore(path, NULL); |
| webrtc_identity_store_->SetTaskRunnerForTesting(pool_owner_->pool()); |
| } |
| |
| void Stop() { webrtc_identity_store_ = nullptr; } |
| |
| protected: |
| TestBrowserThreadBundle browser_thread_bundle_; |
| scoped_ptr<base::SequencedWorkerPoolOwner> pool_owner_; |
| scoped_refptr<WebRTCIdentityStore> webrtc_identity_store_; |
| }; |
| |
| TEST_F(WebRtcIdentityStoreTest, RequestIdentity) { |
| bool completed = false; |
| std::string dummy; |
| base::Closure cancel_callback = |
| RequestIdentityAndRunUtilIdle(kFakeOrigin, |
| kFakeIdentityName1, |
| kFakeCommonName1, |
| &completed, |
| &dummy, |
| &dummy); |
| EXPECT_TRUE(completed); |
| } |
| |
| TEST_F(WebRtcIdentityStoreTest, CancelRequest) { |
| bool completed = false; |
| std::string dummy; |
| base::Closure cancel_callback = webrtc_identity_store_->RequestIdentity( |
| GURL(kFakeOrigin), kFakeIdentityName1, kFakeCommonName1, |
| base::Bind(&OnRequestCompleted, &completed, &dummy, &dummy), |
| true /* enable_cache */); |
| ASSERT_FALSE(cancel_callback.is_null()); |
| cancel_callback.Run(); |
| |
| RunUntilIdle(); |
| EXPECT_FALSE(completed); |
| } |
| |
| TEST_F(WebRtcIdentityStoreTest, ConcurrentUniqueRequests) { |
| bool completed_1 = false; |
| bool completed_2 = false; |
| std::string dummy; |
| base::Closure cancel_callback_1 = webrtc_identity_store_->RequestIdentity( |
| GURL(kFakeOrigin), kFakeIdentityName1, kFakeCommonName1, |
| base::Bind(&OnRequestCompleted, &completed_1, &dummy, &dummy), |
| true /* enable_cache */); |
| ASSERT_FALSE(cancel_callback_1.is_null()); |
| |
| base::Closure cancel_callback_2 = webrtc_identity_store_->RequestIdentity( |
| GURL(kFakeOrigin), kFakeIdentityName2, kFakeCommonName1, |
| base::Bind(&OnRequestCompleted, &completed_2, &dummy, &dummy), |
| true /* enable_cache */); |
| ASSERT_FALSE(cancel_callback_2.is_null()); |
| |
| RunUntilIdle(); |
| EXPECT_TRUE(completed_1); |
| EXPECT_TRUE(completed_2); |
| } |
| |
| TEST_F(WebRtcIdentityStoreTest, DifferentCommonNameReturnNewIdentity) { |
| bool completed_1 = false; |
| bool completed_2 = false; |
| std::string cert_1, cert_2, key_1, key_2; |
| |
| base::Closure cancel_callback_1 = |
| RequestIdentityAndRunUtilIdle(kFakeOrigin, |
| kFakeIdentityName1, |
| kFakeCommonName1, |
| &completed_1, |
| &cert_1, |
| &key_1); |
| |
| base::Closure cancel_callback_2 = |
| RequestIdentityAndRunUtilIdle(kFakeOrigin, |
| kFakeIdentityName1, |
| kFakeCommonName2, |
| &completed_2, |
| &cert_2, |
| &key_2); |
| |
| EXPECT_TRUE(completed_1); |
| EXPECT_TRUE(completed_2); |
| EXPECT_NE(cert_1, cert_2); |
| EXPECT_NE(key_1, key_2); |
| } |
| |
| TEST_F(WebRtcIdentityStoreTest, DisableCacheReturnNewIdentity) { |
| bool completed_1 = false; |
| bool completed_2 = false; |
| std::string cert_1, cert_2, key_1, key_2; |
| |
| base::Closure cancel_callback_1 = RequestIdentityAndRunUtilIdle( |
| kFakeOrigin, kFakeIdentityName1, kFakeCommonName1, &completed_1, &cert_1, |
| &key_1); |
| |
| base::Closure cancel_callback_2 = webrtc_identity_store_->RequestIdentity( |
| GURL(kFakeOrigin), kFakeIdentityName1, kFakeCommonName1, |
| base::Bind(&OnRequestCompleted, &completed_2, &cert_2, &key_2), |
| false /* enable_cache */); |
| EXPECT_FALSE(cancel_callback_2.is_null()); |
| RunUntilIdle(); |
| |
| EXPECT_TRUE(completed_1); |
| EXPECT_TRUE(completed_2); |
| EXPECT_NE(cert_1, cert_2); |
| EXPECT_NE(key_1, key_2); |
| } |
| |
| TEST_F(WebRtcIdentityStoreTest, SerialIdenticalRequests) { |
| bool completed_1 = false; |
| bool completed_2 = false; |
| std::string cert_1, cert_2, key_1, key_2; |
| |
| base::Closure cancel_callback_1 = |
| RequestIdentityAndRunUtilIdle(kFakeOrigin, |
| kFakeIdentityName1, |
| kFakeCommonName1, |
| &completed_1, |
| &cert_1, |
| &key_1); |
| |
| base::Closure cancel_callback_2 = |
| RequestIdentityAndRunUtilIdle(kFakeOrigin, |
| kFakeIdentityName1, |
| kFakeCommonName1, |
| &completed_2, |
| &cert_2, |
| &key_2); |
| |
| EXPECT_TRUE(completed_1); |
| EXPECT_TRUE(completed_2); |
| EXPECT_EQ(cert_1, cert_2); |
| EXPECT_EQ(key_1, key_2); |
| } |
| |
| TEST_F(WebRtcIdentityStoreTest, ConcurrentIdenticalRequestsJoined) { |
| bool completed_1 = false; |
| bool completed_2 = false; |
| std::string cert_1, cert_2, key_1, key_2; |
| |
| base::Closure cancel_callback_1 = webrtc_identity_store_->RequestIdentity( |
| GURL(kFakeOrigin), kFakeIdentityName1, kFakeCommonName1, |
| base::Bind(&OnRequestCompleted, &completed_1, &cert_1, &key_1), |
| true /* enable_cache */); |
| ASSERT_FALSE(cancel_callback_1.is_null()); |
| |
| base::Closure cancel_callback_2 = webrtc_identity_store_->RequestIdentity( |
| GURL(kFakeOrigin), kFakeIdentityName1, kFakeCommonName1, |
| base::Bind(&OnRequestCompleted, &completed_2, &cert_2, &key_2), |
| true /* enable_cache */); |
| ASSERT_FALSE(cancel_callback_2.is_null()); |
| |
| RunUntilIdle(); |
| EXPECT_TRUE(completed_1); |
| EXPECT_TRUE(completed_2); |
| EXPECT_EQ(cert_1, cert_2); |
| EXPECT_EQ(key_1, key_2); |
| } |
| |
| TEST_F(WebRtcIdentityStoreTest, CancelOneOfIdenticalRequests) { |
| bool completed_1 = false; |
| bool completed_2 = false; |
| std::string cert_1, cert_2, key_1, key_2; |
| |
| base::Closure cancel_callback_1 = webrtc_identity_store_->RequestIdentity( |
| GURL(kFakeOrigin), kFakeIdentityName1, kFakeCommonName1, |
| base::Bind(&OnRequestCompleted, &completed_1, &cert_1, &key_1), |
| true /* enable_cache */); |
| ASSERT_FALSE(cancel_callback_1.is_null()); |
| |
| base::Closure cancel_callback_2 = webrtc_identity_store_->RequestIdentity( |
| GURL(kFakeOrigin), kFakeIdentityName1, kFakeCommonName1, |
| base::Bind(&OnRequestCompleted, &completed_2, &cert_2, &key_2), |
| true /* enable_cache */); |
| ASSERT_FALSE(cancel_callback_2.is_null()); |
| |
| cancel_callback_1.Run(); |
| |
| RunUntilIdle(); |
| EXPECT_FALSE(completed_1); |
| EXPECT_TRUE(completed_2); |
| } |
| |
| TEST_F(WebRtcIdentityStoreTest, DeleteDataAndGenerateNewIdentity) { |
| bool completed_1 = false; |
| bool completed_2 = false; |
| std::string cert_1, cert_2, key_1, key_2; |
| |
| // Generate the first identity. |
| base::Closure cancel_callback_1 = |
| RequestIdentityAndRunUtilIdle(kFakeOrigin, |
| kFakeIdentityName1, |
| kFakeCommonName1, |
| &completed_1, |
| &cert_1, |
| &key_1); |
| |
| // Clear the data and the second request should return a new identity. |
| webrtc_identity_store_->DeleteBetween( |
| base::Time(), base::Time::Now(), base::Bind(&base::DoNothing)); |
| RunUntilIdle(); |
| |
| base::Closure cancel_callback_2 = |
| RequestIdentityAndRunUtilIdle(kFakeOrigin, |
| kFakeIdentityName1, |
| kFakeCommonName1, |
| &completed_2, |
| &cert_2, |
| &key_2); |
| |
| EXPECT_TRUE(completed_1); |
| EXPECT_TRUE(completed_2); |
| EXPECT_NE(cert_1, cert_2); |
| EXPECT_NE(key_1, key_2); |
| } |
| |
| TEST_F(WebRtcIdentityStoreTest, ExpiredIdentityDeleted) { |
| // The identities will expire immediately after creation. |
| SetValidityPeriod(base::TimeDelta::FromMilliseconds(0)); |
| |
| bool completed_1 = false; |
| bool completed_2 = false; |
| std::string cert_1, cert_2, key_1, key_2; |
| |
| base::Closure cancel_callback_1 = |
| RequestIdentityAndRunUtilIdle(kFakeOrigin, |
| kFakeIdentityName1, |
| kFakeCommonName1, |
| &completed_1, |
| &cert_1, |
| &key_1); |
| EXPECT_TRUE(completed_1); |
| |
| // Check that the old identity is not returned. |
| base::Closure cancel_callback_2 = |
| RequestIdentityAndRunUtilIdle(kFakeOrigin, |
| kFakeIdentityName1, |
| kFakeCommonName1, |
| &completed_2, |
| &cert_2, |
| &key_2); |
| EXPECT_TRUE(completed_2); |
| EXPECT_NE(cert_1, cert_2); |
| EXPECT_NE(key_1, key_2); |
| } |
| |
| TEST_F(WebRtcIdentityStoreTest, IdentityPersistentAcrossRestart) { |
| base::ScopedTempDir temp_dir; |
| ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
| Restart(temp_dir.path()); |
| |
| bool completed_1 = false; |
| bool completed_2 = false; |
| std::string cert_1, cert_2, key_1, key_2; |
| |
| // Creates an identity. |
| base::Closure cancel_callback_1 = |
| RequestIdentityAndRunUtilIdle(kFakeOrigin, |
| kFakeIdentityName1, |
| kFakeCommonName1, |
| &completed_1, |
| &cert_1, |
| &key_1); |
| EXPECT_TRUE(completed_1); |
| |
| Restart(temp_dir.path()); |
| |
| // Check that the same identity is returned after the restart. |
| base::Closure cancel_callback_2 = |
| RequestIdentityAndRunUtilIdle(kFakeOrigin, |
| kFakeIdentityName1, |
| kFakeCommonName1, |
| &completed_2, |
| &cert_2, |
| &key_2); |
| EXPECT_TRUE(completed_2); |
| EXPECT_EQ(cert_1, cert_2); |
| EXPECT_EQ(key_1, key_2); |
| Stop(); |
| } |
| |
| TEST_F(WebRtcIdentityStoreTest, HandleDBErrors) { |
| base::ScopedTempDir temp_dir; |
| ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
| Restart(temp_dir.path()); |
| |
| bool completed_1 = false; |
| std::string cert_1, key_1; |
| |
| // Creates an identity. |
| RequestIdentityAndRunUtilIdle(kFakeOrigin, |
| kFakeIdentityName1, |
| kFakeCommonName1, |
| &completed_1, |
| &cert_1, |
| &key_1); |
| |
| // Make the table corrupted. |
| base::FilePath db_path = |
| temp_dir.path().Append(FILE_PATH_LITERAL("WebRTCIdentityStore")); |
| EXPECT_TRUE(sql::test::CorruptSizeInHeader(db_path)); |
| |
| // Reset to commit the DB changes, which should fail and not crash. |
| webrtc_identity_store_ = NULL; |
| RunUntilIdle(); |
| |
| // Verifies the corrupted table was razed. |
| scoped_ptr<sql::Connection> db(new sql::Connection()); |
| EXPECT_TRUE(db->Open(db_path)); |
| EXPECT_EQ(0U, sql::test::CountSQLTables(db.get())); |
| |
| Stop(); |
| } |
| |
| } // namespace content |