Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / remoting / test / chromoting_test_driver.cc
blob05dddc0a37bc62764861c210b04d444529ab6557
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 <string>
6 #include <vector>
8 #include "base/bind.h"
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 "remoting/test/test_chromoting_client.h"
24 #include "testing/gtest/include/gtest/gtest.h"
26 namespace switches {
27 const char kAuthCodeSwitchName[] = "authcode";
28 const char kPinSwitchName[] = "pin";
29 const char kHelpSwitchName[] = "help";
30 const char kHostNameSwitchName[] = "hostname";
31 const char kLoggingLevelSwitchName[] = "verbosity";
32 const char kRefreshTokenPathSwitchName[] = "refresh-token-path";
33 const char kSingleProcessTestsSwitchName[] = "single-process-tests";
34 const char kUserNameSwitchName[] = "username";
37 namespace {
38 const char kChromotingAuthScopeValues[] =
39 "https://www.googleapis.com/auth/chromoting "
40 "https://www.googleapis.com/auth/googletalk "
41 "https://www.googleapis.com/auth/userinfo.email";
43 const unsigned int kConnectionTimeoutSeconds = 10;
45 std::string GetAuthorizationCodeUri() {
46 // Replace space characters with a '+' sign when formatting.
47 bool use_plus = true;
48 return base::StringPrintf(
49 "https://accounts.google.com/o/oauth2/auth"
50 "?scope=%s"
51 "&redirect_uri=https://chromoting-oauth.talkgadget.google.com/"
52 "talkgadget/oauth/chrome-remote-desktop/dev"
53 "&response_type=code"
54 "&client_id=%s"
55 "&access_type=offline"
56 "&approval_prompt=force",
57 net::EscapeUrlEncodedData(kChromotingAuthScopeValues, use_plus).c_str(),
58 net::EscapeUrlEncodedData(
59 google_apis::GetOAuth2ClientID(google_apis::CLIENT_REMOTING),
60 use_plus).c_str());
63 void PrintUsage() {
64 printf("\n************************************\n");
65 printf("*** Chromoting Test Driver Usage ***\n");
66 printf("************************************\n");
68 printf("\nUsage:\n");
69 printf(" chromoting_test_driver --username=<example@gmail.com> [options]"
70 " --hostname=<example hostname>\n");
71 printf("\nRequired Parameters:\n");
72 printf(" %s: Specifies which account to use when running tests\n",
73 switches::kUserNameSwitchName);
74 printf(" %s: Specifies which host to connect to when running tests\n",
75 switches::kHostNameSwitchName);
76 printf("\nOptional Parameters:\n");
77 printf(" %s: Exchanged for a refresh and access token for authentication\n",
78 switches::kAuthCodeSwitchName);
79 printf(" %s: Displays additional usage information\n",
80 switches::kHelpSwitchName);
81 printf(" %s: Path to a JSON file containing username/refresh_token KVPs\n",
82 switches::kRefreshTokenPathSwitchName);
83 printf(" %s: Used to authenticate a chromoting connection with the host\n",
84 switches::kPinSwitchName);
85 printf(" %s: Specifies the optional logging level of the tool (0-3)."
86 " [default: off]\n",
87 switches::kLoggingLevelSwitchName);
90 void PrintAuthCodeInfo() {
91 printf("\n*******************************\n");
92 printf("*** Auth Code Example Usage ***\n");
93 printf("*******************************\n\n");
95 printf("If this is the first time you are running the tool,\n");
96 printf("you will need to provide an authorization code.\n");
97 printf("This code will be exchanged for a long term refresh token which\n");
98 printf("will be stored locally and used to acquire a short lived access\n");
99 printf("token to connect to the remoting service apis and establish a\n");
100 printf("remote host connection.\n\n");
102 printf("Note: You may need to repeat this step if the stored refresh token");
103 printf("\n has been revoked or expired.\n");
104 printf(" Passing in the same auth code twice will result in an error\n");
106 printf("\nFollow these steps to produce an auth code:\n"
107 " - Open the Authorization URL link shown below in your browser\n"
108 " - Approve the requested permissions for the tool\n"
109 " - Copy the 'code' value in the redirected URL\n"
110 " - Run the tool and pass in copied auth code as a parameter\n");
112 printf("\nAuthorization URL:\n");
113 printf("%s\n", GetAuthorizationCodeUri().c_str());
115 printf("\nRedirected URL Example:\n");
116 printf("https://chromoting-oauth.talkgadget.google.com/talkgadget/oauth/"
117 "chrome-remote-desktop/dev?code=4/AKtf...\n");
119 printf("\nTool usage example with the newly created auth code:\n");
120 printf("chromoting_test_driver --%s=example@gmail.com --%s=example_host_name"
121 " --%s=4/AKtf...\n\n",
122 switches::kUserNameSwitchName,
123 switches::kHostNameSwitchName,
124 switches::kAuthCodeSwitchName);
127 void PrintJsonFileInfo() {
128 printf("\n****************************************\n");
129 printf("*** Refresh Token File Example Usage ***\n");
130 printf("****************************************\n\n");
132 printf("In order to use this option, a valid JSON file must exist, be\n");
133 printf("properly formatted, and contain a username/token KVP.\n");
134 printf("Contents of example_file.json\n");
135 printf("{\n");
136 printf(" \"username1@fauxdomain.com\": \"1/3798Gsdf898shksdvfyi8sshad\",\n");
137 printf(" \"username2@fauxdomain.com\": \"1/8974sdf87asdgadfgaerhfRsAa\",\n");
138 printf("}\n\n");
140 printf("\nTool usage example:\n");
141 printf("chromoting_test_driver --%s=%s --%s=example_host_name"
142 " --%s=./example_file.json\n\n",
143 switches::kUserNameSwitchName, "username1@fauxdomain.com",
144 switches::kHostNameSwitchName, switches::kRefreshTokenPathSwitchName);
147 } // namespace
149 void OnHostlistRetrieved(
150 base::Closure done_closure,
151 std::vector<remoting::test::HostInfo>* hostlist,
152 const std::vector<remoting::test::HostInfo>& retrieved_hostlist) {
154 VLOG(1) << "OnHostlistRetrieved() Called";
156 DCHECK(hostlist);
158 *hostlist = retrieved_hostlist;
160 VLOG(1) << "There are " << hostlist->size() << " hosts in the hostlist";
162 done_closure.Run();
165 void OnAccessTokenRetrieved(
166 base::Closure done_closure,
167 std::string* access_token,
168 const std::string& retrieved_access_token,
169 const std::string& retrieved_refresh_token) {
171 VLOG(1) << "OnAccessTokenRetrieved() Called";
172 VLOG(1) << "Access Token: " << retrieved_access_token;
174 *access_token = retrieved_access_token;
176 done_closure.Run();
179 int main(int argc, char* argv[]) {
180 testing::InitGoogleTest(&argc, argv);
181 base::TestSuite test_suite(argc, argv);
183 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
184 DCHECK(command_line);
186 // Do not retry if tests fails.
187 command_line->AppendSwitchASCII(switches::kTestLauncherRetryLimit, "0");
189 // Different tests may require access to the same host if run in parallel.
190 // To avoid shared resource contention, tests will be run one at a time.
191 command_line->AppendSwitch(switches::kSingleProcessTestsSwitchName);
193 if (command_line->HasSwitch(switches::kHelpSwitchName)) {
194 PrintUsage();
195 PrintJsonFileInfo();
196 PrintAuthCodeInfo();
197 return 0;
200 // Update the logging verbosity level is user specified one.
201 std::string verbosity_level(
202 command_line->GetSwitchValueASCII(switches::kLoggingLevelSwitchName));
203 if (!verbosity_level.empty()) {
204 // Turn on logging for the test_driver and remoting components.
205 // This switch is parsed during logging::InitLogging.
206 command_line->AppendSwitchASCII("vmodule",
207 "*/remoting/*=" + verbosity_level);
208 logging::LoggingSettings logging_settings;
209 logging::InitLogging(logging_settings);
212 // The username is used to run the tests and determines which refresh token to
213 // select in the refresh token file.
214 const std::string username =
215 command_line->GetSwitchValueASCII(switches::kUserNameSwitchName);
216 if (username.empty()) {
217 LOG(ERROR) << "No username passed in, can't authenticate or run tests!";
218 return -1;
220 VLOG(1) << "Running chromoting tests as: " << username;
222 // Check to see if the user passed in a one time use auth_code for
223 // refreshing their credentials.
224 std::string auth_code =
225 command_line->GetSwitchValueASCII(switches::kAuthCodeSwitchName);
227 const base::FilePath refresh_token_path =
228 command_line->GetSwitchValuePath(switches::kRefreshTokenPathSwitchName);
230 // The hostname determines which host to initiate a session with from the list
231 // returned from the directory service.
232 const std::string hostname =
233 command_line->GetSwitchValueASCII(switches::kHostNameSwitchName);
234 if (hostname.empty()) {
235 LOG(ERROR) << "No hostname passed in, finding the host requires hostname!";
236 return -1;
238 VLOG(1) << "Chromoting tests will connect to: " << hostname;
240 const std::string pin =
241 command_line->GetSwitchValueASCII(switches::kPinSwitchName);
243 // TODO(TonyChun): Move this logic into a shared environment class.
244 scoped_ptr<remoting::test::RefreshTokenStore> refresh_token_store =
245 remoting::test::RefreshTokenStore::OnDisk(username, refresh_token_path);
247 std::string refresh_token = refresh_token_store->FetchRefreshToken();
248 if (auth_code.empty() && refresh_token.empty()) {
249 // RefreshTokenStore already logs which specific error occured.
250 return -1;
253 // Used for running network request tasks.
254 // TODO(TonyChun): Move this logic into a shared environment class.
255 base::MessageLoopForIO message_loop;
257 // Uses the refresh token to get the access token from GAIA.
258 remoting::test::AccessTokenFetcher access_token_fetcher;
260 // A RunLoop that yields to the thread's MessageLoop.
261 scoped_ptr<base::RunLoop> run_loop;
263 // RunLoop to handle callback from GAIA.
264 run_loop.reset(new base::RunLoop());
266 std::string access_token;
267 remoting::test::AccessTokenCallback access_token_callback =
268 base::Bind(&OnAccessTokenRetrieved,
269 run_loop->QuitClosure(),
270 &access_token);
272 if (!auth_code.empty()) {
273 access_token_fetcher.GetAccessTokenFromAuthCode(auth_code,
274 access_token_callback);
275 } else {
276 DCHECK(!refresh_token.empty());
277 access_token_fetcher.GetAccessTokenFromRefreshToken(refresh_token,
278 access_token_callback);
281 run_loop->Run();
283 run_loop.reset(new base::RunLoop());
285 std::vector<remoting::test::HostInfo> hostlist;
286 remoting::test::HostListFetcher::HostlistCallback hostlist_request_callback =
287 base::Bind(&OnHostlistRetrieved, run_loop->QuitClosure(), &hostlist);
289 // Uses the access token to get the hostlist from the directory service.
290 remoting::test::HostListFetcher hostlist_fetcher;
291 hostlist_fetcher.RetrieveHostlist(access_token, hostlist_request_callback);
293 run_loop->Run();
295 run_loop.reset(new base::RunLoop());
297 remoting::test::TestChromotingClient test_chromoting_client;
299 // Check if requested host is online and ready to receive connections.
300 auto it = std::find_if(hostlist.begin(), hostlist.end(),
301 [&hostname](const remoting::test::HostInfo& host_info) {
302 return host_info.host_name == hostname &&
303 host_info.IsReadyForConnection();
305 if (it != hostlist.end()) {
306 // Host is online and ready, initiate a remote session.
307 base::Timer timer(true, false);
308 timer.Start(FROM_HERE,
309 base::TimeDelta::FromSeconds(kConnectionTimeoutSeconds),
310 run_loop->QuitClosure());
311 test_chromoting_client.StartConnection(
312 it->GenerateConnectionSetupInfo(access_token, username, pin));
313 run_loop->Run();
314 test_chromoting_client.EndConnection();
315 } else {
316 LOG(ERROR) << "Requested host not found or not ready to connect";
319 return 0;