| // 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 "components/gcm_driver/instance_id/instance_id_driver.h" |
| |
| #include <stddef.h> |
| |
| #include <cmath> |
| |
| #include "base/bind.h" |
| #include "base/macros.h" |
| #include "base/run_loop.h" |
| #include "base/strings/string_util.h" |
| #include "base/test/scoped_task_environment.h" |
| #include "components/gcm_driver/gcm_buildflags.h" |
| #include "components/gcm_driver/instance_id/fake_gcm_driver_for_instance_id.h" |
| #include "components/gcm_driver/instance_id/instance_id.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| #if BUILDFLAG(USE_GCM_FROM_PLATFORM) |
| #include "components/gcm_driver/instance_id/instance_id_android.h" |
| #include "components/gcm_driver/instance_id/scoped_use_fake_instance_id_android.h" |
| #endif // BUILDFLAG(USE_GCM_FROM_PLATFORM) |
| |
| namespace instance_id { |
| |
| namespace { |
| |
| const char kTestAppID1[] = "TestApp1"; |
| const char kTestAppID2[] = "TestApp2"; |
| const char kAuthorizedEntity1[] = "Sender 1"; |
| const char kAuthorizedEntity2[] = "Sender 2"; |
| const char kScope1[] = "GCM1"; |
| const char kScope2[] = "FooBar"; |
| |
| bool VerifyInstanceID(const std::string& str) { |
| // Checks the length. |
| if (str.length() != static_cast<size_t>( |
| std::ceil(InstanceID::kInstanceIDByteLength * 8 / 6.0))) |
| return false; |
| |
| // Checks if it is URL-safe base64 encoded. |
| for (auto ch : str) { |
| if (!base::IsAsciiAlpha(ch) && !base::IsAsciiDigit(ch) && |
| ch != '_' && ch != '-') |
| return false; |
| } |
| return true; |
| } |
| |
| } // namespace |
| |
| class InstanceIDDriverTest : public testing::Test { |
| public: |
| InstanceIDDriverTest(); |
| ~InstanceIDDriverTest() override; |
| |
| // testing::Test: |
| void SetUp() override; |
| void TearDown() override; |
| |
| void WaitForAsyncOperation(); |
| |
| // Recreates InstanceIDDriver to simulate restart. |
| void RecreateInstanceIDDriver(); |
| |
| // Sync wrappers for async version. |
| std::string GetID(InstanceID* instance_id); |
| base::Time GetCreationTime(InstanceID* instance_id); |
| InstanceID::Result DeleteID(InstanceID* instance_id); |
| std::string GetToken( |
| InstanceID* instance_id, |
| const std::string& authorized_entity, |
| const std::string& scope, |
| const std::map<std::string, std::string>& options); |
| InstanceID::Result DeleteToken( |
| InstanceID* instance_id, |
| const std::string& authorized_entity, |
| const std::string& scope); |
| |
| InstanceIDDriver* driver() const { return driver_.get(); } |
| |
| private: |
| void GetIDCompleted(const std::string& id); |
| void GetCreationTimeCompleted(const base::Time& creation_time); |
| void DeleteIDCompleted(InstanceID::Result result); |
| void GetTokenCompleted(const std::string& token, InstanceID::Result result); |
| void DeleteTokenCompleted(InstanceID::Result result); |
| |
| base::test::ScopedTaskEnvironment scoped_task_environment_; |
| std::unique_ptr<FakeGCMDriverForInstanceID> gcm_driver_; |
| std::unique_ptr<InstanceIDDriver> driver_; |
| |
| #if BUILDFLAG(USE_GCM_FROM_PLATFORM) |
| InstanceIDAndroid::ScopedBlockOnAsyncTasksForTesting block_async_; |
| ScopedUseFakeInstanceIDAndroid use_fake_; |
| #endif // BUILDFLAG(USE_GCM_FROM_PLATFORM) |
| |
| std::string id_; |
| base::Time creation_time_; |
| std::string token_; |
| InstanceID::Result result_; |
| |
| bool async_operation_completed_; |
| base::Closure async_operation_completed_callback_; |
| |
| DISALLOW_COPY_AND_ASSIGN(InstanceIDDriverTest); |
| }; |
| |
| InstanceIDDriverTest::InstanceIDDriverTest() |
| : scoped_task_environment_( |
| base::test::ScopedTaskEnvironment::MainThreadType::UI), |
| result_(InstanceID::UNKNOWN_ERROR), |
| async_operation_completed_(false) {} |
| |
| InstanceIDDriverTest::~InstanceIDDriverTest() { |
| } |
| |
| void InstanceIDDriverTest::SetUp() { |
| gcm_driver_.reset(new FakeGCMDriverForInstanceID); |
| RecreateInstanceIDDriver(); |
| } |
| |
| void InstanceIDDriverTest::TearDown() { |
| driver_.reset(); |
| gcm_driver_.reset(); |
| // |gcm_driver_| owns a GCMKeyStore that owns a ProtoDatabase whose |
| // destructor deletes the underlying LevelDB on the task runner. |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| void InstanceIDDriverTest::RecreateInstanceIDDriver() { |
| driver_.reset(new InstanceIDDriver(gcm_driver_.get())); |
| } |
| |
| void InstanceIDDriverTest::WaitForAsyncOperation() { |
| // No need to wait if async operation is not needed. |
| if (async_operation_completed_) |
| return; |
| base::RunLoop run_loop; |
| async_operation_completed_callback_ = run_loop.QuitClosure(); |
| run_loop.Run(); |
| } |
| |
| std::string InstanceIDDriverTest::GetID(InstanceID* instance_id) { |
| async_operation_completed_ = false; |
| id_.clear(); |
| instance_id->GetID(base::Bind(&InstanceIDDriverTest::GetIDCompleted, |
| base::Unretained(this))); |
| WaitForAsyncOperation(); |
| return id_; |
| } |
| |
| base::Time InstanceIDDriverTest::GetCreationTime(InstanceID* instance_id) { |
| async_operation_completed_ = false; |
| creation_time_ = base::Time(); |
| instance_id->GetCreationTime( |
| base::Bind(&InstanceIDDriverTest::GetCreationTimeCompleted, |
| base::Unretained(this))); |
| WaitForAsyncOperation(); |
| return creation_time_; |
| } |
| |
| InstanceID::Result InstanceIDDriverTest::DeleteID(InstanceID* instance_id) { |
| async_operation_completed_ = false; |
| result_ = InstanceID::UNKNOWN_ERROR; |
| instance_id->DeleteID(base::Bind(&InstanceIDDriverTest::DeleteIDCompleted, |
| base::Unretained(this))); |
| WaitForAsyncOperation(); |
| return result_; |
| } |
| |
| std::string InstanceIDDriverTest::GetToken( |
| InstanceID* instance_id, |
| const std::string& authorized_entity, |
| const std::string& scope, |
| const std::map<std::string, std::string>& options) { |
| async_operation_completed_ = false; |
| token_.clear(); |
| result_ = InstanceID::UNKNOWN_ERROR; |
| instance_id->GetToken( |
| authorized_entity, scope, options, |
| /*is_lazy=*/false, |
| base::BindRepeating(&InstanceIDDriverTest::GetTokenCompleted, |
| base::Unretained(this))); |
| WaitForAsyncOperation(); |
| return token_; |
| } |
| |
| InstanceID::Result InstanceIDDriverTest::DeleteToken( |
| InstanceID* instance_id, |
| const std::string& authorized_entity, |
| const std::string& scope) { |
| async_operation_completed_ = false; |
| result_ = InstanceID::UNKNOWN_ERROR; |
| instance_id->DeleteToken( |
| authorized_entity, |
| scope, |
| base::Bind(&InstanceIDDriverTest::DeleteTokenCompleted, |
| base::Unretained(this))); |
| WaitForAsyncOperation(); |
| return result_; |
| } |
| |
| void InstanceIDDriverTest::GetIDCompleted(const std::string& id) { |
| DCHECK(!async_operation_completed_); |
| async_operation_completed_ = true; |
| id_ = id; |
| if (!async_operation_completed_callback_.is_null()) |
| async_operation_completed_callback_.Run(); |
| } |
| |
| void InstanceIDDriverTest::GetCreationTimeCompleted( |
| const base::Time& creation_time) { |
| DCHECK(!async_operation_completed_); |
| async_operation_completed_ = true; |
| creation_time_ = creation_time; |
| if (!async_operation_completed_callback_.is_null()) |
| async_operation_completed_callback_.Run(); |
| } |
| |
| void InstanceIDDriverTest::DeleteIDCompleted(InstanceID::Result result) { |
| DCHECK(!async_operation_completed_); |
| async_operation_completed_ = true; |
| result_ = result; |
| if (!async_operation_completed_callback_.is_null()) |
| async_operation_completed_callback_.Run(); |
| } |
| |
| void InstanceIDDriverTest::GetTokenCompleted( |
| const std::string& token, InstanceID::Result result) { |
| DCHECK(!async_operation_completed_); |
| async_operation_completed_ = true; |
| token_ = token; |
| result_ = result; |
| if (!async_operation_completed_callback_.is_null()) |
| async_operation_completed_callback_.Run(); |
| } |
| |
| void InstanceIDDriverTest::DeleteTokenCompleted(InstanceID::Result result) { |
| DCHECK(!async_operation_completed_); |
| async_operation_completed_ = true; |
| result_ = result; |
| if (!async_operation_completed_callback_.is_null()) |
| async_operation_completed_callback_.Run(); |
| } |
| |
| TEST_F(InstanceIDDriverTest, GetAndRemoveInstanceID) { |
| EXPECT_FALSE(driver()->ExistsInstanceID(kTestAppID1)); |
| |
| InstanceID* instance_id = driver()->GetInstanceID(kTestAppID1); |
| EXPECT_TRUE(instance_id); |
| EXPECT_TRUE(driver()->ExistsInstanceID(kTestAppID1)); |
| |
| driver()->RemoveInstanceID(kTestAppID1); |
| EXPECT_FALSE(driver()->ExistsInstanceID(kTestAppID1)); |
| } |
| |
| TEST_F(InstanceIDDriverTest, NewID) { |
| // Creation time should not be set when the ID is not created. |
| InstanceID* instance_id1 = driver()->GetInstanceID(kTestAppID1); |
| EXPECT_TRUE(GetCreationTime(instance_id1).is_null()); |
| |
| // New ID is generated for the first time. |
| std::string id1 = GetID(instance_id1); |
| EXPECT_TRUE(VerifyInstanceID(id1)); |
| base::Time creation_time = GetCreationTime(instance_id1); |
| EXPECT_FALSE(creation_time.is_null()); |
| |
| // Same ID is returned for the same app. |
| EXPECT_EQ(id1, GetID(instance_id1)); |
| EXPECT_EQ(creation_time, GetCreationTime(instance_id1)); |
| |
| // New ID is generated for another app. |
| InstanceID* instance_id2 = driver()->GetInstanceID(kTestAppID2); |
| std::string id2 = GetID(instance_id2); |
| EXPECT_TRUE(VerifyInstanceID(id2)); |
| EXPECT_NE(id1, id2); |
| EXPECT_FALSE(GetCreationTime(instance_id2).is_null()); |
| } |
| |
| TEST_F(InstanceIDDriverTest, PersistID) { |
| InstanceID* instance_id = driver()->GetInstanceID(kTestAppID1); |
| |
| // Create the ID for the first time. The ID and creation time should be saved |
| // to the store. |
| std::string id = GetID(instance_id); |
| EXPECT_FALSE(id.empty()); |
| base::Time creation_time = GetCreationTime(instance_id); |
| EXPECT_FALSE(creation_time.is_null()); |
| |
| // Simulate restart by recreating InstanceIDDriver. Same ID and creation time |
| // should be expected. |
| RecreateInstanceIDDriver(); |
| instance_id = driver()->GetInstanceID(kTestAppID1); |
| EXPECT_EQ(creation_time, GetCreationTime(instance_id)); |
| EXPECT_EQ(id, GetID(instance_id)); |
| |
| // Delete the ID. The ID and creation time should be removed from the store. |
| EXPECT_EQ(InstanceID::SUCCESS, DeleteID(instance_id)); |
| EXPECT_TRUE(GetCreationTime(instance_id).is_null()); |
| |
| // Simulate restart by recreating InstanceIDDriver. Different ID should be |
| // expected. |
| // Note that we do not check for different creation time since the test might |
| // be run at a very fast server. |
| RecreateInstanceIDDriver(); |
| instance_id = driver()->GetInstanceID(kTestAppID1); |
| EXPECT_NE(id, GetID(instance_id)); |
| } |
| |
| TEST_F(InstanceIDDriverTest, DeleteID) { |
| InstanceID* instance_id = driver()->GetInstanceID(kTestAppID1); |
| std::string id1 = GetID(instance_id); |
| EXPECT_FALSE(id1.empty()); |
| EXPECT_FALSE(GetCreationTime(instance_id).is_null()); |
| |
| // New ID will be generated from GetID after calling DeleteID. |
| EXPECT_EQ(InstanceID::SUCCESS, DeleteID(instance_id)); |
| EXPECT_TRUE(GetCreationTime(instance_id).is_null()); |
| |
| std::string id2 = GetID(instance_id); |
| EXPECT_FALSE(id2.empty()); |
| EXPECT_NE(id1, id2); |
| EXPECT_FALSE(GetCreationTime(instance_id).is_null()); |
| } |
| |
| TEST_F(InstanceIDDriverTest, GetToken) { |
| InstanceID* instance_id = driver()->GetInstanceID(kTestAppID1); |
| std::map<std::string, std::string> options; |
| std::string token1 = |
| GetToken(instance_id, kAuthorizedEntity1, kScope1, options); |
| EXPECT_FALSE(token1.empty()); |
| |
| // Same token is returned for same authorized entity and scope. |
| EXPECT_EQ(token1, |
| GetToken(instance_id, kAuthorizedEntity1, kScope1, options)); |
| |
| // Different token is returned for different authorized entity or scope. |
| std::string token2 = |
| GetToken(instance_id, kAuthorizedEntity1, kScope2, options); |
| EXPECT_FALSE(token2.empty()); |
| EXPECT_NE(token1, token2); |
| |
| std::string token3 = |
| GetToken(instance_id, kAuthorizedEntity2, kScope1, options); |
| EXPECT_FALSE(token3.empty()); |
| EXPECT_NE(token1, token3); |
| EXPECT_NE(token2, token3); |
| } |
| |
| TEST_F(InstanceIDDriverTest, DeleteToken) { |
| InstanceID* instance_id = driver()->GetInstanceID(kTestAppID1); |
| std::map<std::string, std::string> options; |
| |
| // Gets 2 tokens. |
| std::string token1 = |
| GetToken(instance_id, kAuthorizedEntity1, kScope1, options); |
| EXPECT_FALSE(token1.empty()); |
| std::string token2 = |
| GetToken(instance_id, kAuthorizedEntity2, kScope1, options); |
| EXPECT_FALSE(token1.empty()); |
| EXPECT_NE(token1, token2); |
| |
| // Different token is returned for same authorized entity and scope after |
| // deletion. |
| EXPECT_EQ(InstanceID::SUCCESS, |
| DeleteToken(instance_id, kAuthorizedEntity1, kScope1)); |
| std::string new_token1 = |
| GetToken(instance_id, kAuthorizedEntity1, kScope2, options); |
| EXPECT_FALSE(new_token1.empty()); |
| EXPECT_NE(token1, new_token1); |
| |
| // The other token is not affected by the deletion. |
| EXPECT_EQ(token2, |
| GetToken(instance_id, kAuthorizedEntity2, kScope1, options)); |
| } |
| |
| } // namespace instance_id |