// Copyright (c) 2011 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 "chrome/browser/chromeos/login/parallel_authenticator.h" #include <string> #include <vector> #include "base/file_path.h" #include "base/file_util.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop.h" #include "base/path_service.h" #include "base/string_util.h" #include "base/stringprintf.h" #include "chrome/browser/chromeos/cros/mock_cryptohome_library.h" #include "chrome/browser/chromeos/cros/mock_library_loader.h" #include "chrome/browser/chromeos/login/mock_auth_response_handler.h" #include "chrome/browser/chromeos/login/mock_login_status_consumer.h" #include "chrome/browser/chromeos/login/mock_url_fetchers.h" #include "chrome/browser/chromeos/login/test_attempt_state.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/net/gaia/gaia_auth_fetcher_unittest.h" #include "chrome/common/net/url_fetcher.h" #include "chrome/test/testing_profile.h" #include "chrome/test/thread_test_helper.h" #include "content/browser/browser_thread.h" #include "googleurl/src/gurl.h" #include "net/base/net_errors.h" #include "net/url_request/url_request_status.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" using namespace file_util; using ::testing::AnyNumber; using ::testing::DoAll; using ::testing::Invoke; using ::testing::Return; using ::testing::SetArgumentPointee; using ::testing::_; namespace chromeos { class ResolveChecker : public ThreadTestHelper { public: ResolveChecker(TestAttemptState* state, ParallelAuthenticator* auth, ParallelAuthenticator::AuthState expected) : ThreadTestHelper(BrowserThread::IO), state_(state), auth_(auth), expected_(expected) { } ~ResolveChecker() {} virtual void RunTest() { auth_->set_attempt_state(state_); set_test_result(expected_ == auth_->ResolveState()); } private: TestAttemptState* state_; ParallelAuthenticator* auth_; ParallelAuthenticator::AuthState expected_; }; class ParallelAuthenticatorTest : public ::testing::Test { public: ParallelAuthenticatorTest() : message_loop_(MessageLoop::TYPE_UI), ui_thread_(BrowserThread::UI, &message_loop_), file_thread_(BrowserThread::FILE), io_thread_(BrowserThread::IO), username_("me@nowhere.org"), password_("fakepass") { hash_ascii_.assign("0a010000000000a0"); hash_ascii_.append(std::string(16, '0')); } ~ParallelAuthenticatorTest() {} virtual void SetUp() { chromeos::CrosLibrary::TestApi* test_api = chromeos::CrosLibrary::Get()->GetTestApi(); loader_ = new MockLibraryLoader(); ON_CALL(*loader_, Load(_)) .WillByDefault(Return(true)); EXPECT_CALL(*loader_, Load(_)) .Times(AnyNumber()); test_api->SetLibraryLoader(loader_, true); mock_library_ = new MockCryptohomeLibrary(); test_api->SetCryptohomeLibrary(mock_library_, true); file_thread_.Start(); io_thread_.Start(); auth_ = new ParallelAuthenticator(&consumer_); state_.reset(new TestAttemptState(username_, password_, hash_ascii_, "", "", false)); } // Tears down the test fixture. virtual void TearDown() { // Prevent bogus gMock leak check from firing. chromeos::CrosLibrary::TestApi* test_api = chromeos::CrosLibrary::Get()->GetTestApi(); test_api->SetLibraryLoader(NULL, false); test_api->SetCryptohomeLibrary(NULL, false); } FilePath PopulateTempFile(const char* data, int data_len) { FilePath out; FILE* tmp_file = CreateAndOpenTemporaryFile(&out); EXPECT_NE(tmp_file, static_cast<FILE*>(NULL)); EXPECT_EQ(WriteFile(out, data, data_len), data_len); EXPECT_TRUE(CloseFile(tmp_file)); return out; } FilePath FakeLocalaccountFile(const std::string& ascii) { FilePath exe_dir; FilePath local_account_file; PathService::Get(base::DIR_EXE, &exe_dir); FILE* tmp_file = CreateAndOpenTemporaryFileInDir(exe_dir, &local_account_file); int ascii_len = ascii.length(); EXPECT_NE(tmp_file, static_cast<FILE*>(NULL)); EXPECT_EQ(WriteFile(local_account_file, ascii.c_str(), ascii_len), ascii_len); EXPECT_TRUE(CloseFile(tmp_file)); return local_account_file; } void ReadLocalaccountFile(ParallelAuthenticator* auth, const std::string& filename) { BrowserThread::PostTask( BrowserThread::FILE, FROM_HERE, NewRunnableMethod(auth, &ParallelAuthenticator::LoadLocalaccount, filename)); file_thread_.Stop(); file_thread_.Start(); } // Allow test to fail and exit gracefully, even if OnLoginFailure() // wasn't supposed to happen. void FailOnLoginFailure() { ON_CALL(consumer_, OnLoginFailure(_)) .WillByDefault(Invoke(MockConsumer::OnFailQuitAndFail)); } // Allow test to fail and exit gracefully, even if OnLoginSuccess() // wasn't supposed to happen. void FailOnLoginSuccess() { ON_CALL(consumer_, OnLoginSuccess(_, _, _, _)) .WillByDefault(Invoke(MockConsumer::OnSuccessQuitAndFail)); } // Allow test to fail and exit gracefully, even if // OnOffTheRecordLoginSuccess() wasn't supposed to happen. void FailOnGuestLoginSuccess() { ON_CALL(consumer_, OnOffTheRecordLoginSuccess()) .WillByDefault(Invoke(MockConsumer::OnGuestSuccessQuitAndFail)); } void ExpectLoginFailure(const LoginFailure& failure) { EXPECT_CALL(consumer_, OnLoginFailure(failure)) .WillOnce(Invoke(MockConsumer::OnFailQuit)) .RetiresOnSaturation(); } void ExpectLoginSuccess(const std::string& username, const std::string& password, const GaiaAuthConsumer::ClientLoginResult& result, bool pending) { EXPECT_CALL(consumer_, OnLoginSuccess(username, password, result, pending)) .WillOnce(Invoke(MockConsumer::OnSuccessQuit)) .RetiresOnSaturation(); } void ExpectGuestLoginSuccess() { EXPECT_CALL(consumer_, OnOffTheRecordLoginSuccess()) .WillOnce(Invoke(MockConsumer::OnGuestSuccessQuit)) .RetiresOnSaturation(); } void ExpectPasswordChange() { EXPECT_CALL(consumer_, OnPasswordChangeDetected(result_)) .WillOnce(Invoke(MockConsumer::OnMigrateQuit)) .RetiresOnSaturation(); } void RunResolve(ParallelAuthenticator* auth, MessageLoop* loop) { BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, NewRunnableMethod(auth, &ParallelAuthenticator::Resolve)); loop->Run(); } void SetAttemptState(ParallelAuthenticator* auth, TestAttemptState* state) { auth->set_attempt_state(state); } MessageLoop message_loop_; BrowserThread ui_thread_; BrowserThread file_thread_; BrowserThread io_thread_; std::string username_; std::string password_; std::string hash_ascii_; GaiaAuthConsumer::ClientLoginResult result_; // Mocks, destroyed by CrosLibrary class. MockCryptohomeLibrary* mock_library_; MockLibraryLoader* loader_; MockConsumer consumer_; scoped_refptr<ParallelAuthenticator> auth_; scoped_ptr<TestAttemptState> state_; }; TEST_F(ParallelAuthenticatorTest, SaltToAscii) { unsigned char fake_salt[8] = { 0 }; fake_salt[0] = 10; fake_salt[1] = 1; fake_salt[7] = 10 << 4; std::vector<unsigned char> salt_v(fake_salt, fake_salt + sizeof(fake_salt)); ON_CALL(*mock_library_, GetSystemSalt()) .WillByDefault(Return(salt_v)); EXPECT_CALL(*mock_library_, GetSystemSalt()) .Times(1) .RetiresOnSaturation(); EXPECT_EQ("0a010000000000a0", auth_->SaltAsAscii()); } TEST_F(ParallelAuthenticatorTest, ReadLocalaccount) { FilePath tmp_file_path = FakeLocalaccountFile(username_); ReadLocalaccountFile(auth_.get(), tmp_file_path.BaseName().value()); EXPECT_EQ(auth_->localaccount_, username_); Delete(tmp_file_path, false); } TEST_F(ParallelAuthenticatorTest, ReadLocalaccountTrailingWS) { FilePath tmp_file_path = FakeLocalaccountFile(base::StringPrintf("%s\n", username_.c_str())); ReadLocalaccountFile(auth_.get(), tmp_file_path.BaseName().value()); EXPECT_EQ(auth_->localaccount_, username_); Delete(tmp_file_path, false); } TEST_F(ParallelAuthenticatorTest, ReadNoLocalaccount) { FilePath tmp_file_path = FakeLocalaccountFile(username_); EXPECT_TRUE(Delete(tmp_file_path, false)); // Ensure non-existent file. ReadLocalaccountFile(auth_.get(), tmp_file_path.BaseName().value()); EXPECT_EQ(auth_->localaccount_, std::string()); } TEST_F(ParallelAuthenticatorTest, OnLoginSuccess) { EXPECT_CALL(consumer_, OnLoginSuccess(username_, password_, result_, false)) .Times(1) .RetiresOnSaturation(); SetAttemptState(auth_, state_.release()); auth_->OnLoginSuccess(result_, false); } TEST_F(ParallelAuthenticatorTest, OnPasswordChangeDetected) { EXPECT_CALL(consumer_, OnPasswordChangeDetected(result_)) .Times(1) .RetiresOnSaturation(); SetAttemptState(auth_, state_.release()); auth_->OnPasswordChangeDetected(result_); } TEST_F(ParallelAuthenticatorTest, ResolveNothingDone) { scoped_refptr<ResolveChecker> checker( new ResolveChecker(state_.release(), auth_.get(), ParallelAuthenticator::CONTINUE)); EXPECT_TRUE(checker->Run()); } TEST_F(ParallelAuthenticatorTest, ResolvePossiblePwChange) { // Set up state as though a cryptohome mount attempt has occurred // and been rejected. state_->PresetCryptohomeStatus(false, chromeos::kCryptohomeMountErrorKeyFailure); scoped_refptr<ResolveChecker> checker( new ResolveChecker(state_.release(), auth_.get(), ParallelAuthenticator::POSSIBLE_PW_CHANGE)); EXPECT_TRUE(checker->Run()); } TEST_F(ParallelAuthenticatorTest, DriveFailedMount) { FailOnLoginSuccess(); ExpectLoginFailure(LoginFailure(LoginFailure::COULD_NOT_MOUNT_CRYPTOHOME)); // Set up state as though a cryptohome mount attempt has occurred // and failed. state_->PresetCryptohomeStatus(false, 0); SetAttemptState(auth_, state_.release()); RunResolve(auth_.get(), &message_loop_); } TEST_F(ParallelAuthenticatorTest, DriveGuestLogin) { ExpectGuestLoginSuccess(); FailOnLoginFailure(); // Set up mock cryptohome library to respond as though a tmpfs mount // attempt has occurred and succeeded. mock_library_->SetUp(true, 0); EXPECT_CALL(*mock_library_, AsyncMountForBwsi(_)) .Times(1) .RetiresOnSaturation(); auth_->LoginOffTheRecord(); message_loop_.Run(); } TEST_F(ParallelAuthenticatorTest, DriveGuestLoginButFail) { FailOnGuestLoginSuccess(); ExpectLoginFailure(LoginFailure(LoginFailure::COULD_NOT_MOUNT_TMPFS)); // Set up mock cryptohome library to respond as though a tmpfs mount // attempt has occurred and failed. mock_library_->SetUp(false, 0); EXPECT_CALL(*mock_library_, AsyncMountForBwsi(_)) .Times(1) .RetiresOnSaturation(); auth_->LoginOffTheRecord(); message_loop_.Run(); } TEST_F(ParallelAuthenticatorTest, DriveDataResync) { ExpectLoginSuccess(username_, password_, result_, false); FailOnLoginFailure(); // Set up mock cryptohome library to respond successfully to a cryptohome // remove attempt and a cryptohome create attempt (specified by the |true| // argument to AsyncMount). mock_library_->SetUp(true, 0); EXPECT_CALL(*mock_library_, AsyncRemove(username_, _)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*mock_library_, AsyncMount(username_, hash_ascii_, true, _)) .Times(1) .RetiresOnSaturation(); state_->PresetOnlineLoginStatus(result_, LoginFailure::None()); SetAttemptState(auth_, state_.release()); auth_->ResyncEncryptedData(result_); message_loop_.Run(); } TEST_F(ParallelAuthenticatorTest, DriveResyncFail) { FailOnLoginSuccess(); ExpectLoginFailure(LoginFailure(LoginFailure::DATA_REMOVAL_FAILED)); // Set up mock cryptohome library to fail a cryptohome remove attempt. mock_library_->SetUp(false, 0); EXPECT_CALL(*mock_library_, AsyncRemove(username_, _)) .Times(1) .RetiresOnSaturation(); SetAttemptState(auth_, state_.release()); auth_->ResyncEncryptedData(result_); message_loop_.Run(); } TEST_F(ParallelAuthenticatorTest, DriveRequestOldPassword) { FailOnLoginSuccess(); ExpectPasswordChange(); state_->PresetCryptohomeStatus(false, chromeos::kCryptohomeMountErrorKeyFailure); state_->PresetOnlineLoginStatus(result_, LoginFailure::None()); SetAttemptState(auth_, state_.release()); RunResolve(auth_.get(), &message_loop_); } TEST_F(ParallelAuthenticatorTest, DriveDataRecover) { ExpectLoginSuccess(username_, password_, result_, false); FailOnLoginFailure(); // Set up mock cryptohome library to respond successfully to a key migration. mock_library_->SetUp(true, 0); EXPECT_CALL(*mock_library_, AsyncMigrateKey(username_, _, hash_ascii_, _)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*mock_library_, AsyncMount(username_, hash_ascii_, false, _)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*mock_library_, GetSystemSalt()) .WillOnce(Return(CryptohomeBlob(2, 0))) .RetiresOnSaturation(); state_->PresetOnlineLoginStatus(result_, LoginFailure::None()); SetAttemptState(auth_, state_.release()); auth_->RecoverEncryptedData(std::string(), result_); message_loop_.Run(); } TEST_F(ParallelAuthenticatorTest, DriveDataRecoverButFail) { FailOnLoginSuccess(); ExpectPasswordChange(); // Set up mock cryptohome library to fail a key migration attempt, // asserting that the wrong password was used. mock_library_->SetUp(false, chromeos::kCryptohomeMountErrorKeyFailure); EXPECT_CALL(*mock_library_, AsyncMigrateKey(username_, _, hash_ascii_, _)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*mock_library_, GetSystemSalt()) .WillOnce(Return(CryptohomeBlob(2, 0))) .RetiresOnSaturation(); SetAttemptState(auth_, state_.release()); auth_->RecoverEncryptedData(std::string(), result_); message_loop_.Run(); } TEST_F(ParallelAuthenticatorTest, ResolveNoMount) { // Set up state as though a cryptohome mount attempt has occurred // and been rejected because the user doesn't exist. state_->PresetCryptohomeStatus( false, chromeos::kCryptohomeMountErrorUserDoesNotExist); scoped_refptr<ResolveChecker> checker( new ResolveChecker(state_.release(), auth_.get(), ParallelAuthenticator::NO_MOUNT)); EXPECT_TRUE(checker->Run()); } TEST_F(ParallelAuthenticatorTest, ResolveCreateNew) { // Set up state as though a cryptohome mount attempt has occurred // and been rejected because the user doesn't exist; additionally, // an online auth attempt has completed successfully. state_->PresetCryptohomeStatus( false, chromeos::kCryptohomeMountErrorUserDoesNotExist); state_->PresetOnlineLoginStatus(GaiaAuthConsumer::ClientLoginResult(), LoginFailure::None()); scoped_refptr<ResolveChecker> checker( new ResolveChecker(state_.release(), auth_.get(), ParallelAuthenticator::CREATE_NEW)); EXPECT_TRUE(checker->Run()); } TEST_F(ParallelAuthenticatorTest, DriveCreateForNewUser) { ExpectLoginSuccess(username_, password_, result_, false); FailOnLoginFailure(); // Set up mock cryptohome library to respond successfully to a cryptohome // create attempt (specified by the |true| argument to AsyncMount). mock_library_->SetUp(true, 0); EXPECT_CALL(*mock_library_, AsyncMount(username_, hash_ascii_, true, _)) .Times(1) .RetiresOnSaturation(); // Set up state as though a cryptohome mount attempt has occurred // and been rejected because the user doesn't exist; additionally, // an online auth attempt has completed successfully. state_->PresetCryptohomeStatus( false, chromeos::kCryptohomeMountErrorUserDoesNotExist); state_->PresetOnlineLoginStatus(GaiaAuthConsumer::ClientLoginResult(), LoginFailure::None()); SetAttemptState(auth_, state_.release()); RunResolve(auth_.get(), &message_loop_); } TEST_F(ParallelAuthenticatorTest, DriveOfflineLogin) { ExpectLoginSuccess(username_, password_, result_, false); FailOnLoginFailure(); // Set up state as though a cryptohome mount attempt has occurred and // succeeded. state_->PresetCryptohomeStatus(true, 0); GoogleServiceAuthError error = GoogleServiceAuthError::FromConnectionError(net::ERR_CONNECTION_RESET); state_->PresetOnlineLoginStatus(result_, LoginFailure::FromNetworkAuthFailure(error)); SetAttemptState(auth_, state_.release()); RunResolve(auth_.get(), &message_loop_); } TEST_F(ParallelAuthenticatorTest, DriveOfflineLoginDelayedOnline) { ExpectLoginSuccess(username_, password_, result_, true); FailOnLoginFailure(); // Set up state as though a cryptohome mount attempt has occurred and // succeeded. state_->PresetCryptohomeStatus(true, 0); // state_ is released further down. SetAttemptState(auth_, state_.get()); RunResolve(auth_.get(), &message_loop_); // Offline login has completed, so now we "complete" the online request. GoogleServiceAuthError error( GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); LoginFailure failure = LoginFailure::FromNetworkAuthFailure(error); state_.release()->PresetOnlineLoginStatus(result_, failure); ExpectLoginFailure(failure); RunResolve(auth_.get(), &message_loop_); } TEST_F(ParallelAuthenticatorTest, DriveOfflineLoginGetNewPassword) { ExpectLoginSuccess(username_, password_, result_, true); FailOnLoginFailure(); // Set up mock cryptohome library to respond successfully to a key migration. mock_library_->SetUp(true, 0); EXPECT_CALL(*mock_library_, AsyncMigrateKey(username_, state_->ascii_hash, _, _)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*mock_library_, GetSystemSalt()) .WillOnce(Return(CryptohomeBlob(2, 0))) .RetiresOnSaturation(); // Set up state as though a cryptohome mount attempt has occurred and // succeeded; also, an online request that never made it. state_->PresetCryptohomeStatus(true, 0); // state_ is released further down. SetAttemptState(auth_, state_.get()); RunResolve(auth_.get(), &message_loop_); // Offline login has completed, so now we "complete" the online request. GoogleServiceAuthError error( GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); LoginFailure failure = LoginFailure::FromNetworkAuthFailure(error); state_.release()->PresetOnlineLoginStatus(result_, failure); ExpectLoginFailure(failure); RunResolve(auth_.get(), &message_loop_); // After the request below completes, OnLoginSuccess gets called again. ExpectLoginSuccess(username_, password_, result_, false); MockFactory<SuccessFetcher> factory; URLFetcher::set_factory(&factory); TestingProfile profile; auth_->RetryAuth(&profile, username_, std::string(), std::string(), std::string()); message_loop_.Run(); } TEST_F(ParallelAuthenticatorTest, DriveOfflineLoginGetCaptchad) { ExpectLoginSuccess(username_, password_, result_, true); FailOnLoginFailure(); EXPECT_CALL(*mock_library_, GetSystemSalt()) .WillOnce(Return(CryptohomeBlob(2, 0))) .RetiresOnSaturation(); // Set up state as though a cryptohome mount attempt has occurred and // succeeded; also, an online request that never made it. state_->PresetCryptohomeStatus(true, 0); // state_ is released further down. SetAttemptState(auth_, state_.get()); RunResolve(auth_.get(), &message_loop_); // Offline login has completed, so now we "complete" the online request. GoogleServiceAuthError error( GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); LoginFailure failure = LoginFailure::FromNetworkAuthFailure(error); state_.release()->PresetOnlineLoginStatus(result_, failure); ExpectLoginFailure(failure); RunResolve(auth_.get(), &message_loop_); // After the request below completes, OnLoginSuccess gets called again. failure = LoginFailure::FromNetworkAuthFailure( GoogleServiceAuthError::FromCaptchaChallenge( CaptchaFetcher::GetCaptchaToken(), GURL(CaptchaFetcher::GetCaptchaUrl()), GURL(CaptchaFetcher::GetUnlockUrl()))); ExpectLoginFailure(failure); MockFactory<CaptchaFetcher> factory; URLFetcher::set_factory(&factory); TestingProfile profile; auth_->RetryAuth(&profile, username_, std::string(), std::string(), std::string()); message_loop_.Run(); } TEST_F(ParallelAuthenticatorTest, DriveOnlineLogin) { GaiaAuthConsumer::ClientLoginResult success("sid", "lsid", "", ""); ExpectLoginSuccess(username_, password_, success, false); FailOnLoginFailure(); // Set up state as though a cryptohome mount attempt has occurred and // succeeded. state_->PresetCryptohomeStatus(true, 0); state_->PresetOnlineLoginStatus(success, LoginFailure::None()); SetAttemptState(auth_, state_.release()); RunResolve(auth_.get(), &message_loop_); } TEST_F(ParallelAuthenticatorTest, DriveNeedNewPassword) { FailOnLoginSuccess(); // Set failing on success as the default... // ...but expect ONE successful login first. ExpectLoginSuccess(username_, password_, result_, true); GoogleServiceAuthError error( GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); LoginFailure failure = LoginFailure::FromNetworkAuthFailure(error); ExpectLoginFailure(failure); // Set up state as though a cryptohome mount attempt has occurred and // succeeded. state_->PresetCryptohomeStatus(true, 0); state_->PresetOnlineLoginStatus(result_, failure); SetAttemptState(auth_, state_.release()); RunResolve(auth_.get(), &message_loop_); } TEST_F(ParallelAuthenticatorTest, DriveLocalLogin) { ExpectGuestLoginSuccess(); FailOnLoginFailure(); // Set up mock cryptohome library to respond as though a tmpfs mount // attempt has occurred and succeeded. mock_library_->SetUp(true, 0); EXPECT_CALL(*mock_library_, AsyncMountForBwsi(_)) .Times(1) .RetiresOnSaturation(); // Pre-set test state as though an online login attempt failed to complete, // and that a cryptohome mount attempt failed because the user doesn't exist. GoogleServiceAuthError error = GoogleServiceAuthError::FromConnectionError(net::ERR_CONNECTION_RESET); LoginFailure failure = LoginFailure::FromNetworkAuthFailure(error); state_->PresetOnlineLoginStatus(result_, failure); state_->PresetCryptohomeStatus( false, chromeos::kCryptohomeMountErrorUserDoesNotExist); SetAttemptState(auth_, state_.release()); // Deal with getting the localaccount file FilePath tmp_file_path = FakeLocalaccountFile(username_); ReadLocalaccountFile(auth_.get(), tmp_file_path.BaseName().value()); RunResolve(auth_.get(), &message_loop_); Delete(tmp_file_path, false); } TEST_F(ParallelAuthenticatorTest, DriveUnlock) { ExpectLoginSuccess(username_, std::string(), result_, false); FailOnLoginFailure(); // Set up mock cryptohome library to respond successfully to a cryptohome // key-check attempt. mock_library_->SetUp(true, 0); EXPECT_CALL(*mock_library_, AsyncCheckKey(username_, _, _)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*mock_library_, GetSystemSalt()) .WillOnce(Return(CryptohomeBlob(2, 0))) .RetiresOnSaturation(); auth_->AuthenticateToUnlock(username_, ""); message_loop_.Run(); } TEST_F(ParallelAuthenticatorTest, DriveLocalUnlock) { ExpectLoginSuccess(username_, std::string(), result_, false); FailOnLoginFailure(); // Set up mock cryptohome library to fail a cryptohome key-check // attempt. mock_library_->SetUp(false, 0); EXPECT_CALL(*mock_library_, AsyncCheckKey(username_, _, _)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*mock_library_, GetSystemSalt()) .WillOnce(Return(CryptohomeBlob(2, 0))) .RetiresOnSaturation(); // Deal with getting the localaccount file FilePath tmp_file_path = FakeLocalaccountFile(username_); ReadLocalaccountFile(auth_.get(), tmp_file_path.BaseName().value()); auth_->AuthenticateToUnlock(username_, ""); message_loop_.Run(); Delete(tmp_file_path, false); } } // namespace chromeos