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..e36eab8927097c076c82a2e8b3c76527ede3267f |
| --- /dev/null |
| +++ b/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api_unittest.cc |
| @@ -0,0 +1,271 @@ |
| +// 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/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"; |
| + |
| +// Verifies that two mode lists are equal. |
| +void EXPECT_MODES(const ModeList& x, const ModeList& y) { |
| + ASSERT_EQ(x.size(), y.size()) << "Vectors x and y are of unequal length"; |
| + for (size_t i = 0; i < x.size(); ++i) |
| + EXPECT_EQ(x[i], y[i]) << "Vectors x and y differ at index " << i; |
| +} |
| + |
| +} // 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 fake authenticator so quickUnlockPrivate.checkPassword doesn't |
| + // call cryptohome methods. |
| + QuickUnlockPrivateSetModesFunction::SetCreateAuthenticatorForTesting( |
|
Devlin
2016/06/06 16:35:01
The create_authenticator_ variable is global, whic
jdufault
2016/06/08 18:39:10
I've done this is TearDown().
|
| + [](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; |
| + }); |
| + |
| + // Stub out event handling since we are not setting up an event router. |
| + // Some tests can and do override this by installing their own event |
| + // handler. |
| + QuickUnlockPrivateSetModesFunction::SetModesChangedEventHandlerForTesting( |
| + [](const ModeList& modes) {}); |
| + |
| + // 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({}, {}); |
| + } |
| + |
| + void TearDown() override { |
| + ExtensionApiUnittest::TearDown(); |
| + |
| + QuickUnlockPrivateSetModesFunction::SetModesChangedEventHandlerForTesting( |
| + nullptr); |
| + } |
| + |
| + // Wrapper for chrome.quickUnlockPrivate.getAvailableModes. |
| + ModeList GetAvailableModes() { |
| + // Run the function. |
| + std::unique_ptr<base::Value> result = |
| + RunFunction(new QuickUnlockPrivateGetAvailableModesFunction(), |
| + new base::ListValue()); |
| + |
| + // Extract the results. |
| + ModeList modes; |
| + |
| + base::ListValue* list; |
| + EXPECT_TRUE(result->GetAsList(&list)); |
| + for (base::ListValue::iterator i = list->begin(); i != list->end(); ++i) { |
| + std::string mode; |
| + EXPECT_TRUE((*i)->GetAsString(&mode)); |
| + modes.push_back(quick_unlock_private::ParseQuickUnlockMode(mode)); |
| + } |
| + |
| + return modes; |
| + } |
| + |
| + // Wrapper for chrome.quickUnlockPrivate.getActiveModes. |
| + ModeList GetActiveModes() { |
| + auto result = RunFunction(new QuickUnlockPrivateGetActiveModesFunction(), |
| + new base::ListValue()); |
| + |
| + ModeList modes; |
| + |
| + base::ListValue* list; |
| + EXPECT_TRUE(result->GetAsList(&list)); |
| + for (base::ListValue::iterator i = list->begin(); i != list->end(); ++i) { |
| + std::string mode; |
| + EXPECT_TRUE((*i)->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 the parameters. |
| + auto params = new base::ListValue(); |
| + params->AppendString(password); |
| + |
| + auto serialized_modes = new base::ListValue(); |
|
Devlin
2016/06/06 16:35:01
nit: typically preferred style to enforce ownershi
jdufault
2016/06/08 18:39:09
Done.
|
| + for (QuickUnlockMode mode : modes) |
| + serialized_modes->AppendString(quick_unlock_private::ToString(mode)); |
| + params->Append(base::WrapUnique(serialized_modes)); |
| + |
| + auto serialized_passwords = new base::ListValue(); |
| + for (const std::string& password : passwords) |
| + serialized_passwords->AppendString(password); |
| + params->Append(base::WrapUnique(serialized_passwords)); |
| + |
| + // Run function and extract the result. |
| + auto func_result = |
| + RunFunction(new QuickUnlockPrivateSetModesFunction(), params); |
| + bool result; |
| + EXPECT_TRUE(func_result->GetAsBoolean(&result)); |
| + return result; |
| + } |
| + |
| + std::string SetModesWithError(const std::string& args) { |
| + return api_test_utils::RunFunctionAndReturnError( |
| + new QuickUnlockPrivateSetModesFunction(), 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|. This function takes |
| + // ownership over |func| and |params|; they are passed as raw pointers for |
| + // caller convenience. |
| + std::unique_ptr<base::Value> RunFunction(UIThreadExtensionFunction* func, |
| + base::ListValue* params) { |
| + return std::unique_ptr<base::Value>( |
| + api_test_utils::RunFunctionWithDelegateAndReturnSingleResult( |
| + func, base::WrapUnique(params), profile(), |
| + base::WrapUnique(new ExtensionFunctionDispatcher(profile())), |
| + api_test_utils::NONE)); |
| + } |
| + |
| + FakeChromeUserManager* fake_user_manager_; |
| + ScopedUserManagerEnabler scoped_user_manager_; |
| + |
| + 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_MODES(GetAvailableModes(), {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_MODES(GetActiveModes(), {}); |
| + |
| + // Try to enable PIN, but use an invalid password. Verify that no event is |
| + // raised and GetActiveModes still returns an empty set. |
| + QuickUnlockPrivateSetModesFunction::SetModesChangedEventHandlerForTesting( |
| + [](const ModeList& modes) { FAIL(); }); |
| + EXPECT_FALSE(SetModesUsingPassword( |
| + kInvalidPassword, {QuickUnlockMode::QUICK_UNLOCK_MODE_PIN}, {"11"})); |
| + EXPECT_MODES(GetActiveModes(), {}); |
| +} |
| + |
| +// 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({}, {})); |
| + QuickUnlockPrivateSetModesFunction::SetModesChangedEventHandlerForTesting( |
| + [](const ModeList& modes) { FAIL(); }); |
| + EXPECT_TRUE(SetModes({}, {})); |
| + |
| + // Turn on PIN unlock, and then verify turning it on again and also changing |
| + // the password does not trigger an event. |
| + QuickUnlockPrivateSetModesFunction::SetModesChangedEventHandlerForTesting( |
|
Devlin
2016/06/06 16:35:01
Why do we need to do this everywhere?
jdufault
2016/06/08 18:39:09
You mean calling SetModesChangedEventHandlerForTes
|
| + [](const ModeList& modes) {}); |
| + EXPECT_TRUE(SetModes({QuickUnlockMode::QUICK_UNLOCK_MODE_PIN}, {"11"})); |
| + QuickUnlockPrivateSetModesFunction::SetModesChangedEventHandlerForTesting( |
| + [](const ModeList& modes) { FAIL(); }); |
| + 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. |
| + QuickUnlockPrivateSetModesFunction::SetModesChangedEventHandlerForTesting( |
| + [](const ModeList& modes) { |
| + EXPECT_MODES(modes, {QuickUnlockMode::QUICK_UNLOCK_MODE_PIN}); |
| + }); |
| + EXPECT_TRUE(SetModes({QuickUnlockMode::QUICK_UNLOCK_MODE_PIN}, {"11"})); |
| + EXPECT_MODES(GetActiveModes(), {QuickUnlockMode::QUICK_UNLOCK_MODE_PIN}); |
| + |
| + // SetModes can be used to turn off a quick unlock mode. |
| + QuickUnlockPrivateSetModesFunction::SetModesChangedEventHandlerForTesting( |
| + [](const ModeList& modes) { EXPECT_MODES(modes, {}); }); |
| + EXPECT_TRUE(SetModes({}, {})); |
| + EXPECT_MODES(GetActiveModes(), {}); |
| +} |
| + |
| +// 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()); |
| +} |
| + |
| +} // namespace chromeos |