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.
6 #include "base/command_line.h"
7 #include "base/logging.h"
8 #include "base/strings/stringprintf.h"
9 #include "base/test/launcher/unit_test_launcher.h"
10 #include "base/test/test_suite.h"
11 #include "base/test/test_switches.h"
12 #include "google_apis/google_api_keys.h"
13 #include "net/base/escape.h"
14 #include "remoting/test/app_remoting_test_driver_environment.h"
15 #include "testing/gtest/include/gtest/gtest.h"
18 const char kAuthCodeSwitchName
[] = "authcode";
19 const char kHelpSwitchName
[] = "help";
20 const char kLoggingLevelSwitchName
[] = "verbosity";
21 const char kServiceEnvironmentSwitchName
[] = "environment";
22 const char kShowHostAvailabilitySwitchName
[] = "show-host-availability";
23 const char kSingleProcessTestsSwitchName
[] = "single-process-tests";
24 const char kUserNameSwitchName
[] = "username";
29 // Requested permissions needed for App Remoting tests. The spaces in between
30 // scope fragments are necessary and will be escaped properly before use.
31 const char kAppRemotingAuthScopeValues
[] =
32 "https://www.googleapis.com/auth/appremoting.runapplication"
33 " https://www.googleapis.com/auth/googletalk"
34 " https://www.googleapis.com/auth/userinfo.email"
35 " https://docs.google.com/feeds"
36 " https://www.googleapis.com/auth/drive";
38 std::string
GetAuthorizationCodeUri() {
39 // Replace space characters with a '+' sign when formatting.
41 return base::StringPrintf(
42 "https://accounts.google.com/o/oauth2/auth"
44 "&redirect_uri=https://chromoting-oauth.talkgadget.google.com/"
45 "talkgadget/oauth/chrome-remote-desktop/dev"
48 "&access_type=offline"
49 "&approval_prompt=force",
50 net::EscapeUrlEncodedData(kAppRemotingAuthScopeValues
, use_plus
).c_str(),
51 net::EscapeUrlEncodedData(
52 google_apis::GetOAuth2ClientID(google_apis::CLIENT_REMOTING
),
57 printf("\n**************************************\n");
58 printf("*** App Remoting Test Driver Usage ***\n");
59 printf("**************************************\n");
62 printf(" ar_test_driver --username=<example@gmail.com> [options]\n");
63 printf("\nRequired Parameters:\n");
64 printf(" %s: Specifies which account to use when running tests\n",
65 switches::kUserNameSwitchName
);
66 printf("\nOptional Parameters:\n");
67 printf(" %s: Exchanged for a refresh and access token for authentication\n",
68 switches::kAuthCodeSwitchName
);
69 printf(" %s: Displays additional usage information\n",
70 switches::kHelpSwitchName
);
71 printf(" %s: Specifies the service api to use (dev|test) [default: dev]\n",
72 switches::kServiceEnvironmentSwitchName
);
74 " %s: Retrieves and displays the connection status for all known "
75 "hosts, no tests will be run\n",
76 switches::kShowHostAvailabilitySwitchName
);
78 " %s: Specifies the optional logging level of the tool (0-3)."
80 switches::kLoggingLevelSwitchName
);
83 void PrintAuthCodeInfo() {
84 printf("\n*******************************\n");
85 printf("*** Auth Code Example Usage ***\n");
86 printf("*******************************\n\n");
88 printf("If this is the first time you are running the tool,\n");
89 printf("you will need to provide an authorization code.\n");
90 printf("This code will be exchanged for a long term refresh token which\n");
91 printf("will be stored locally and used to acquire a short lived access\n");
92 printf("token to connect to the remoting service apis and establish a\n");
93 printf("remote host connection.\n\n");
95 printf("Note: You may need to repeat this step if the stored refresh token");
96 printf("\n has been revoked or expired.\n");
97 printf(" Passing in the same auth code twice will result in an error\n");
100 "\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");
111 "https://chromoting-oauth.talkgadget.google.com/talkgadget/oauth/"
112 "chrome-remote-desktop/dev?code=4/AKtf...\n");
114 printf("\nTool usage example with the newly created auth code:\n");
115 printf("ar_test_driver --%s=example@gmail.com --%s=4/AKtf...\n\n",
116 switches::kUserNameSwitchName
, switches::kAuthCodeSwitchName
);
121 int main(int argc
, char** argv
) {
122 testing::InitGoogleTest(&argc
, argv
);
123 TestSuite
test_suite(argc
, argv
);
125 // The pointer returned here refers to a singleton, since we don't own the
126 // lifetime of the object, don't wrap in a scoped_ptr construct or release it.
127 base::CommandLine
* command_line
= base::CommandLine::ForCurrentProcess();
128 DCHECK(command_line
);
130 // We do not want to retry failures as a failed test should signify an error
131 // to be investigated.
132 command_line
->AppendSwitchASCII(switches::kTestLauncherRetryLimit
, "0");
134 // We do not want to run the tests in parallel and we do not want to retry
135 // failures. The reason for running in a single process is that some tests
136 // may share the same remoting host and they cannot be run concurrently, also
137 // the test output gets spammed with test launcher messages which reduces the
138 // readability of the results.
139 command_line
->AppendSwitch(switches::kSingleProcessTestsSwitchName
);
141 // If the user passed in the help flag, then show the help info for this tool
142 // and 'run' the tests which will print the gtest specific help and then exit.
143 // NOTE: We do this check after updating the switches as otherwise the gtest
144 // help is written in parallel with our text and can appear interleaved.
145 if (command_line
->HasSwitch(switches::kHelpSwitchName
)) {
148 return base::LaunchUnitTestsSerially(
150 base::Bind(&base::TestSuite::Run
, base::Unretained(&test_suite
)));
153 // Verify we received the required input from the command line.
154 if (!command_line
->HasSwitch(switches::kUserNameSwitchName
)) {
155 LOG(ERROR
) << "No user name passed in, can't authenticate without that!";
160 std::string user_name
;
161 user_name
= command_line
->GetSwitchValueASCII(switches::kUserNameSwitchName
);
162 DVLOG(1) << "Running tests as: " << user_name
;
164 std::string auth_code
;
165 // Check to see if the user passed in a one time use auth_code for
166 // refreshing their credentials.
167 auth_code
= command_line
->GetSwitchValueASCII(switches::kAuthCodeSwitchName
);
169 // If the user passed in a service environment, use it, otherwise set a
171 remoting::test::ServiceEnvironment service_environment
;
172 std::string service_environment_switch
= command_line
->GetSwitchValueASCII(
173 switches::kServiceEnvironmentSwitchName
);
174 if (service_environment_switch
.empty() ||
175 service_environment_switch
== "dev") {
176 service_environment
=
177 remoting::test::ServiceEnvironment::kDeveloperEnvironment
;
178 } else if (service_environment_switch
== "test") {
179 service_environment
=
180 remoting::test::ServiceEnvironment::kTestingEnvironment
;
182 LOG(ERROR
) << "Invalid " << switches::kServiceEnvironmentSwitchName
183 << " argument passed in.";
188 // Update the logging verbosity level is user specified one.
189 std::string verbosity_level
;
191 command_line
->GetSwitchValueASCII(switches::kLoggingLevelSwitchName
);
192 if (!verbosity_level
.empty()) {
193 // Turn on logging for the test_driver and remoting components.
194 // This switch is parsed during logging::InitLogging.
195 command_line
->AppendSwitchASCII("vmodule",
196 "*/remoting/*=" + verbosity_level
);
197 logging::LoggingSettings logging_settings
;
198 logging::InitLogging(logging_settings
);
201 // Create and register our global test data object. It will handle
202 // retrieving an access token for the user and spinning up VMs.
203 // The GTest framework will own the lifetime of this object once
204 // it is registered below.
205 scoped_ptr
<remoting::test::AppRemotingTestDriverEnvironment
> shared_data
;
207 shared_data
.reset(new remoting::test::AppRemotingTestDriverEnvironment(
208 user_name
, service_environment
));
210 if (!shared_data
->Initialize(auth_code
)) {
211 // If we failed to initialize our shared data object, then bail.
215 if (command_line
->HasSwitch(switches::kShowHostAvailabilitySwitchName
)) {
216 // When this flag is specified, we will retrieve connection information
217 // for all known applications and report the status. No tests will be run.
218 shared_data
->ShowHostAvailability();
222 // Since we've successfully set up our shared_data object, we'll assign the
223 // value to our global* and transfer ownership to the framework.
224 remoting::test::AppRemotingSharedData
= shared_data
.release();
225 testing::AddGlobalTestEnvironment(remoting::test::AppRemotingSharedData
);
227 // Because many tests may access the same remoting host(s), we need to run
228 // the tests sequentially so they do not interfere with each other.
229 return base::LaunchUnitTestsSerially(
231 base::Bind(&base::TestSuite::Run
, base::Unretained(&test_suite
)));