Chromium Code Reviews| Index: chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api_unittest.cc |
| diff --git a/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api_unittest.cc b/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api_unittest.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..6abdf47f213abad56c965ebe14f3afc130f47be1 |
| --- /dev/null |
| +++ b/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api_unittest.cc |
| @@ -0,0 +1,297 @@ |
| +// 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. |
| + |
| +// This file tests the chromeos.quickUnlockPrivate extension API. |
| + |
| +#include "chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api.h" |
| + |
| +#include "base/bind.h" |
| + |
| +#include "base/memory/ptr_util.h" |
| +#include "chrome/browser/chromeos/login/quick_unlock/pin_storage.h" |
| +#include "chrome/browser/chromeos/login/quick_unlock/pin_storage_factory.h" |
| +#include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h" |
| +#include "chrome/browser/chromeos/login/users/scoped_user_manager_enabler.h" |
| +#include "chrome/browser/extensions/extension_api_unittest.h" |
| +#include "chromeos/login/auth/fake_extended_authenticator.h" |
| +#include "extensions/browser/api_test_utils.h" |
| +#include "extensions/browser/extension_function_dispatcher.h" |
| + |
| +using namespace extensions; |
| +namespace quick_unlock_private = extensions::api::quick_unlock_private; |
| +using QuickUnlockMode = quick_unlock_private::QuickUnlockMode; |
| +using ModeList = std::vector<QuickUnlockMode>; |
| + |
| +namespace chromeos { |
| +namespace { |
| + |
| +const char* kTestUserEmail = "testuser@gmail.com"; |
| +const char* kTestUserEmailHash = "testuser@gmail.com-hash"; |
| +const char* kValidPassword = "valid"; |
| +const char* kInvalidPassword = "invalid"; |
| + |
| +chromeos::ExtendedAuthenticator* CreateFakeAuthenticator( |
| + chromeos::AuthStatusConsumer* auth_status_consumer) { |
| + AccountId account_id = AccountId::FromUserEmail(kTestUserEmail); |
| + chromeos::UserContext expected_context(account_id); |
| + expected_context.SetKey(Key(kValidPassword)); |
| + |
| + chromeos::ExtendedAuthenticator* authenticator = |
| + new chromeos::FakeExtendedAuthenticator(auth_status_consumer, |
| + expected_context); |
| + return authenticator; |
| +} |
| + |
| +void DoNothing(const ModeList& modes) {} |
| + |
| +void FailIfCalled(const ModeList& modes) { |
| + FAIL(); |
| +} |
| + |
| +} // namespace |
| + |
| +class QuickUnlockPrivateUnitTest : public ExtensionApiUnittest { |
| + public: |
| + QuickUnlockPrivateUnitTest() |
| + : fake_user_manager_(new FakeChromeUserManager()), |
| + scoped_user_manager_(fake_user_manager_) {} |
| + |
| + protected: |
| + void SetUp() override { |
| + ExtensionApiUnittest::SetUp(); |
| + |
| + // Setup a primary user. |
| + auto test_account = AccountId::FromUserEmail(kTestUserEmail); |
| + fake_user_manager_->AddUser(test_account); |
| + fake_user_manager_->UserLoggedIn(test_account, kTestUserEmailHash, false); |
| + |
| + // Ensure that quick unlock is turned off. |
| + SetModes({}, {}); |
| + |
| + modes_changed_handler_ = base::Bind(&DoNothing); |
| + } |
| + |
| + // If a mode change event is raised, fail the test. |
| + void FailIfModesChanged() { |
| + modes_changed_handler_ = base::Bind(&FailIfCalled); |
| + } |
| + |
| + // If a mode change event is raised, expect the given |modes|. |
| + void ExpectModesChanged(const std::vector<QuickUnlockMode>& modes) { |
| + modes_changed_handler_ = |
| + base::Bind(&QuickUnlockPrivateUnitTest::ExpectModeList, |
| + base::Unretained(this), modes); |
| + expect_modes_changed_ = true; |
| + } |
| + |
| + // Wrapper for chrome.quickUnlockPrivate.getAvailableModes. |
| + ModeList GetAvailableModes() { |
| + // Run the function. |
| + std::unique_ptr<base::Value> result = |
| + RunFunction(new QuickUnlockPrivateGetAvailableModesFunction(), |
| + base::WrapUnique(new base::ListValue())); |
| + |
| + // Extract the results. |
| + ModeList modes; |
| + |
| + base::ListValue* list = nullptr; |
| + EXPECT_TRUE(result->GetAsList(&list)); |
| + // Consume the unique_ptr by reference so we don't take ownership. |
| + for (const std::unique_ptr<base::Value>& value : (*list)) { |
| + std::string mode; |
| + EXPECT_TRUE(value->GetAsString(&mode)); |
| + modes.push_back(quick_unlock_private::ParseQuickUnlockMode(mode)); |
| + } |
| + |
| + return modes; |
| + } |
| + |
| + // Wrapper for chrome.quickUnlockPrivate.getActiveModes. |
| + ModeList GetActiveModes() { |
| + std::unique_ptr<base::Value> result = |
| + RunFunction(new QuickUnlockPrivateGetActiveModesFunction(), |
| + base::WrapUnique(new base::ListValue())); |
| + |
| + ModeList modes; |
| + |
| + base::ListValue* list = nullptr; |
| + EXPECT_TRUE(result->GetAsList(&list)); |
| + for (const std::unique_ptr<base::Value>& value : *list) { |
| + std::string mode; |
| + EXPECT_TRUE(value->GetAsString(&mode)); |
| + modes.push_back(quick_unlock_private::ParseQuickUnlockMode(mode)); |
| + } |
| + |
| + return modes; |
| + } |
| + |
| + // Wrapper for chrome.quickUnlockPrivate.setModes that automatically uses a |
| + // valid password. |
| + bool SetModes(const ModeList& modes, |
| + const std::vector<std::string>& passwords) { |
| + return SetModesUsingPassword(kValidPassword, modes, passwords); |
| + } |
| + |
| + // Wrapper for chrome.quickUnlockPrivate.setModes. |
| + bool SetModesUsingPassword(const std::string& password, |
| + const ModeList& modes, |
| + const std::vector<std::string>& passwords) { |
| + // Serialize parameters. |
| + auto params = base::WrapUnique(new base::ListValue()); |
| + params->AppendString(password); |
| + |
| + auto serialized_modes = base::WrapUnique(new base::ListValue()); |
| + for (QuickUnlockMode mode : modes) |
| + serialized_modes->AppendString(quick_unlock_private::ToString(mode)); |
| + params->Append(std::move(serialized_modes)); |
| + |
| + auto serialized_passwords = base::WrapUnique(new base::ListValue()); |
| + for (const std::string& password : passwords) |
| + serialized_passwords->AppendString(password); |
| + params->Append(std::move(serialized_passwords)); |
| + |
| + // Setup a fake authenticator so quickUnlockPrivate.checkPassword doesn't |
| + // call cryptohome methods. |
| + auto* func = new QuickUnlockPrivateSetModesFunction(); |
| + func->SetAuthenticatorAllocatorForTesting( |
| + base::Bind(&CreateFakeAuthenticator)); |
| + |
| + // Stub out event handling since we are not setting up an event router. |
| + func->SetModesChangedEventHandlerForTesting(modes_changed_handler_); |
| + |
| + // Run function and extract the result. |
| + std::unique_ptr<base::Value> func_result = |
| + RunFunction(func, std::move(params)); |
| + bool result; |
| + EXPECT_TRUE(func_result->GetAsBoolean(&result)); |
| + |
| + // Verify that the mode change event handler was run if it was registered. |
| + // ExpectModesChanged will set expect_modes_changed_ to true and the event |
| + // handler will set it to false; so if the handler never runs, |
| + // expect_modes_changed_ will still be true. |
| + EXPECT_FALSE(expect_modes_changed_) << "Mode change event was not raised"; |
| + |
| + return result; |
| + } |
| + |
| + std::string SetModesWithError(const std::string& args) { |
| + auto func = new QuickUnlockPrivateSetModesFunction(); |
| + func->SetAuthenticatorAllocatorForTesting( |
| + base::Bind(&CreateFakeAuthenticator)); |
| + func->SetModesChangedEventHandlerForTesting(base::Bind(&DoNothing)); |
| + |
| + return api_test_utils::RunFunctionAndReturnError(func, args, profile()); |
| + } |
| + |
| + // Returns true if |password| is correct. This calls into SetModes to do so. |
| + // This will turn off any active quick unlock modes. |
| + bool CheckPassword(const std::string& password) { |
| + return SetModesUsingPassword(password, {}, {}); |
| + } |
| + |
| + private: |
| + // Runs the given |func| with the given |params|. |
| + std::unique_ptr<base::Value> RunFunction( |
| + scoped_refptr<UIThreadExtensionFunction> func, |
| + std::unique_ptr<base::ListValue> params) { |
| + return std::unique_ptr<base::Value>( |
| + api_test_utils::RunFunctionWithDelegateAndReturnSingleResult( |
| + func, std::move(params), profile(), |
| + base::WrapUnique(new ExtensionFunctionDispatcher(profile())), |
| + api_test_utils::NONE)); |
| + } |
| + |
| + // Verifies a mode change event is raised and that |expected| is now the |
| + // active set of quick unlock modes. |
| + void ExpectModeList(const ModeList& expected, const ModeList& actual) { |
| + EXPECT_EQ(expected, actual); |
| + expect_modes_changed_ = false; |
| + } |
| + |
| + FakeChromeUserManager* fake_user_manager_; |
| + ScopedUserManagerEnabler scoped_user_manager_; |
| + QuickUnlockPrivateSetModesFunction::ModesChangedEventHandler |
| + modes_changed_handler_; |
| + bool expect_modes_changed_ = false; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(QuickUnlockPrivateUnitTest); |
| +}; |
| + |
| +// Verify that password checking works. |
| +TEST_F(QuickUnlockPrivateUnitTest, CheckPassword) { |
| + EXPECT_TRUE(CheckPassword(kValidPassword)); |
| + EXPECT_FALSE(CheckPassword(kInvalidPassword)); |
| +} |
| + |
| +// Verifies that this returns PIN for GetAvailableModes. |
| +TEST_F(QuickUnlockPrivateUnitTest, GetAvailableModes) { |
| + EXPECT_EQ(GetAvailableModes(), |
| + ModeList{QuickUnlockMode::QUICK_UNLOCK_MODE_PIN}); |
| +} |
| + |
| +// Verifies that an invalid password cannot be used to update the mode list. |
| +TEST_F(QuickUnlockPrivateUnitTest, SetModesFailsWithInvalidPassword) { |
| + // Verify there is no active mode. |
| + EXPECT_EQ(GetActiveModes(), ModeList{}); |
| + |
| + // Try to enable PIN, but use an invalid password. Verify that no event is |
| + // raised and GetActiveModes still returns an empty set. |
| + FailIfModesChanged(); |
| + EXPECT_FALSE(SetModesUsingPassword( |
| + kInvalidPassword, {QuickUnlockMode::QUICK_UNLOCK_MODE_PIN}, {"11"})); |
| + EXPECT_EQ(GetActiveModes(), ModeList{}); |
| +} |
| + |
| +// Verifies that the quickUnlockPrivate.onActiveModesChanged is only raised when |
| +// the active set of modes changes. |
| +TEST_F(QuickUnlockPrivateUnitTest, ModeChangeEventOnlyRaisedWhenModesChange) { |
| + // Make sure quick unlock is turned off, and then verify that turning it off |
| + // again does not trigger an event. |
| + EXPECT_TRUE(SetModes({}, {})); |
| + FailIfModesChanged(); |
| + EXPECT_TRUE(SetModes({}, {})); |
| + |
| + // Turn on PIN unlock, and then verify turning it on again and also changing |
| + // the password does not trigger an event. |
| + ExpectModesChanged(ModeList{QuickUnlockMode::QUICK_UNLOCK_MODE_PIN}); |
| + EXPECT_TRUE(SetModes({QuickUnlockMode::QUICK_UNLOCK_MODE_PIN}, {"11"})); |
| + FailIfModesChanged(); |
| + EXPECT_TRUE(SetModes({QuickUnlockMode::QUICK_UNLOCK_MODE_PIN}, {"22"})); |
| + EXPECT_TRUE(SetModes({QuickUnlockMode::QUICK_UNLOCK_MODE_PIN}, {""})); |
| +} |
| + |
| +TEST_F(QuickUnlockPrivateUnitTest, SetModesAndGetActiveModes) { |
| + // Update mode to PIN raises an event and updates GetActiveModes. |
| + ExpectModesChanged(ModeList{QuickUnlockMode::QUICK_UNLOCK_MODE_PIN}); |
| + EXPECT_TRUE(SetModes({QuickUnlockMode::QUICK_UNLOCK_MODE_PIN}, {"11"})); |
| + EXPECT_EQ(GetActiveModes(), ModeList{QuickUnlockMode::QUICK_UNLOCK_MODE_PIN}); |
| + |
| + // SetModes can be used to turn off a quick unlock mode. |
| + ExpectModesChanged(ModeList{}); |
| + EXPECT_TRUE(SetModes({}, {})); |
| + EXPECT_EQ(GetActiveModes(), ModeList{}); |
| +} |
| + |
| +// Verifies that enabling PIN quick unlock actually talks to the PIN subsystem. |
| +TEST_F(QuickUnlockPrivateUnitTest, VerifyAuthenticationAgainstPIN) { |
| + PinStorage* pin_storage = PinStorageFactory::GetForProfile(profile()); |
| + |
| + EXPECT_TRUE(SetModes({}, {})); |
| + EXPECT_FALSE(pin_storage->IsPinSet()); |
| + |
| + EXPECT_TRUE(SetModes({QuickUnlockMode::QUICK_UNLOCK_MODE_PIN}, {"11"})); |
| + EXPECT_TRUE(pin_storage->IsPinSet()); |
| + |
| + pin_storage->MarkStrongAuth(); |
| + pin_storage->ResetUnlockAttemptCount(); |
| + EXPECT_TRUE(pin_storage->TryAuthenticatePin("11")); |
| + EXPECT_FALSE(pin_storage->TryAuthenticatePin("00")); |
| +} |
| + |
| +TEST_F(QuickUnlockPrivateUnitTest, ThrowErrorOnMismatchedParameterCount) { |
| + EXPECT_FALSE(SetModesWithError("[\"valid\", [\"PIN\"], []]").empty()); |
| + EXPECT_FALSE(SetModesWithError("[\"valid\", [], [\"11\"]]").empty()); |
| +} |
|
Devlin
2016/06/21 00:16:46
Test deactivating quick unlock?
jdufault
2016/06/21 18:46:39
Yep, that is done inside of 'SetModesAndGetActiveM
|
| + |
| +} // namespace chromeos |