1 // 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.
5 #include "remoting/test/chromoting_test_driver_environment.h"
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"
16 #include "remoting/test/host_list_fetcher.h"
17 #include "remoting/test/refresh_token_store.h"
22 ChromotingTestDriverEnvironment
* g_chromoting_shared_data
= nullptr;
24 ChromotingTestDriverEnvironment::EnvironmentOptions::EnvironmentOptions() {
27 ChromotingTestDriverEnvironment::EnvironmentOptions::~EnvironmentOptions() {
30 ChromotingTestDriverEnvironment::ChromotingTestDriverEnvironment(
31 const EnvironmentOptions
& options
)
32 : host_name_(options
.host_name
),
33 user_name_(options
.user_name
),
35 refresh_token_file_path_(options
.refresh_token_file_path
),
36 test_access_token_fetcher_(nullptr),
37 test_refresh_token_store_(nullptr),
38 test_host_list_fetcher_(nullptr) {
39 DCHECK(!user_name_
.empty());
40 DCHECK(!host_name_
.empty());
43 ChromotingTestDriverEnvironment::~ChromotingTestDriverEnvironment() {
46 bool ChromotingTestDriverEnvironment::Initialize(
47 const std::string
& auth_code
) {
48 if (!access_token_
.empty()) {
52 if (!base::MessageLoop::current()) {
53 message_loop_
.reset(new base::MessageLoopForIO
);
56 // If a unit test has set |test_refresh_token_store_| then we should use it
57 // below. Note that we do not want to destroy the test object.
58 scoped_ptr
<RefreshTokenStore
> temporary_refresh_token_store
;
59 RefreshTokenStore
* refresh_token_store
= test_refresh_token_store_
;
60 if (!refresh_token_store
) {
61 temporary_refresh_token_store
=
62 RefreshTokenStore::OnDisk(user_name_
, refresh_token_file_path_
);
63 refresh_token_store
= temporary_refresh_token_store
.get();
66 // Check to see if we have a refresh token stored for this user.
67 refresh_token_
= refresh_token_store
->FetchRefreshToken();
68 if (refresh_token_
.empty()) {
69 // This isn't necessarily an error as this might be a first run scenario.
70 VLOG(2) << "No refresh token stored for " << user_name_
;
72 if (auth_code
.empty()) {
73 // No token and no Auth code means no service connectivity, bail!
74 LOG(ERROR
) << "Cannot retrieve an access token without a stored refresh"
75 << " token on disk or an auth_code passed into the tool";
80 if (!RetrieveAccessToken(auth_code
) || !RetrieveHostList()) {
81 // If we cannot retrieve an access token or a host list, then nothing is
82 // going to work. We should let the caller know that our object is not ready
90 void ChromotingTestDriverEnvironment::DisplayHostList() {
91 const char kHostAvailabilityFormatString
[] = "%-45s%-15s%-35s";
93 LOG(INFO
) << base::StringPrintf(kHostAvailabilityFormatString
,
94 "Host Name", "Host Status", "Host JID");
95 LOG(INFO
) << base::StringPrintf(kHostAvailabilityFormatString
,
96 "---------", "-----------", "--------");
99 for (const HostInfo
& host_info
: host_list_
) {
100 HostStatus host_status
= host_info
.status
;
101 if (host_status
== kHostStatusOnline
) {
103 } else if (host_status
== kHostStatusOffline
) {
109 LOG(INFO
) << base::StringPrintf(
110 kHostAvailabilityFormatString
, host_info
.host_name
.c_str(),
111 status
.c_str(), host_info
.host_jid
.c_str());
115 void ChromotingTestDriverEnvironment::SetAccessTokenFetcherForTest(
116 AccessTokenFetcher
* access_token_fetcher
) {
117 DCHECK(access_token_fetcher
);
119 test_access_token_fetcher_
= access_token_fetcher
;
122 void ChromotingTestDriverEnvironment::SetRefreshTokenStoreForTest(
123 RefreshTokenStore
* refresh_token_store
) {
124 DCHECK(refresh_token_store
);
126 test_refresh_token_store_
= refresh_token_store
;
129 void ChromotingTestDriverEnvironment::SetHostListFetcherForTest(
130 HostListFetcher
* host_list_fetcher
) {
131 DCHECK(host_list_fetcher
);
133 test_host_list_fetcher_
= host_list_fetcher
;
136 void ChromotingTestDriverEnvironment::TearDown() {
137 // Letting the MessageLoop tear down during the test destructor results in
138 // errors after test completion, when the MessageLoop dtor touches the
139 // registered AtExitManager. The AtExitManager is torn down before the test
140 // destructor is executed, so we tear down the MessageLoop here, while it is
142 message_loop_
.reset();
145 bool ChromotingTestDriverEnvironment::RetrieveAccessToken(
146 const std::string
& auth_code
) {
147 base::RunLoop run_loop
;
149 access_token_
.clear();
151 AccessTokenCallback access_token_callback
=
152 base::Bind(&ChromotingTestDriverEnvironment::OnAccessTokenRetrieved
,
153 base::Unretained(this), run_loop
.QuitClosure());
155 // If a unit test has set |test_access_token_fetcher_| then we should use it
156 // below. Note that we do not want to destroy the test object at the end of
157 // the function which is why we have the dance below.
158 scoped_ptr
<AccessTokenFetcher
> temporary_access_token_fetcher
;
159 AccessTokenFetcher
* access_token_fetcher
= test_access_token_fetcher_
;
160 if (!access_token_fetcher
) {
161 temporary_access_token_fetcher
.reset(new AccessTokenFetcher());
162 access_token_fetcher
= temporary_access_token_fetcher
.get();
165 if (!auth_code
.empty()) {
166 // If the user passed in an authcode, then use it to retrieve an
167 // updated access/refresh token.
168 access_token_fetcher
->GetAccessTokenFromAuthCode(auth_code
,
169 access_token_callback
);
171 DCHECK(!refresh_token_
.empty());
173 access_token_fetcher
->GetAccessTokenFromRefreshToken(refresh_token_
,
174 access_token_callback
);
179 // If we were using an auth_code and received a valid refresh token,
180 // then we want to store it locally. If we had an auth code and did not
181 // receive a refresh token, then we should let the user know and exit.
182 if (!auth_code
.empty()) {
183 if (!refresh_token_
.empty()) {
184 // If a unit test has set |test_refresh_token_store_| then we should use
185 // it below. Note that we do not want to destroy the test object.
186 scoped_ptr
<RefreshTokenStore
> temporary_refresh_token_store
;
187 RefreshTokenStore
* refresh_token_store
= test_refresh_token_store_
;
188 if (!refresh_token_store
) {
189 temporary_refresh_token_store
=
190 RefreshTokenStore::OnDisk(user_name_
, refresh_token_file_path_
);
191 refresh_token_store
= temporary_refresh_token_store
.get();
194 if (!refresh_token_store
->StoreRefreshToken(refresh_token_
)) {
195 // If we failed to persist the refresh token, then we should let the
196 // user sort out the issue before continuing.
200 LOG(ERROR
) << "Failed to use AUTH CODE to retrieve a refresh token.\n"
201 << "Was the one-time use AUTH CODE used more than once?";
206 if (access_token_
.empty()) {
207 LOG(ERROR
) << "Failed to retrieve access token.";
214 void ChromotingTestDriverEnvironment::OnAccessTokenRetrieved(
215 base::Closure done_closure
,
216 const std::string
& retrieved_access_token
,
217 const std::string
& retrieved_refresh_token
) {
218 VLOG(1) << "OnAccessTokenRetrieved() Called";
219 VLOG(1) << "Access Token: " << retrieved_access_token
;
221 access_token_
= retrieved_access_token
;
222 refresh_token_
= retrieved_refresh_token
;
227 bool ChromotingTestDriverEnvironment::RetrieveHostList() {
228 base::RunLoop run_loop
;
231 host_info_
= HostInfo();
233 // If a unit test has set |test_host_list_fetcher_| then we should use it
234 // below. Note that we do not want to destroy the test object at the end of
235 // the function which is why we have the dance below.
236 scoped_ptr
<HostListFetcher
> temporary_host_list_fetcher
;
237 HostListFetcher
* host_list_fetcher
= test_host_list_fetcher_
;
238 if (!host_list_fetcher
) {
239 temporary_host_list_fetcher
.reset(new HostListFetcher());
240 host_list_fetcher
= temporary_host_list_fetcher
.get();
243 remoting::test::HostListFetcher::HostlistCallback host_list_callback
=
244 base::Bind(&ChromotingTestDriverEnvironment::OnHostListRetrieved
,
245 base::Unretained(this), run_loop
.QuitClosure());
247 host_list_fetcher
->RetrieveHostlist(access_token_
, host_list_callback
);
251 if (host_list_
.empty()) {
252 // Note: Access token may have expired, but it is unlikely.
253 LOG(ERROR
) << "Retrieved host list is empty.\n"
254 << "Does the account have hosts set up?";
258 // If the host or command line parameters are not setup correctly, we want to
259 // let the user fix the issue before continuing.
260 bool found_host_name
= false;
261 auto host_info_iter
= std::find_if(host_list_
.begin(), host_list_
.end(),
262 [this, &found_host_name
](const remoting::test::HostInfo
& host_info
) {
263 if (host_info
.host_name
== host_name_
) {
264 found_host_name
= true;
265 return host_info
.IsReadyForConnection();
269 if (host_info_iter
== host_list_
.end()) {
270 if (found_host_name
) {
271 LOG(ERROR
) << this->host_name_
<< " is not ready to connect.";
273 LOG(ERROR
) << this->host_name_
<< " was not found in the host list.";
279 host_info_
= *host_info_iter
;
284 void ChromotingTestDriverEnvironment::OnHostListRetrieved(
285 base::Closure done_closure
,
286 const std::vector
<HostInfo
>& retrieved_host_list
) {
287 VLOG(1) << "OnHostListRetrieved() Called";
289 host_list_
= retrieved_host_list
;
295 } // namespace remoting