1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/chromeos/login/cryptohome_op.h"
6
7#include <string>
8
9#include "base/memory/ref_counted.h"
10#include "base/message_loop.h"
11#include "chrome/browser/chromeos/cros/mock_library_loader.h"
12#include "chrome/browser/chromeos/cros/mock_cryptohome_library.h"
13#include "chrome/browser/chromeos/login/auth_attempt_state.h"
14#include "chrome/browser/chromeos/login/mock_auth_attempt_state_resolver.h"
15#include "chrome/browser/chromeos/login/test_attempt_state.h"
16#include "content/browser/browser_thread.h"
17#include "testing/gmock/include/gmock/gmock.h"
18#include "testing/gtest/include/gtest/gtest.h"
19
20using ::testing::AnyNumber;
21using ::testing::Invoke;
22using ::testing::Return;
23using ::testing::_;
24
25namespace chromeos {
26
27class CryptohomeOpTest : public ::testing::Test {
28 public:
29  CryptohomeOpTest()
30      : message_loop_(MessageLoop::TYPE_UI),
31        ui_thread_(BrowserThread::UI, &message_loop_),
32        io_thread_(BrowserThread::IO),
33        username_("me@nowhere.org"),
34        hash_ascii_("0a010000000000a0"),
35        state_(username_, "", hash_ascii_, "", "", false),
36        resolver_(new MockAuthAttemptStateResolver),
37        mock_library_(new MockCryptohomeLibrary) {
38  }
39
40  virtual ~CryptohomeOpTest() {}
41
42  virtual void SetUp() {
43    CrosLibrary::TestApi* test_api = CrosLibrary::Get()->GetTestApi();
44
45    MockLibraryLoader* loader = new MockLibraryLoader();
46    ON_CALL(*loader, Load(_))
47        .WillByDefault(Return(true));
48    EXPECT_CALL(*loader, Load(_))
49        .Times(AnyNumber());
50
51    // Passes ownership of |loader| to CrosLibrary.
52    test_api->SetLibraryLoader(loader, true);
53    // |mock_library_| is mine, though.
54    test_api->SetCryptohomeLibrary(mock_library_.get(), false);
55    mock_library_->SetUp(false, 0);
56
57    io_thread_.Start();
58  }
59
60  virtual void TearDown() {
61    // Prevent bogus gMock leak check from firing.
62    chromeos::CrosLibrary::TestApi* test_api =
63        chromeos::CrosLibrary::Get()->GetTestApi();
64    test_api->SetLibraryLoader(NULL, false);
65  }
66
67  void ExpectMigrate(bool passing_old_hash, const std::string& hash) {
68    if (passing_old_hash) {
69      EXPECT_CALL(*(mock_library_.get()), AsyncMigrateKey(username_,
70                                                          hash,
71                                                          hash_ascii_,
72                                                          _))
73          .Times(1)
74          .RetiresOnSaturation();
75    } else {
76      EXPECT_CALL(*(mock_library_.get()), AsyncMigrateKey(username_,
77                                                          hash_ascii_,
78                                                          hash,
79                                                          _))
80          .Times(1)
81          .RetiresOnSaturation();
82    }
83  }
84
85  void ExpectMount() {
86    EXPECT_CALL(*(mock_library_.get()),
87                AsyncMount(username_, hash_ascii_, true, _))
88        .Times(1)
89        .RetiresOnSaturation();
90  }
91
92  void ExpectMountGuest() {
93    EXPECT_CALL(*(mock_library_.get()), AsyncMountForBwsi(_))
94        .Times(1)
95        .RetiresOnSaturation();
96  }
97
98  void ExpectRemove() {
99    EXPECT_CALL(*(mock_library_.get()), AsyncRemove(username_, _))
100        .Times(1)
101        .RetiresOnSaturation();
102  }
103
104  void ExpectCheckKey() {
105    EXPECT_CALL(*(mock_library_.get()),
106                AsyncCheckKey(username_, hash_ascii_, _))
107        .Times(1)
108        .RetiresOnSaturation();
109  }
110
111  void RunTest(CryptohomeOp* op, bool outcome, int code) {
112    mock_library_->SetAsyncBehavior(outcome, code);
113
114    EXPECT_CALL(*(resolver_.get()), Resolve())
115        .Times(1)
116        .RetiresOnSaturation();
117
118    EXPECT_TRUE(op->Initiate());
119    // Force IO thread to finish tasks so I can verify |state_|.
120    io_thread_.Stop();
121
122    EXPECT_EQ(outcome, state_.cryptohome_outcome());
123    EXPECT_EQ(code, state_.cryptohome_code());
124  }
125
126  MessageLoop message_loop_;
127  BrowserThread ui_thread_;
128  BrowserThread io_thread_;
129  std::string username_;
130  std::string hash_ascii_;
131  TestAttemptState state_;
132  scoped_ptr<MockAuthAttemptStateResolver> resolver_;
133  scoped_refptr<CryptohomeOp> op_;
134  scoped_ptr<MockCryptohomeLibrary> mock_library_;
135
136};
137
138TEST_F(CryptohomeOpTest, MountSuccess) {
139  ExpectMount();
140  scoped_refptr<CryptohomeOp> op(
141      CryptohomeOp::CreateMountAttempt(&state_, resolver_.get(), true));
142  RunTest(op.get(), true, kCryptohomeMountErrorNone);
143}
144
145TEST_F(CryptohomeOpTest, MountFatal) {
146  ExpectMount();
147  scoped_refptr<CryptohomeOp> op(
148      CryptohomeOp::CreateMountAttempt(&state_, resolver_.get(), true));
149  RunTest(op.get(), false, kCryptohomeMountErrorFatal);
150}
151
152TEST_F(CryptohomeOpTest, MountKeyFailure) {
153  ExpectMount();
154  scoped_refptr<CryptohomeOp> op(
155      CryptohomeOp::CreateMountAttempt(&state_, resolver_.get(), true));
156  RunTest(op.get(), false, kCryptohomeMountErrorKeyFailure);
157}
158
159TEST_F(CryptohomeOpTest, MountRecreated) {
160  ExpectMount();
161  scoped_refptr<CryptohomeOp> op(
162      CryptohomeOp::CreateMountAttempt(&state_, resolver_.get(), true));
163  RunTest(op.get(), true, kCryptohomeMountErrorRecreated);
164}
165
166TEST_F(CryptohomeOpTest, MountGuestSuccess) {
167  ExpectMountGuest();
168  scoped_refptr<CryptohomeOp> op(
169      CryptohomeOp::CreateMountGuestAttempt(&state_, resolver_.get()));
170  RunTest(op.get(), true, kCryptohomeMountErrorNone);
171}
172
173TEST_F(CryptohomeOpTest, MountGuestFatal) {
174  ExpectMountGuest();
175  scoped_refptr<CryptohomeOp> op(
176      CryptohomeOp::CreateMountGuestAttempt(&state_, resolver_.get()));
177  RunTest(op.get(), false, kCryptohomeMountErrorFatal);
178}
179
180TEST_F(CryptohomeOpTest, MigrateSuccessPassOld) {
181  ExpectMigrate(true, "");
182  scoped_refptr<CryptohomeOp> op(
183      CryptohomeOp::CreateMigrateAttempt(&state_, resolver_.get(), true, ""));
184  RunTest(op.get(), true, kCryptohomeMountErrorNone);
185}
186
187TEST_F(CryptohomeOpTest, MigrateSuccessPassNew) {
188  ExpectMigrate(false, "");
189  scoped_refptr<CryptohomeOp> op(
190      CryptohomeOp::CreateMigrateAttempt(&state_, resolver_.get(), false, ""));
191  RunTest(op.get(), true, kCryptohomeMountErrorNone);
192}
193
194TEST_F(CryptohomeOpTest, MigrateKeyFailure) {
195  ExpectMigrate(true, "");
196  scoped_refptr<CryptohomeOp> op(
197      CryptohomeOp::CreateMigrateAttempt(&state_, resolver_.get(), true, ""));
198  RunTest(op.get(), false, kCryptohomeMountErrorKeyFailure);
199}
200
201TEST_F(CryptohomeOpTest, RemoveSuccess) {
202  ExpectRemove();
203  scoped_refptr<CryptohomeOp> op(
204      CryptohomeOp::CreateRemoveAttempt(&state_, resolver_.get()));
205  RunTest(op.get(), true, kCryptohomeMountErrorNone);
206}
207
208TEST_F(CryptohomeOpTest, RemoveFailure) {
209  ExpectRemove();
210  scoped_refptr<CryptohomeOp> op(
211      CryptohomeOp::CreateRemoveAttempt(&state_, resolver_.get()));
212  RunTest(op.get(), false, kCryptohomeMountErrorKeyFailure);
213}
214
215TEST_F(CryptohomeOpTest, CheckKeySuccess) {
216  ExpectCheckKey();
217  scoped_refptr<CryptohomeOp> op(
218      CryptohomeOp::CreateCheckKeyAttempt(&state_, resolver_.get()));
219  RunTest(op.get(), true, kCryptohomeMountErrorNone);
220}
221
222TEST_F(CryptohomeOpTest, CheckKeyFailure) {
223  ExpectCheckKey();
224  scoped_refptr<CryptohomeOp> op(
225      CryptohomeOp::CreateCheckKeyAttempt(&state_, resolver_.get()));
226  RunTest(op.get(), false, kCryptohomeMountErrorKeyFailure);
227}
228
229}  // namespace chromeos
230