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/files/file_path.h"
8 #include "base/logging.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/strings/stringprintf.h"
11 #include "base/test/launcher/unit_test_launcher.h"
12 #include "base/test/test_suite.h"
13 #include "base/test/test_switches.h"
14 #include "google_apis/google_api_keys.h"
15 #include "net/base/escape.h"
16 #include "remoting/test/chromoting_test_driver_environment.h"
17 #include "testing/gtest/include/gtest/gtest.h"
20 const char kAuthCodeSwitchName
[] = "authcode";
21 const char kHelpSwitchName
[] = "help";
22 const char kHostNameSwitchName
[] = "hostname";
23 const char kLoggingLevelSwitchName
[] = "verbosity";
24 const char kPinSwitchName
[] = "pin";
25 const char kRefreshTokenPathSwitchName
[] = "refresh-token-path";
26 const char kSingleProcessTestsSwitchName
[] = "single-process-tests";
27 const char kUserNameSwitchName
[] = "username";
31 const char kChromotingAuthScopeValues
[] =
32 "https://www.googleapis.com/auth/chromoting "
33 "https://www.googleapis.com/auth/googletalk "
34 "https://www.googleapis.com/auth/userinfo.email";
36 std::string
GetAuthorizationCodeUri() {
37 // Replace space characters with a '+' sign when formatting.
39 return base::StringPrintf(
40 "https://accounts.google.com/o/oauth2/auth"
42 "&redirect_uri=https://chromoting-oauth.talkgadget.google.com/"
43 "talkgadget/oauth/chrome-remote-desktop/dev"
46 "&access_type=offline"
47 "&approval_prompt=force",
48 net::EscapeUrlEncodedData(kChromotingAuthScopeValues
, use_plus
).c_str(),
49 net::EscapeUrlEncodedData(
50 google_apis::GetOAuth2ClientID(google_apis::CLIENT_REMOTING
),
55 printf("\n************************************\n");
56 printf("*** Chromoting Test Driver Usage ***\n");
57 printf("************************************\n");
60 printf(" chromoting_test_driver --username=<example@gmail.com> [options]"
61 " --hostname=<example hostname>\n");
62 printf("\nRequired Parameters:\n");
63 printf(" %s: Specifies which account to use when running tests\n",
64 switches::kUserNameSwitchName
);
65 printf(" %s: Specifies which host to connect to when running tests\n",
66 switches::kHostNameSwitchName
);
67 printf("\nOptional Parameters:\n");
68 printf(" %s: Exchanged for a refresh and access token for authentication\n",
69 switches::kAuthCodeSwitchName
);
70 printf(" %s: Displays additional usage information\n",
71 switches::kHelpSwitchName
);
72 printf(" %s: Path to a JSON file containing username/refresh_token KVPs\n",
73 switches::kRefreshTokenPathSwitchName
);
74 printf(" %s: Specifies the optional logging level of the tool (0-3)."
75 " [default: off]\n", switches::kLoggingLevelSwitchName
);
78 void PrintAuthCodeInfo() {
79 printf("\n*******************************\n");
80 printf("*** Auth Code Example Usage ***\n");
81 printf("*******************************\n\n");
83 printf("If this is the first time you are running the tool,\n");
84 printf("you will need to provide an authorization code.\n");
85 printf("This code will be exchanged for a long term refresh token which\n");
86 printf("will be stored locally and used to acquire a short lived access\n");
87 printf("token to connect to the remoting service apis and establish a\n");
88 printf("remote host connection.\n\n");
90 printf("Note: You may need to repeat this step if the stored refresh token");
91 printf("\n has been revoked or expired.\n");
92 printf(" Passing in the same auth code twice will result in an error\n");
94 printf("\nFollow these steps to produce an auth code:\n"
95 " - Open the Authorization URL link shown below in your browser\n"
96 " - Approve the requested permissions for the tool\n"
97 " - Copy the 'code' value in the redirected URL\n"
98 " - Run the tool and pass in copied auth code as a parameter\n");
100 printf("\nAuthorization URL:\n");
101 printf("%s\n", GetAuthorizationCodeUri().c_str());
103 printf("\nRedirected URL Example:\n");
104 printf("https://chromoting-oauth.talkgadget.google.com/talkgadget/oauth/"
105 "chrome-remote-desktop/dev?code=4/AKtf...\n");
107 printf("\nTool usage example with the newly created auth code:\n");
108 printf("chromoting_test_driver --%s=example@gmail.com --%s=example_host_name"
109 " --%s=4/AKtf...\n\n",
110 switches::kUserNameSwitchName
,
111 switches::kHostNameSwitchName
,
112 switches::kAuthCodeSwitchName
);
115 void PrintJsonFileInfo() {
116 printf("\n****************************************\n");
117 printf("*** Refresh Token File Example Usage ***\n");
118 printf("****************************************\n\n");
120 printf("In order to use this option, a valid JSON file must exist, be\n");
121 printf("properly formatted, and contain a username/token KVP.\n");
122 printf("Contents of example_file.json\n");
124 printf(" \"username1@fauxdomain.com\": \"1/3798Gsdf898shksdvfyi8sshad\",\n");
125 printf(" \"username2@fauxdomain.com\": \"1/8974sdf87asdgadfgaerhfRsAa\",\n");
128 printf("\nTool usage example:\n");
129 printf("chromoting_test_driver --%s=%s --%s=example_host_name"
130 " --%s=./example_file.json\n\n",
131 switches::kUserNameSwitchName
, "username1@fauxdomain.com",
132 switches::kHostNameSwitchName
, switches::kRefreshTokenPathSwitchName
);
135 // This class exists so that we can create a test suite which does not create
136 // its own AtExitManager. The problem we are working around occurs when
137 // the test suite does not create an AtExitManager (e.g. if no tests are run)
138 // and the environment object destroys its MessageLoop, then a crash will occur.
139 class NoAtExitBaseTestSuite
: public base::TestSuite
{
141 NoAtExitBaseTestSuite(int argc
, char** argv
)
142 : base::TestSuite(argc
, argv
, false) {}
144 static int RunTestSuite(int argc
, char** argv
) {
145 return NoAtExitBaseTestSuite(argc
, argv
).Run();
151 int main(int argc
, char* argv
[]) {
152 base::AtExitManager at_exit
;
153 base::MessageLoopForIO message_loop
;
154 testing::InitGoogleTest(&argc
, argv
);
156 if (!base::CommandLine::InitializedForCurrentProcess()) {
157 if (!base::CommandLine::Init(argc
, argv
)) {
158 LOG(ERROR
) << "Failed to initialize command line singleton.";
163 base::CommandLine
* command_line
= base::CommandLine::ForCurrentProcess();
164 DCHECK(command_line
);
166 // Do not retry if tests fails.
167 command_line
->AppendSwitchASCII(switches::kTestLauncherRetryLimit
, "0");
169 // Different tests may require access to the same host if run in parallel.
170 // To avoid shared resource contention, tests will be run one at a time.
171 command_line
->AppendSwitch(switches::kSingleProcessTestsSwitchName
);
173 // If the user passed in the help flag, then show the help info for this tool
174 // and 'run' the tests which will print the gtest specific help and then exit.
175 // NOTE: We do this check after updating the switches as otherwise the gtest
176 // help is written in parallel with our text and can appear interleaved.
177 if (command_line
->HasSwitch(switches::kHelpSwitchName
)) {
181 return base::LaunchUnitTestsSerially(
182 argc
, argv
, base::Bind(&NoAtExitBaseTestSuite::RunTestSuite
, argc
, argv
));
185 // Update the logging verbosity level if user specified one.
186 std::string
verbosity_level(
187 command_line
->GetSwitchValueASCII(switches::kLoggingLevelSwitchName
));
188 if (!verbosity_level
.empty()) {
189 // Turn on logging for the test_driver and remoting components.
190 // This switch is parsed during logging::InitLogging.
191 command_line
->AppendSwitchASCII("vmodule",
192 "*/remoting/*=" + verbosity_level
);
193 logging::LoggingSettings logging_settings
;
194 logging::InitLogging(logging_settings
);
197 remoting::test::ChromotingTestDriverEnvironment::EnvironmentOptions options
;
200 command_line
->GetSwitchValueASCII(switches::kUserNameSwitchName
);
201 if (options
.user_name
.empty()) {
202 LOG(ERROR
) << "No username passed in, can't authenticate or run tests!";
205 VLOG(1) << "Running chromoting tests as: " << options
.user_name
;
207 // Check to see if the user passed in a one time use auth_code for
208 // refreshing their credentials.
209 std::string auth_code
=
210 command_line
->GetSwitchValueASCII(switches::kAuthCodeSwitchName
);
211 options
.refresh_token_file_path
=
212 command_line
->GetSwitchValuePath(switches::kRefreshTokenPathSwitchName
);
214 // The host name determines which host to initiate a session with from the
215 // host list returned from the directory service.
217 command_line
->GetSwitchValueASCII(switches::kHostNameSwitchName
);
219 if (options
.host_name
.empty()) {
220 LOG(ERROR
) << "No hostname passed in, connect to host requires hostname!";
223 VLOG(1) << "Chromoting tests will connect to: " << options
.host_name
;
225 options
.pin
= command_line
->GetSwitchValueASCII(switches::kPinSwitchName
);
227 // Create and register our global test data object. It will handle
228 // retrieving an access token or host list for the user. The GTest framework
229 // will own the lifetime of this object once it is registered below.
230 scoped_ptr
<remoting::test::ChromotingTestDriverEnvironment
> shared_data(
231 new remoting::test::ChromotingTestDriverEnvironment(options
));
233 if (!shared_data
->Initialize(auth_code
)) {
234 // If we failed to initialize our shared data object, then bail.
238 // Since we've successfully set up our shared_data object, we'll assign the
239 // value to our global* and transfer ownership to the framework.
240 remoting::test::g_chromoting_shared_data
= shared_data
.release();
241 testing::AddGlobalTestEnvironment(remoting::test::g_chromoting_shared_data
);
243 // Running the tests serially will avoid clients from connecting to the same
245 return base::LaunchUnitTestsSerially(
246 argc
, argv
, base::Bind(&NoAtExitBaseTestSuite::RunTestSuite
, argc
, argv
));