Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / remoting / test / app_remoting_test_driver.cc
blobdd6cb68427e075c2afa077c15cc13d329b1d85cb
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 "base/at_exit.h"
6 #include "base/bind.h"
7 #include "base/command_line.h"
8 #include "base/files/file_path.h"
9 #include "base/logging.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/test/launcher/unit_test_launcher.h"
13 #include "base/test/test_suite.h"
14 #include "base/test/test_switches.h"
15 #include "google_apis/google_api_keys.h"
16 #include "net/base/escape.h"
17 #include "remoting/test/app_remoting_test_driver_environment.h"
18 #include "testing/gtest/include/gtest/gtest.h"
20 namespace switches {
21 const char kAuthCodeSwitchName[] = "authcode";
22 const char kHelpSwitchName[] = "help";
23 const char kLoggingLevelSwitchName[] = "verbosity";
24 const char kRefreshTokenFileSwitchName[] = "refresh-token-file";
25 const char kReleaseHostsAfterTestingSwitchName[] = "release-hosts-after-tests";
26 const char kServiceEnvironmentSwitchName[] = "environment";
27 const char kShowHostAvailabilitySwitchName[] = "show-host-availability";
28 const char kSingleProcessTestsSwitchName[] = "single-process-tests";
29 const char kUserNameSwitchName[] = "username";
30 } // namespace switches
32 namespace {
34 // Requested permissions needed for App Remoting tests. The spaces in between
35 // scope fragments are necessary and will be escaped properly before use.
36 const char kAppRemotingAuthScopeValues[] =
37 "https://www.googleapis.com/auth/appremoting.runapplication"
38 " https://www.googleapis.com/auth/googletalk"
39 " https://www.googleapis.com/auth/userinfo.email"
40 " https://docs.google.com/feeds"
41 " https://www.googleapis.com/auth/drive";
43 std::string GetAuthorizationCodeUri() {
44 // Replace space characters with a '+' sign when formatting.
45 bool use_plus = true;
46 return base::StringPrintf(
47 "https://accounts.google.com/o/oauth2/auth"
48 "?scope=%s"
49 "&redirect_uri=https://chromoting-oauth.talkgadget.google.com/"
50 "talkgadget/oauth/chrome-remote-desktop/dev"
51 "&response_type=code"
52 "&client_id=%s"
53 "&access_type=offline"
54 "&approval_prompt=force",
55 net::EscapeUrlEncodedData(kAppRemotingAuthScopeValues, use_plus).c_str(),
56 net::EscapeUrlEncodedData(
57 google_apis::GetOAuth2ClientID(google_apis::CLIENT_REMOTING),
58 use_plus).c_str());
61 void PrintUsage() {
62 printf("\n**************************************\n");
63 printf("*** App Remoting Test Driver Usage ***\n");
64 printf("**************************************\n");
66 printf("\nUsage:\n");
67 printf(" ar_test_driver --username=<example@gmail.com> [options]\n");
68 printf("\nRequired Parameters:\n");
69 printf(" %s: Specifies which account to use when running tests\n",
70 switches::kUserNameSwitchName);
71 printf("\nOptional Parameters:\n");
72 printf(" %s: Exchanged for a refresh and access token for authentication\n",
73 switches::kAuthCodeSwitchName);
74 printf(" %s: Path to a JSON file containing username/refresh_token KVPs\n",
75 switches::kRefreshTokenFileSwitchName);
76 printf(" %s: Displays additional usage information\n",
77 switches::kHelpSwitchName);
78 printf(" %s: Specifies the service api to use (dev|test) [default: dev]\n",
79 switches::kServiceEnvironmentSwitchName);
80 printf(
81 " %s: Retrieves and displays the connection status for all known "
82 "hosts, no tests will be run\n",
83 switches::kShowHostAvailabilitySwitchName);
84 printf(
85 " %s: Send a message to the service after all tests have been run to "
86 "release remote hosts the tool used for testing.\n",
87 switches::kReleaseHostsAfterTestingSwitchName);
88 printf(
89 " %s: Specifies the optional logging level of the tool (0-3)."
90 " [default: off]\n",
91 switches::kLoggingLevelSwitchName);
94 void PrintAuthCodeInfo() {
95 printf("\n*******************************\n");
96 printf("*** Auth Code Example Usage ***\n");
97 printf("*******************************\n\n");
99 printf("If this is the first time you are running the tool,\n");
100 printf("you will need to provide an authorization code.\n");
101 printf("This code will be exchanged for a long term refresh token which\n");
102 printf("will be stored locally and used to acquire a short lived access\n");
103 printf("token to connect to the remoting service apis and establish a\n");
104 printf("remote host connection.\n\n");
106 printf("Note: You may need to repeat this step if the stored refresh token");
107 printf("\n has been revoked or expired.\n");
108 printf(" Passing in the same auth code twice will result in an error\n");
110 printf(
111 "\nFollow these steps to produce an auth code:\n"
112 " - Open the Authorization URL link shown below in your browser\n"
113 " - Approve the requested permissions for the tool\n"
114 " - Copy the 'code' value in the redirected URL\n"
115 " - Run the tool and pass in copied auth code as a parameter\n");
117 printf("\nAuthorization URL:\n");
118 printf("%s\n", GetAuthorizationCodeUri().c_str());
120 printf("\nRedirected URL Example:\n");
121 printf(
122 "https://chromoting-oauth.talkgadget.google.com/talkgadget/oauth/"
123 "chrome-remote-desktop/dev?code=4/AKtf...\n");
125 printf("\nTool usage example with the newly created auth code:\n");
126 printf("ar_test_driver --%s=example@gmail.com --%s=4/AKtf...\n\n",
127 switches::kUserNameSwitchName, switches::kAuthCodeSwitchName);
130 void PrintJsonFileInfo() {
131 printf("\n*****************************************\n");
132 printf("*** Refresh Token File Example Usage ***\n");
133 printf("****************************************\n\n");
135 printf("In order to use this option, a valid JSON file must exist, be\n");
136 printf("properly formatted, and contain a username/token KVP.\n");
137 printf("Contents of example_file.json\n");
138 printf("{\n");
139 printf(" \"username1@fauxdomain.com\": \"1/3798Gsdf898shksdvfyi8sshad\",\n");
140 printf(" \"username2@fauxdomain.com\": \"1/8974sdf87asdgadfgaerhfRsAa\",\n");
141 printf("}\n\n");
143 printf("\nTool usage example:\n");
144 printf("ar_test_driver --%s=%s --%s=./example_file.json\n\n",
145 switches::kUserNameSwitchName, "username1@fauxdomain.com",
146 switches::kRefreshTokenFileSwitchName);
149 // This class exists so that we can create a test suite which does not create
150 // its own AtExitManager. The problem we are working around occurs when
151 // the test suite does not create an AtExitManager (e.g. if no tests are run)
152 // and the environment object destroys its MessageLoop, then a crash will occur.
153 class NoAtExitBaseTestSuite : public base::TestSuite {
154 public:
155 NoAtExitBaseTestSuite(int argc, char** argv)
156 : base::TestSuite(argc, argv, false) {}
158 static int RunTestSuite(int argc, char** argv) {
159 return NoAtExitBaseTestSuite(argc, argv).Run();
163 } // namespace
165 int main(int argc, char** argv) {
166 base::AtExitManager at_exit;
167 base::MessageLoopForIO message_loop;
168 testing::InitGoogleTest(&argc, argv);
170 if (!base::CommandLine::InitializedForCurrentProcess()) {
171 if (!base::CommandLine::Init(argc, argv)) {
172 LOG(ERROR) << "Failed to initialize command line singleton.";
173 return -1;
177 // The pointer returned here refers to a singleton, since we don't own the
178 // lifetime of the object, don't wrap in a scoped_ptr construct or release it.
179 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
180 DCHECK(command_line);
182 // We do not want to retry failures as a failed test should signify an error
183 // to be investigated.
184 command_line->AppendSwitchASCII(switches::kTestLauncherRetryLimit, "0");
186 // We do not want to run the tests in parallel and we do not want to retry
187 // failures. The reason for running in a single process is that some tests
188 // may share the same remoting host and they cannot be run concurrently, also
189 // the test output gets spammed with test launcher messages which reduces the
190 // readability of the results.
191 command_line->AppendSwitch(switches::kSingleProcessTestsSwitchName);
193 // If the user passed in the help flag, then show the help info for this tool
194 // and 'run' the tests which will print the gtest specific help and then exit.
195 // NOTE: We do this check after updating the switches as otherwise the gtest
196 // help is written in parallel with our text and can appear interleaved.
197 if (command_line->HasSwitch(switches::kHelpSwitchName)) {
198 PrintUsage();
199 PrintJsonFileInfo();
200 PrintAuthCodeInfo();
201 return base::LaunchUnitTestsSerially(
202 argc, argv,
203 base::Bind(&NoAtExitBaseTestSuite::RunTestSuite, argc, argv));
206 remoting::test::AppRemotingTestDriverEnvironment::EnvironmentOptions options;
208 // Verify we received the required input from the command line.
209 options.user_name =
210 command_line->GetSwitchValueASCII(switches::kUserNameSwitchName);
211 if (options.user_name.empty()) {
212 LOG(ERROR) << "No user name passed in, can't authenticate without that!";
213 PrintUsage();
214 return -1;
216 VLOG(1) << "Running tests as: " << options.user_name;
218 // Check to see if the user passed in a one time use auth_code for
219 // refreshing their credentials.
220 std::string auth_code(
221 command_line->GetSwitchValueASCII(switches::kAuthCodeSwitchName));
223 options.refresh_token_file_path =
224 command_line->GetSwitchValuePath(switches::kRefreshTokenFileSwitchName);
226 options.release_hosts_when_done =
227 command_line->HasSwitch(switches::kReleaseHostsAfterTestingSwitchName);
229 // If the user passed in a service environment, use it, otherwise set a
230 // default value.
231 std::string service_environment_switch(command_line->GetSwitchValueASCII(
232 switches::kServiceEnvironmentSwitchName));
233 if (service_environment_switch.empty() ||
234 service_environment_switch == "dev") {
235 options.service_environment =
236 remoting::test::ServiceEnvironment::kDeveloperEnvironment;
237 } else if (service_environment_switch == "test") {
238 options.service_environment =
239 remoting::test::ServiceEnvironment::kTestingEnvironment;
240 } else if (service_environment_switch == "staging") {
241 options.service_environment =
242 remoting::test::ServiceEnvironment::kStagingEnvironment;
243 } else {
244 LOG(ERROR) << "Invalid " << switches::kServiceEnvironmentSwitchName
245 << " argument passed in.";
246 PrintUsage();
247 return -1;
250 // Update the logging verbosity level is user specified one.
251 std::string verbosity_level(
252 command_line->GetSwitchValueASCII(switches::kLoggingLevelSwitchName));
253 if (!verbosity_level.empty()) {
254 // Turn on logging for the test_driver and remoting components.
255 // This switch is parsed during logging::InitLogging.
256 command_line->AppendSwitchASCII("vmodule",
257 "*/remoting/*=" + verbosity_level);
258 logging::LoggingSettings logging_settings;
259 logging::InitLogging(logging_settings);
262 // Create and register our global test data object. It will handle
263 // retrieving an access token for the user and spinning up VMs.
264 // The GTest framework will own the lifetime of this object once
265 // it is registered below.
266 scoped_ptr<remoting::test::AppRemotingTestDriverEnvironment> shared_data(
267 new remoting::test::AppRemotingTestDriverEnvironment(options));
269 if (!shared_data->Initialize(auth_code)) {
270 // If we failed to initialize our shared data object, then bail.
271 return -1;
274 if (command_line->HasSwitch(switches::kShowHostAvailabilitySwitchName)) {
275 // When this flag is specified, we will retrieve connection information
276 // for all known applications and report the status. Tests can be skipped
277 // using a gtest_filter flag.
278 shared_data->ShowHostAvailability();
281 // Since we've successfully set up our shared_data object, we'll assign the
282 // value to our global* and transfer ownership to the framework.
283 remoting::test::AppRemotingSharedData = shared_data.release();
284 testing::AddGlobalTestEnvironment(remoting::test::AppRemotingSharedData);
286 // Because many tests may access the same remoting host(s), we need to run
287 // the tests sequentially so they do not interfere with each other.
288 return base::LaunchUnitTestsSerially(
289 argc, argv, base::Bind(&NoAtExitBaseTestSuite::RunTestSuite, argc, argv));