blob: 2bd23881028c5b7c73e33e8ae4052cce6bc4b986 [file] [log] [blame]
tonychuned3010a2015-07-24 16:22:061// Copyright 2015 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 "remoting/test/chromoting_test_driver_environment.h"
6
7#include <string>
8#include <vector>
9
10#include "base/bind.h"
11#include "base/logging.h"
12#include "base/message_loop/message_loop.h"
13#include "base/run_loop.h"
14#include "base/strings/stringprintf.h"
15#include "remoting/test/access_token_fetcher.h"
tonychuned3010a2015-07-24 16:22:0616#include "remoting/test/host_list_fetcher.h"
17#include "remoting/test/refresh_token_store.h"
18
19namespace remoting {
20namespace test {
21
tonychun44959712015-07-28 17:38:2422ChromotingTestDriverEnvironment* g_chromoting_shared_data = nullptr;
23
Chris Watkins6fe52aa2017-11-28 03:24:0524ChromotingTestDriverEnvironment::EnvironmentOptions::EnvironmentOptions() =
25 default;
tonychuned3010a2015-07-24 16:22:0626
Chris Watkins6fe52aa2017-11-28 03:24:0527ChromotingTestDriverEnvironment::EnvironmentOptions::~EnvironmentOptions() =
28 default;
tonychuned3010a2015-07-24 16:22:0629
30ChromotingTestDriverEnvironment::ChromotingTestDriverEnvironment(
31 const EnvironmentOptions& options)
32 : host_name_(options.host_name),
joedowafb02952016-04-05 23:13:3133 host_jid_(options.host_jid),
tonychuned3010a2015-07-24 16:22:0634 user_name_(options.user_name),
tonychun44959712015-07-28 17:38:2435 pin_(options.pin),
tonychuned3010a2015-07-24 16:22:0636 refresh_token_file_path_(options.refresh_token_file_path),
joedow910989d2016-12-02 21:15:3237 use_test_environment_(options.use_test_environment) {
tonychuned3010a2015-07-24 16:22:0638 DCHECK(!user_name_.empty());
39 DCHECK(!host_name_.empty());
40}
41
Chris Watkins6fe52aa2017-11-28 03:24:0542ChromotingTestDriverEnvironment::~ChromotingTestDriverEnvironment() = default;
tonychuned3010a2015-07-24 16:22:0643
44bool ChromotingTestDriverEnvironment::Initialize(
45 const std::string& auth_code) {
46 if (!access_token_.empty()) {
47 return true;
48 }
49
50 if (!base::MessageLoop::current()) {
51 message_loop_.reset(new base::MessageLoopForIO);
52 }
53
54 // If a unit test has set |test_refresh_token_store_| then we should use it
55 // below. Note that we do not want to destroy the test object.
dcheng0765c492016-04-06 22:41:5356 std::unique_ptr<RefreshTokenStore> temporary_refresh_token_store;
tonychuned3010a2015-07-24 16:22:0657 RefreshTokenStore* refresh_token_store = test_refresh_token_store_;
58 if (!refresh_token_store) {
59 temporary_refresh_token_store =
60 RefreshTokenStore::OnDisk(user_name_, refresh_token_file_path_);
61 refresh_token_store = temporary_refresh_token_store.get();
62 }
63
64 // Check to see if we have a refresh token stored for this user.
65 refresh_token_ = refresh_token_store->FetchRefreshToken();
66 if (refresh_token_.empty()) {
67 // This isn't necessarily an error as this might be a first run scenario.
68 VLOG(2) << "No refresh token stored for " << user_name_;
69
70 if (auth_code.empty()) {
71 // No token and no Auth code means no service connectivity, bail!
72 LOG(ERROR) << "Cannot retrieve an access token without a stored refresh"
73 << " token on disk or an auth_code passed into the tool";
74 return false;
75 }
76 }
77
joedowafb02952016-04-05 23:13:3178 if (!RetrieveAccessToken(auth_code)) {
79 // If we cannot retrieve an access token, then nothing is going to work.
80 // Let the caller know that our object is not ready to be used.
tonychuned3010a2015-07-24 16:22:0681 return false;
82 }
83
84 return true;
85}
86
87void ChromotingTestDriverEnvironment::DisplayHostList() {
joedow22a45672017-03-06 18:49:1988 const char kHostAvailabilityFormatString[] = "%-25s%-15s%-35s\n";
tonychuned3010a2015-07-24 16:22:0689
joedow22a45672017-03-06 18:49:1990 printf(kHostAvailabilityFormatString, "Host Name", "Host Status", "Host JID");
91 printf(kHostAvailabilityFormatString, "---------", "-----------", "--------");
tonychuned3010a2015-07-24 16:22:0692
93 std::string status;
94 for (const HostInfo& host_info : host_list_) {
95 HostStatus host_status = host_info.status;
96 if (host_status == kHostStatusOnline) {
97 status = "ONLINE";
98 } else if (host_status == kHostStatusOffline) {
99 status = "OFFLINE";
100 } else {
101 status = "UNKNOWN";
102 }
103
joedow22a45672017-03-06 18:49:19104 printf(kHostAvailabilityFormatString, host_info.host_name.c_str(),
105 status.c_str(), host_info.host_jid.c_str());
tonychuned3010a2015-07-24 16:22:06106 }
107}
108
joedow22a45672017-03-06 18:49:19109bool ChromotingTestDriverEnvironment::WaitForHostOnline() {
joedowafb02952016-04-05 23:13:31110 if (host_list_.empty()) {
111 RetrieveHostList();
112 }
113
joedow22a45672017-03-06 18:49:19114 DisplayHostList();
115
anandc44945a92015-12-11 00:10:01116 // Refresh the |host_list_| periodically to check if expected JID is online.
joedowafb02952016-04-05 23:13:31117 const base::TimeDelta kTotalTimeInSeconds = base::TimeDelta::FromSeconds(60);
anandc44945a92015-12-11 00:10:01118 const base::TimeDelta kSleepTimeInSeconds = base::TimeDelta::FromSeconds(5);
joedowafb02952016-04-05 23:13:31119 const int kMaxIterations = kTotalTimeInSeconds / kSleepTimeInSeconds;
120
joedow22a45672017-03-06 18:49:19121 for (int iterations = 0; iterations < kMaxIterations; iterations++) {
122 if (!FindHostInHostList()) {
123 LOG(WARNING) << "Host '" << host_name_ << "' with JID '" << host_jid_
124 << "' not found in host list.";
125 return false;
126 }
127
joedowafb02952016-04-05 23:13:31128 if (host_info_.IsReadyForConnection()) {
joedow22a45672017-03-06 18:49:19129 if (iterations > 0) {
joedowafb02952016-04-05 23:13:31130 VLOG(0) << "Host online after: "
joedow22a45672017-03-06 18:49:19131 << iterations * kSleepTimeInSeconds.InSeconds() << " seconds.";
anandc44945a92015-12-11 00:10:01132 }
133 return true;
134 }
joedowafb02952016-04-05 23:13:31135
anandc44945a92015-12-11 00:10:01136 // Wait a while before refreshing host list.
137 base::PlatformThread::Sleep(kSleepTimeInSeconds);
138 RefreshHostList();
anandc44945a92015-12-11 00:10:01139 }
joedowafb02952016-04-05 23:13:31140
joedow22a45672017-03-06 18:49:19141 LOG(ERROR) << "Host '" << host_name_ << "' with JID '" << host_jid_
142 << "' still not online after "
143 << kMaxIterations * kSleepTimeInSeconds.InSeconds() << " seconds.";
anandc44945a92015-12-11 00:10:01144 return false;
145}
146
joedow22a45672017-03-06 18:49:19147bool ChromotingTestDriverEnvironment::FindHostInHostList() {
148 bool host_found = false;
149 for (HostInfo& host_info : host_list_) {
150 // The JID is optional so we consider an empty string to be a '*' match.
151 bool host_jid_match =
152 host_jid_.empty() || (host_jid_ == host_info.host_jid);
153 bool host_name_match = host_name_ == host_info.host_name;
154
155 if (host_name_match && host_jid_match) {
156 host_info_ = host_info;
157 host_found = true;
158 break;
159 }
160 }
161 return host_found;
162}
163
tonychuned3010a2015-07-24 16:22:06164void ChromotingTestDriverEnvironment::SetAccessTokenFetcherForTest(
165 AccessTokenFetcher* access_token_fetcher) {
166 DCHECK(access_token_fetcher);
167
168 test_access_token_fetcher_ = access_token_fetcher;
169}
170
171void ChromotingTestDriverEnvironment::SetRefreshTokenStoreForTest(
172 RefreshTokenStore* refresh_token_store) {
173 DCHECK(refresh_token_store);
174
175 test_refresh_token_store_ = refresh_token_store;
176}
177
178void ChromotingTestDriverEnvironment::SetHostListFetcherForTest(
179 HostListFetcher* host_list_fetcher) {
180 DCHECK(host_list_fetcher);
181
182 test_host_list_fetcher_ = host_list_fetcher;
183}
184
joedowafb02952016-04-05 23:13:31185void ChromotingTestDriverEnvironment::SetHostNameForTest(
186 const std::string& host_name) {
187 host_name_ = host_name;
188}
189
190void ChromotingTestDriverEnvironment::SetHostJidForTest(
191 const std::string& host_jid) {
192 host_jid_ = host_jid;
193}
194
tonychuned3010a2015-07-24 16:22:06195void ChromotingTestDriverEnvironment::TearDown() {
196 // Letting the MessageLoop tear down during the test destructor results in
197 // errors after test completion, when the MessageLoop dtor touches the
198 // registered AtExitManager. The AtExitManager is torn down before the test
199 // destructor is executed, so we tear down the MessageLoop here, while it is
200 // still valid.
201 message_loop_.reset();
202}
203
204bool ChromotingTestDriverEnvironment::RetrieveAccessToken(
205 const std::string& auth_code) {
206 base::RunLoop run_loop;
207
208 access_token_.clear();
209
210 AccessTokenCallback access_token_callback =
211 base::Bind(&ChromotingTestDriverEnvironment::OnAccessTokenRetrieved,
212 base::Unretained(this), run_loop.QuitClosure());
213
214 // If a unit test has set |test_access_token_fetcher_| then we should use it
215 // below. Note that we do not want to destroy the test object at the end of
216 // the function which is why we have the dance below.
dcheng0765c492016-04-06 22:41:53217 std::unique_ptr<AccessTokenFetcher> temporary_access_token_fetcher;
tonychuned3010a2015-07-24 16:22:06218 AccessTokenFetcher* access_token_fetcher = test_access_token_fetcher_;
219 if (!access_token_fetcher) {
220 temporary_access_token_fetcher.reset(new AccessTokenFetcher());
221 access_token_fetcher = temporary_access_token_fetcher.get();
222 }
223
224 if (!auth_code.empty()) {
225 // If the user passed in an authcode, then use it to retrieve an
226 // updated access/refresh token.
227 access_token_fetcher->GetAccessTokenFromAuthCode(auth_code,
228 access_token_callback);
229 } else {
230 DCHECK(!refresh_token_.empty());
231
232 access_token_fetcher->GetAccessTokenFromRefreshToken(refresh_token_,
233 access_token_callback);
234 }
235
236 run_loop.Run();
237
238 // If we were using an auth_code and received a valid refresh token,
239 // then we want to store it locally. If we had an auth code and did not
240 // receive a refresh token, then we should let the user know and exit.
241 if (!auth_code.empty()) {
242 if (!refresh_token_.empty()) {
243 // If a unit test has set |test_refresh_token_store_| then we should use
244 // it below. Note that we do not want to destroy the test object.
dcheng0765c492016-04-06 22:41:53245 std::unique_ptr<RefreshTokenStore> temporary_refresh_token_store;
tonychuned3010a2015-07-24 16:22:06246 RefreshTokenStore* refresh_token_store = test_refresh_token_store_;
247 if (!refresh_token_store) {
248 temporary_refresh_token_store =
249 RefreshTokenStore::OnDisk(user_name_, refresh_token_file_path_);
250 refresh_token_store = temporary_refresh_token_store.get();
251 }
252
253 if (!refresh_token_store->StoreRefreshToken(refresh_token_)) {
254 // If we failed to persist the refresh token, then we should let the
255 // user sort out the issue before continuing.
256 return false;
257 }
258 } else {
259 LOG(ERROR) << "Failed to use AUTH CODE to retrieve a refresh token.\n"
260 << "Was the one-time use AUTH CODE used more than once?";
261 return false;
262 }
263 }
264
265 if (access_token_.empty()) {
266 LOG(ERROR) << "Failed to retrieve access token.";
267 return false;
268 }
269
270 return true;
271}
272
273void ChromotingTestDriverEnvironment::OnAccessTokenRetrieved(
274 base::Closure done_closure,
275 const std::string& retrieved_access_token,
276 const std::string& retrieved_refresh_token) {
277 VLOG(1) << "OnAccessTokenRetrieved() Called";
278 VLOG(1) << "Access Token: " << retrieved_access_token;
279
280 access_token_ = retrieved_access_token;
281 refresh_token_ = retrieved_refresh_token;
282
283 done_closure.Run();
284}
285
anandc44945a92015-12-11 00:10:01286bool ChromotingTestDriverEnvironment::RefreshHostList() {
287 host_list_.clear();
288
289 return RetrieveHostList();
290}
291
tonychuned3010a2015-07-24 16:22:06292bool ChromotingTestDriverEnvironment::RetrieveHostList() {
293 base::RunLoop run_loop;
294
joedowafb02952016-04-05 23:13:31295 // Clear the previous host info.
tonychun29b70952015-08-06 04:03:12296 host_info_ = HostInfo();
tonychuned3010a2015-07-24 16:22:06297
298 // If a unit test has set |test_host_list_fetcher_| then we should use it
299 // below. Note that we do not want to destroy the test object at the end of
300 // the function which is why we have the dance below.
dcheng0765c492016-04-06 22:41:53301 std::unique_ptr<HostListFetcher> temporary_host_list_fetcher;
tonychuned3010a2015-07-24 16:22:06302 HostListFetcher* host_list_fetcher = test_host_list_fetcher_;
303 if (!host_list_fetcher) {
304 temporary_host_list_fetcher.reset(new HostListFetcher());
305 host_list_fetcher = temporary_host_list_fetcher.get();
306 }
307
308 remoting::test::HostListFetcher::HostlistCallback host_list_callback =
309 base::Bind(&ChromotingTestDriverEnvironment::OnHostListRetrieved,
310 base::Unretained(this), run_loop.QuitClosure());
311
joedow910989d2016-12-02 21:15:32312 host_list_fetcher->RetrieveHostlist(
313 access_token_,
314 use_test_environment_ ? kHostListTestRequestUrl : kHostListProdRequestUrl,
315 host_list_callback);
tonychuned3010a2015-07-24 16:22:06316
317 run_loop.Run();
318
319 if (host_list_.empty()) {
320 // Note: Access token may have expired, but it is unlikely.
321 LOG(ERROR) << "Retrieved host list is empty.\n"
322 << "Does the account have hosts set up?";
323 return false;
324 }
325
joedow22a45672017-03-06 18:49:19326 return true;
tonychuned3010a2015-07-24 16:22:06327}
328
329void ChromotingTestDriverEnvironment::OnHostListRetrieved(
330 base::Closure done_closure,
331 const std::vector<HostInfo>& retrieved_host_list) {
332 VLOG(1) << "OnHostListRetrieved() Called";
333
334 host_list_ = retrieved_host_list;
335
336 done_closure.Run();
337}
338
339} // namespace test
340} // namespace remoting