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.
9 #include "base/command_line.h"
10 #include "base/files/file_path.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 "base/test/test_suite.h"
16 #include "base/test/test_switches.h"
17 #include "google_apis/google_api_keys.h"
18 #include "net/base/escape.h"
19 #include "remoting/test/access_token_fetcher.h"
20 #include "remoting/test/host_info.h"
21 #include "remoting/test/host_list_fetcher.h"
22 #include "remoting/test/refresh_token_store.h"
23 #include "testing/gtest/include/gtest/gtest.h"
26 const char kAuthCodeSwitchName
[] = "authcode";
27 const char kHelpSwitchName
[] = "help";
28 const char kHostNameSwitchName
[] = "hostname";
29 const char kLoggingLevelSwitchName
[] = "verbosity";
30 const char kRefreshTokenPathSwitchName
[] = "refresh-token-path";
31 const char kSingleProcessTestsSwitchName
[] = "single-process-tests";
32 const char kUserNameSwitchName
[] = "username";
36 const char kChromotingAuthScopeValues
[] =
37 "https://www.googleapis.com/auth/chromoting "
38 "https://www.googleapis.com/auth/googletalk "
39 "https://www.googleapis.com/auth/userinfo.email";
41 std::string
GetAuthorizationCodeUri() {
42 // Replace space characters with a '+' sign when formatting.
44 return base::StringPrintf(
45 "https://accounts.google.com/o/oauth2/auth"
47 "&redirect_uri=https://chromoting-oauth.talkgadget.google.com/"
48 "talkgadget/oauth/chrome-remote-desktop/dev"
51 "&access_type=offline"
52 "&approval_prompt=force",
53 net::EscapeUrlEncodedData(kChromotingAuthScopeValues
, use_plus
).c_str(),
54 net::EscapeUrlEncodedData(
55 google_apis::GetOAuth2ClientID(google_apis::CLIENT_REMOTING
),
60 printf("\n************************************\n");
61 printf("*** Chromoting Test Driver Usage ***\n");
62 printf("************************************\n");
65 printf(" chromoting_test_driver --username=<example@gmail.com> [options]"
66 " --hostname=<example hostname>\n");
67 printf("\nRequired Parameters:\n");
68 printf(" %s: Specifies which account to use when running tests\n",
69 switches::kUserNameSwitchName
);
70 printf(" %s: Specifies which host to connect to when running tests\n",
71 switches::kHostNameSwitchName
);
72 printf("\nOptional Parameters:\n");
73 printf(" %s: Exchanged for a refresh and access token for authentication\n",
74 switches::kAuthCodeSwitchName
);
75 printf(" %s: Displays additional usage information\n",
76 switches::kHelpSwitchName
);
77 printf(" %s: Path to a JSON file containing username/refresh_token KVPs\n",
78 switches::kRefreshTokenPathSwitchName
);
79 printf(" %s: Specifies the optional logging level of the tool (0-3)."
81 switches::kLoggingLevelSwitchName
);
84 void PrintAuthCodeInfo() {
85 printf("\n*******************************\n");
86 printf("*** Auth Code Example Usage ***\n");
87 printf("*******************************\n\n");
89 printf("If this is the first time you are running the tool,\n");
90 printf("you will need to provide an authorization code.\n");
91 printf("This code will be exchanged for a long term refresh token which\n");
92 printf("will be stored locally and used to acquire a short lived access\n");
93 printf("token to connect to the remoting service apis and establish a\n");
94 printf("remote host connection.\n\n");
96 printf("Note: You may need to repeat this step if the stored refresh token");
97 printf("\n has been revoked or expired.\n");
98 printf(" Passing in the same auth code twice will result in an error\n");
100 printf("\nFollow these steps to produce an auth code:\n"
101 " - Open the Authorization URL link shown below in your browser\n"
102 " - Approve the requested permissions for the tool\n"
103 " - Copy the 'code' value in the redirected URL\n"
104 " - Run the tool and pass in copied auth code as a parameter\n");
106 printf("\nAuthorization URL:\n");
107 printf("%s\n", GetAuthorizationCodeUri().c_str());
109 printf("\nRedirected URL Example:\n");
110 printf("https://chromoting-oauth.talkgadget.google.com/talkgadget/oauth/"
111 "chrome-remote-desktop/dev?code=4/AKtf...\n");
113 printf("\nTool usage example with the newly created auth code:\n");
114 printf("chromoting_test_driver --%s=example@gmail.com --%s=example_host_name"
115 " --%s=4/AKtf...\n\n",
116 switches::kUserNameSwitchName
,
117 switches::kHostNameSwitchName
,
118 switches::kAuthCodeSwitchName
);
121 void PrintJsonFileInfo() {
122 printf("\n****************************************\n");
123 printf("*** Refresh Token File Example Usage ***\n");
124 printf("****************************************\n\n");
126 printf("In order to use this option, a valid JSON file must exist, be\n");
127 printf("properly formatted, and contain a username/token KVP.\n");
128 printf("Contents of example_file.json\n");
130 printf(" \"username1@fauxdomain.com\": \"1/3798Gsdf898shksdvfyi8sshad\",\n");
131 printf(" \"username2@fauxdomain.com\": \"1/8974sdf87asdgadfgaerhfRsAa\",\n");
134 printf("\nTool usage example:\n");
135 printf("chromoting_test_driver --%s=%s --%s=example_host_name"
136 " --%s=./example_file.json\n\n",
137 switches::kUserNameSwitchName
, "username1@fauxdomain.com",
138 switches::kHostNameSwitchName
, switches::kRefreshTokenPathSwitchName
);
143 void OnHostlistRetrieved(
144 base::Closure done_closure
,
145 std::vector
<remoting::test::HostInfo
>* hostlist
,
146 const std::vector
<remoting::test::HostInfo
>& retrieved_hostlist
) {
148 VLOG(1) << "OnHostlistRetrieved() Called";
152 *hostlist
= retrieved_hostlist
;
154 VLOG(1) << "There are " << hostlist
->size() << " hosts in the hostlist";
159 void OnAccessTokenRetrieved(
160 base::Closure done_closure
,
161 std::string
* access_token
,
162 const std::string
& retrieved_access_token
,
163 const std::string
& retrieved_refresh_token
) {
165 VLOG(1) << "OnAccessTokenRetrieved() Called";
166 VLOG(1) << "Access Token: " << retrieved_access_token
;
168 *access_token
= retrieved_access_token
;
173 int main(int argc
, char* argv
[]) {
174 testing::InitGoogleTest(&argc
, argv
);
175 base::TestSuite
test_suite(argc
, argv
);
177 base::CommandLine
* command_line
= base::CommandLine::ForCurrentProcess();
178 DCHECK(command_line
);
180 // Do not retry if tests fails.
181 command_line
->AppendSwitchASCII(switches::kTestLauncherRetryLimit
, "0");
183 // Different tests may require access to the same host if run in parallel.
184 // To avoid shared resource contention, tests will be run one at a time.
185 command_line
->AppendSwitch(switches::kSingleProcessTestsSwitchName
);
187 if (command_line
->HasSwitch(switches::kHelpSwitchName
)) {
194 // Update the logging verbosity level is user specified one.
195 std::string
verbosity_level(
196 command_line
->GetSwitchValueASCII(switches::kLoggingLevelSwitchName
));
197 if (!verbosity_level
.empty()) {
198 // Turn on logging for the test_driver and remoting components.
199 // This switch is parsed during logging::InitLogging.
200 command_line
->AppendSwitchASCII("vmodule",
201 "*/remoting/*=" + verbosity_level
);
202 logging::LoggingSettings logging_settings
;
203 logging::InitLogging(logging_settings
);
206 // The username is used to run the tests and determines which refresh token to
207 // select in the refresh token file.
208 std::string username
=
209 command_line
->GetSwitchValueASCII(switches::kUserNameSwitchName
);
211 if (username
.empty()) {
212 LOG(ERROR
) << "No username passed in, can't authenticate or run tests!";
215 VLOG(1) << "Running chromoting tests as: " << username
;
217 // Check to see if the user passed in a one time use auth_code for
218 // refreshing their credentials.
219 std::string auth_code
=
220 command_line
->GetSwitchValueASCII(switches::kAuthCodeSwitchName
);
222 base::FilePath refresh_token_path
=
223 command_line
->GetSwitchValuePath(switches::kRefreshTokenPathSwitchName
);
225 // The hostname determines which host to initiate a session with from the list
226 // returned from the directory service.
227 std::string hostname
=
228 command_line
->GetSwitchValueASCII(switches::kHostNameSwitchName
);
230 if (hostname
.empty()) {
231 LOG(ERROR
) << "No hostname passed in, connect to host requires hostname!";
234 VLOG(1) << "Chromoting tests will connect to: " << hostname
;
236 // TODO(TonyChun): Move this logic into a shared environment class.
237 scoped_ptr
<remoting::test::RefreshTokenStore
> refresh_token_store
=
238 remoting::test::RefreshTokenStore::OnDisk(username
, refresh_token_path
);
240 std::string refresh_token
= refresh_token_store
->FetchRefreshToken();
241 if (auth_code
.empty() && refresh_token
.empty()) {
242 // RefreshTokenStore already logs which specific error occured.
246 // Used for running network request tasks.
247 // TODO(TonyChun): Move this logic into a shared environment class.
248 base::MessageLoopForIO message_loop
;
250 // Uses the refresh token to get the access token from GAIA.
251 remoting::test::AccessTokenFetcher access_token_fetcher
;
253 // A RunLoop that yields to the thread's MessageLoop.
254 scoped_ptr
<base::RunLoop
> run_loop
;
256 // RunLoop to handle callback from GAIA.
257 run_loop
.reset(new base::RunLoop());
259 std::string access_token
;
260 remoting::test::AccessTokenCallback access_token_callback
=
261 base::Bind(&OnAccessTokenRetrieved
,
262 run_loop
->QuitClosure(),
265 if (!auth_code
.empty()) {
266 access_token_fetcher
.GetAccessTokenFromAuthCode(auth_code
,
267 access_token_callback
);
269 DCHECK(!refresh_token
.empty());
270 access_token_fetcher
.GetAccessTokenFromRefreshToken(refresh_token
,
271 access_token_callback
);
276 // RunLoop to handle callback from directory service.
277 run_loop
.reset(new base::RunLoop());
279 std::vector
<remoting::test::HostInfo
> hostlist
;
280 remoting::test::HostListFetcher::HostlistCallback hostlist_request_callback
=
281 base::Bind(&OnHostlistRetrieved
, run_loop
->QuitClosure(), &hostlist
);
283 // Uses the access token to get the hostlist from the directory service.
284 remoting::test::HostListFetcher hostlist_fetcher
;
285 hostlist_fetcher
.RetrieveHostlist(access_token
, hostlist_request_callback
);