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/app_remoting_test_driver_environment.h"
11 #include "base/bind.h"
12 #include "base/callback_forward.h"
13 #include "base/logging.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/run_loop.h"
16 #include "base/strings/stringprintf.h"
17 #include "remoting/test/access_token_fetcher.h"
18 #include "remoting/test/app_remoting_report_issue_request.h"
19 #include "remoting/test/refresh_token_store.h"
20 #include "remoting/test/remote_host_info.h"
25 AppRemotingTestDriverEnvironment
* AppRemotingSharedData
;
27 AppRemotingTestDriverEnvironment::EnvironmentOptions::EnvironmentOptions()
28 : refresh_token_file_path(base::FilePath()),
29 service_environment(kUnknownEnvironment
),
30 release_hosts_when_done(false) {}
32 AppRemotingTestDriverEnvironment::EnvironmentOptions::~EnvironmentOptions() {}
34 AppRemotingTestDriverEnvironment::AppRemotingTestDriverEnvironment(
35 const EnvironmentOptions
& options
)
36 : user_name_(options
.user_name
),
37 service_environment_(options
.service_environment
),
38 release_hosts_when_done_(options
.release_hosts_when_done
),
39 refresh_token_file_path_(options
.refresh_token_file_path
),
40 test_access_token_fetcher_(nullptr),
41 test_app_remoting_report_issue_request_(nullptr),
42 test_refresh_token_store_(nullptr),
43 test_remote_host_info_fetcher_(nullptr) {
44 DCHECK(!user_name_
.empty());
45 DCHECK(service_environment_
< kUnknownEnvironment
);
48 AppRemotingTestDriverEnvironment::~AppRemotingTestDriverEnvironment() {}
50 bool AppRemotingTestDriverEnvironment::Initialize(
51 const std::string
& auth_code
) {
52 if (!access_token_
.empty()) {
56 if (!base::MessageLoop::current()) {
57 message_loop_
.reset(new base::MessageLoopForIO
);
60 // If a unit test has set |test_refresh_token_store_| then we should use it
61 // below. Note that we do not want to destroy the test object.
62 scoped_ptr
<RefreshTokenStore
> temporary_refresh_token_store
;
63 RefreshTokenStore
* refresh_token_store
= test_refresh_token_store_
;
64 if (!refresh_token_store
) {
65 temporary_refresh_token_store
=
66 RefreshTokenStore::OnDisk(user_name_
, refresh_token_file_path_
);
67 refresh_token_store
= temporary_refresh_token_store
.get();
70 // Check to see if we have a refresh token stored for this user.
71 refresh_token_
= refresh_token_store
->FetchRefreshToken();
72 if (refresh_token_
.empty()) {
73 // This isn't necessarily an error as this might be a first run scenario.
74 VLOG(2) << "No refresh token stored for " << user_name_
;
76 if (auth_code
.empty()) {
77 // No token and no Auth code means no service connectivity, bail!
78 LOG(ERROR
) << "Cannot retrieve an access token without a stored refresh"
79 << " token on disk or an auth_code passed into the tool";
84 if (!RetrieveAccessToken(auth_code
)) {
85 // If we cannot retrieve an access token, then nothing is going to work and
86 // we should let the caller know that our object is not ready to be used.
93 bool AppRemotingTestDriverEnvironment::RefreshAccessToken() {
94 DCHECK(!refresh_token_
.empty());
96 // Empty auth code is used when refreshing.
97 return RetrieveAccessToken(std::string());
100 bool AppRemotingTestDriverEnvironment::GetRemoteHostInfoForApplicationId(
101 const std::string
& application_id
,
102 RemoteHostInfo
* remote_host_info
) {
103 DCHECK(!application_id
.empty());
104 DCHECK(remote_host_info
);
106 if (access_token_
.empty()) {
107 LOG(ERROR
) << "RemoteHostInfo requested without a valid access token. "
108 << "Ensure the environment object has been initialized.";
112 base::RunLoop run_loop
;
114 RemoteHostInfoCallback remote_host_info_fetch_callback
= base::Bind(
115 &AppRemotingTestDriverEnvironment::OnRemoteHostInfoRetrieved
,
116 base::Unretained(this), run_loop
.QuitClosure(), remote_host_info
);
118 // If a unit test has set |test_remote_host_info_fetcher_| then we should use
119 // it below. Note that we do not want to destroy the test object at the end
120 // of the function which is why we have the dance below.
121 scoped_ptr
<RemoteHostInfoFetcher
> temporary_remote_host_info_fetcher
;
122 RemoteHostInfoFetcher
* remote_host_info_fetcher
=
123 test_remote_host_info_fetcher_
;
124 if (!remote_host_info_fetcher
) {
125 temporary_remote_host_info_fetcher
.reset(new RemoteHostInfoFetcher());
126 remote_host_info_fetcher
= temporary_remote_host_info_fetcher
.get();
129 remote_host_info_fetcher
->RetrieveRemoteHostInfo(
130 application_id
, access_token_
, service_environment_
,
131 remote_host_info_fetch_callback
);
135 return remote_host_info
->IsReadyForConnection();
138 void AppRemotingTestDriverEnvironment::AddHostToReleaseList(
139 const std::string
& application_id
,
140 const std::string
& host_id
) {
141 if (!release_hosts_when_done_
) {
145 auto map_iterator
= host_ids_to_release_
.find(application_id
);
146 if (map_iterator
== host_ids_to_release_
.end()) {
147 std::vector
<std::string
> host_id_list(1, host_id
);
148 host_ids_to_release_
.insert(std::make_pair(application_id
, host_id_list
));
150 std::vector
<std::string
>* host_ids
= &map_iterator
->second
;
151 if (std::find(host_ids
->begin(), host_ids
->end(), host_id
) ==
153 host_ids
->push_back(host_id
);
158 void AppRemotingTestDriverEnvironment::ShowHostAvailability() {
159 const char kHostAvailabilityFormatString
[] = "%-25s%-35s%-10s";
161 LOG(INFO
) << base::StringPrintf(kHostAvailabilityFormatString
,
162 "Application Name", "Application ID",
165 for (const auto& application_name
: application_names_
) {
166 const RemoteApplicationDetails
& application_details
=
167 GetDetailsFromAppName(application_name
);
169 RemoteHostInfo remote_host_info
;
170 GetRemoteHostInfoForApplicationId(application_details
.application_id
,
174 RemoteHostStatus remote_host_status
= remote_host_info
.remote_host_status
;
175 if (remote_host_status
== kRemoteHostStatusReady
) {
177 } else if (remote_host_status
== kRemoteHostStatusPending
) {
178 status
= "Pending :|";
180 status
= "Unknown :(";
183 LOG(INFO
) << base::StringPrintf(
184 kHostAvailabilityFormatString
, application_name
.c_str(),
185 application_details
.application_id
.c_str(), status
.c_str());
189 const RemoteApplicationDetails
&
190 AppRemotingTestDriverEnvironment::GetDetailsFromAppName(
191 const std::string
& application_name
) {
192 const auto map_pair_iterator
=
193 application_details_map_
.find(application_name
);
194 DCHECK(map_pair_iterator
!= application_details_map_
.end());
196 return map_pair_iterator
->second
;
199 void AppRemotingTestDriverEnvironment::SetAccessTokenFetcherForTest(
200 AccessTokenFetcher
* access_token_fetcher
) {
201 DCHECK(access_token_fetcher
);
203 test_access_token_fetcher_
= access_token_fetcher
;
206 void AppRemotingTestDriverEnvironment::SetAppRemotingReportIssueRequestForTest(
207 AppRemotingReportIssueRequest
* app_remoting_report_issue_request
) {
208 DCHECK(app_remoting_report_issue_request
);
210 test_app_remoting_report_issue_request_
= app_remoting_report_issue_request
;
213 void AppRemotingTestDriverEnvironment::SetRefreshTokenStoreForTest(
214 RefreshTokenStore
* refresh_token_store
) {
215 DCHECK(refresh_token_store
);
217 test_refresh_token_store_
= refresh_token_store
;
220 void AppRemotingTestDriverEnvironment::SetRemoteHostInfoFetcherForTest(
221 RemoteHostInfoFetcher
* remote_host_info_fetcher
) {
222 DCHECK(remote_host_info_fetcher
);
224 test_remote_host_info_fetcher_
= remote_host_info_fetcher
;
227 void AppRemotingTestDriverEnvironment::TearDown() {
228 // If a unit test has set |test_app_remoting_report_issue_request_| then we
229 // should use it below. Note that we do not want to destroy the test object
230 // at the end of the function which is why we have the dance below.
231 scoped_ptr
<AppRemotingReportIssueRequest
> temporary_report_issue_request
;
232 AppRemotingReportIssueRequest
* report_issue_request
=
233 test_app_remoting_report_issue_request_
;
234 if (!report_issue_request
) {
235 temporary_report_issue_request
.reset(new AppRemotingReportIssueRequest());
236 report_issue_request
= temporary_report_issue_request
.get();
239 for (const auto& kvp
: host_ids_to_release_
) {
240 std::string application_id
= kvp
.first
;
241 VLOG(1) << "Releasing hosts for application: " << application_id
;
243 for (const auto& host_id
: kvp
.second
) {
244 base::RunLoop run_loop
;
246 VLOG(1) << " Releasing host: " << host_id
;
247 bool request_started
= report_issue_request
->Start(
248 application_id
, host_id
, access_token_
, service_environment_
, true,
249 run_loop
.QuitClosure());
251 if (request_started
) {
254 LOG(ERROR
) << "Failed to send ReportIssueRequest for: "
255 << application_id
<< ", " << host_id
;
259 temporary_report_issue_request
.reset();
261 // Letting the MessageLoop tear down during the test destructor results in
262 // errors after test completion, when the MessageLoop dtor touches the
263 // registered AtExitManager. The AtExitManager is torn down before the test
264 // destructor is executed, so we tear down the MessageLoop here, while it is
266 message_loop_
.reset();
269 bool AppRemotingTestDriverEnvironment::RetrieveAccessToken(
270 const std::string
& auth_code
) {
271 base::RunLoop run_loop
;
273 access_token_
.clear();
275 AccessTokenCallback access_token_callback
=
276 base::Bind(&AppRemotingTestDriverEnvironment::OnAccessTokenRetrieved
,
277 base::Unretained(this), run_loop
.QuitClosure());
279 // If a unit test has set |test_access_token_fetcher_| then we should use it
280 // below. Note that we do not want to destroy the test object at the end of
281 // the function which is why we have the dance below.
282 scoped_ptr
<AccessTokenFetcher
> temporary_access_token_fetcher
;
283 AccessTokenFetcher
* access_token_fetcher
= test_access_token_fetcher_
;
284 if (!access_token_fetcher
) {
285 temporary_access_token_fetcher
.reset(new AccessTokenFetcher());
286 access_token_fetcher
= temporary_access_token_fetcher
.get();
289 if (!auth_code
.empty()) {
290 // If the user passed in an authcode, then use it to retrieve an
291 // updated access/refresh token.
292 access_token_fetcher
->GetAccessTokenFromAuthCode(auth_code
,
293 access_token_callback
);
295 DCHECK(!refresh_token_
.empty());
297 access_token_fetcher
->GetAccessTokenFromRefreshToken(refresh_token_
,
298 access_token_callback
);
303 // If we were using an auth_code and received a valid refresh token,
304 // then we want to store it locally. If we had an auth code and did not
305 // receive a refresh token, then we should let the user know and exit.
306 if (!auth_code
.empty()) {
307 if (!refresh_token_
.empty()) {
308 // If a unit test has set |test_refresh_token_store_| then we should use
309 // it below. Note that we do not want to destroy the test object.
310 scoped_ptr
<RefreshTokenStore
> temporary_refresh_token_store
;
311 RefreshTokenStore
* refresh_token_store
= test_refresh_token_store_
;
312 if (!refresh_token_store
) {
313 temporary_refresh_token_store
=
314 RefreshTokenStore::OnDisk(user_name_
, refresh_token_file_path_
);
315 refresh_token_store
= temporary_refresh_token_store
.get();
318 if (!refresh_token_store
->StoreRefreshToken(refresh_token_
)) {
319 // If we failed to persist the refresh token, then we should let the
320 // user sort out the issue before continuing.
324 LOG(ERROR
) << "Failed to use AUTH CODE to retrieve a refresh token.\n"
325 << "Was the one-time use AUTH CODE used more than once?";
330 if (access_token_
.empty()) {
331 LOG(ERROR
) << "Failed to retrieve access token.";
338 void AppRemotingTestDriverEnvironment::OnAccessTokenRetrieved(
339 base::Closure done_closure
,
340 const std::string
& access_token
,
341 const std::string
& refresh_token
) {
342 access_token_
= access_token
;
343 refresh_token_
= refresh_token
;
348 void AppRemotingTestDriverEnvironment::OnRemoteHostInfoRetrieved(
349 base::Closure done_closure
,
350 RemoteHostInfo
* remote_host_info
,
351 const RemoteHostInfo
& retrieved_remote_host_info
) {
352 DCHECK(remote_host_info
);
354 *remote_host_info
= retrieved_remote_host_info
;
360 } // namespace remoting