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 "remoting/test/app_remoting_connected_client_fixture.h"
7 #include "base/json/json_reader.h"
8 #include "base/logging.h"
9 #include "base/run_loop.h"
10 #include "base/thread_task_runner_handle.h"
11 #include "base/timer/timer.h"
12 #include "base/values.h"
13 #include "remoting/protocol/host_stub.h"
14 #include "remoting/test/app_remoting_test_driver_environment.h"
15 #include "remoting/test/remote_application_details.h"
16 #include "remoting/test/test_chromoting_client.h"
19 const int kDefaultDPI
= 96;
20 const int kDefaultWidth
= 1024;
21 const int kDefaultHeight
= 768;
23 const char kHostProcessWindowTitle
[] = "Host Process";
25 void SimpleHostMessageHandler(
26 const std::string
& target_message_type
,
27 const std::string
& target_message_data
,
28 const base::Closure
& done_closure
,
29 bool* message_received
,
30 const remoting::protocol::ExtensionMessage
& message
) {
31 if (message
.type() == target_message_type
&&
32 message
.data() == target_message_data
) {
33 *message_received
= true;
42 AppRemotingConnectedClientFixture::AppRemotingConnectedClientFixture()
43 : application_details_(
44 AppRemotingSharedData
->GetDetailsFromAppName(GetParam())),
45 connection_is_ready_for_tests_(false),
46 timer_(new base::Timer(true, false)) {
49 AppRemotingConnectedClientFixture::~AppRemotingConnectedClientFixture() {
52 void AppRemotingConnectedClientFixture::SetUp() {
53 message_loop_
.reset(new base::MessageLoopForIO
);
55 client_
.reset(new TestChromotingClient());
56 client_
->AddRemoteConnectionObserver(this);
60 if (!connection_is_ready_for_tests_
) {
61 FAIL() << "Remote host connection could not be established.";
62 client_
->EndConnection();
66 void AppRemotingConnectedClientFixture::TearDown() {
67 // |client_| must be destroyed before |message_loop_| as some of its
68 // members are destroyed via DeleteSoon on the message loop's TaskRunner.
69 client_
->RemoveRemoteConnectionObserver(this);
72 base::RunLoop().RunUntilIdle();
74 message_loop_
.reset();
77 bool AppRemotingConnectedClientFixture::VerifyResponseForSimpleHostMessage(
78 const std::string
& message_request_title
,
79 const std::string
& message_response_title
,
80 const std::string
& message_payload
,
81 const base::TimeDelta
& max_wait_time
) {
82 DCHECK(thread_checker_
.CalledOnValidThread());
84 bool message_received
= false;
86 DCHECK(!run_loop_
|| !run_loop_
->running());
87 run_loop_
.reset(new base::RunLoop());
89 host_message_received_callback_
=
90 base::Bind(&SimpleHostMessageHandler
, message_response_title
,
91 message_payload
, run_loop_
->QuitClosure(), &message_received
);
93 protocol::ExtensionMessage message
;
94 message
.set_type(message_request_title
);
95 message
.set_data(message_payload
);
96 client_
->host_stub()->DeliverClientMessage(message
);
98 DCHECK(!timer_
->IsRunning());
99 timer_
->Start(FROM_HERE
, max_wait_time
, run_loop_
->QuitClosure());
104 host_message_received_callback_
.Reset();
106 return message_received
;
109 void AppRemotingConnectedClientFixture::StartConnection() {
110 DCHECK(thread_checker_
.CalledOnValidThread());
112 RemoteHostInfo remote_host_info
;
113 remoting::test::AppRemotingSharedData
->GetRemoteHostInfoForApplicationId(
114 application_details_
.application_id
, &remote_host_info
);
116 if (!remote_host_info
.IsReadyForConnection()) {
117 LOG(ERROR
) << "Remote Host is unavailable for connections.";
121 DCHECK(!run_loop_
|| !run_loop_
->running());
122 run_loop_
.reset(new base::RunLoop());
124 // We will wait up to 30 seconds to complete the remote connection and for the
125 // main application window to become visible.
126 DCHECK(!timer_
->IsRunning());
127 timer_
->Start(FROM_HERE
, base::TimeDelta::FromSeconds(30),
128 run_loop_
->QuitClosure());
130 client_
->StartConnection(AppRemotingSharedData
->user_name(),
131 AppRemotingSharedData
->access_token(),
138 void AppRemotingConnectedClientFixture::ConnectionStateChanged(
139 protocol::ConnectionToHost::State state
,
140 protocol::ErrorCode error_code
) {
141 DCHECK(thread_checker_
.CalledOnValidThread());
143 // If the connection is closed or failed then mark the connection as closed
144 // and quit the current RunLoop if it exists.
145 if (state
== protocol::ConnectionToHost::CLOSED
||
146 state
== protocol::ConnectionToHost::FAILED
||
147 error_code
!= protocol::OK
) {
148 connection_is_ready_for_tests_
= false;
156 void AppRemotingConnectedClientFixture::ConnectionReady(bool ready
) {
157 DCHECK(thread_checker_
.CalledOnValidThread());
160 SendClientConnectionDetailsToHost();
162 // We will only get called here with a false value for |ready| if the video
163 // renderer encounters an error.
164 connection_is_ready_for_tests_
= false;
172 void AppRemotingConnectedClientFixture::HostMessageReceived(
173 const protocol::ExtensionMessage
& message
) {
174 DCHECK(thread_checker_
.CalledOnValidThread());
176 // If a callback is not registered, then the message is passed to a default
177 // handler for the class based on the message type.
178 if (!host_message_received_callback_
.is_null()) {
179 host_message_received_callback_
.Run(message
);
180 } else if (message
.type() == "onWindowAdded") {
181 HandleOnWindowAddedMessage(message
);
183 DVLOG(2) << "HostMessage not handled by HostMessageReceived().";
184 DVLOG(2) << "type: " << message
.type();
185 DVLOG(2) << "data: " << message
.data();
189 void AppRemotingConnectedClientFixture::SendClientConnectionDetailsToHost() {
190 // First send an access token which will be used for Google Drive access.
191 protocol::ExtensionMessage message
;
192 message
.set_type("accessToken");
193 message
.set_data(AppRemotingSharedData
->access_token());
195 DVLOG(1) << "Sending access token to host";
196 client_
->host_stub()->DeliverClientMessage(message
);
198 // Next send the host a description of the client screen size.
199 protocol::ClientResolution client_resolution
;
200 client_resolution
.set_width(kDefaultWidth
);
201 client_resolution
.set_height(kDefaultHeight
);
202 client_resolution
.set_x_dpi(kDefaultDPI
);
203 client_resolution
.set_y_dpi(kDefaultDPI
);
204 client_resolution
.set_dips_width(kDefaultWidth
);
205 client_resolution
.set_dips_height(kDefaultHeight
);
207 DVLOG(1) << "Sending ClientResolution details to host";
208 client_
->host_stub()->NotifyClientResolution(client_resolution
);
210 // Finally send a message to start sending us video packets.
211 protocol::VideoControl video_control
;
212 video_control
.set_enable(true);
214 DVLOG(1) << "Sending enable VideoControl message to host";
215 client_
->host_stub()->ControlVideo(video_control
);
218 void AppRemotingConnectedClientFixture::HandleOnWindowAddedMessage(
219 const remoting::protocol::ExtensionMessage
& message
) {
220 DCHECK_EQ(message
.type(), "onWindowAdded");
222 const base::DictionaryValue
* message_data
= nullptr;
223 scoped_ptr
<base::Value
> host_message(base::JSONReader::Read(message
.data()));
224 if (!host_message
.get() || !host_message
->GetAsDictionary(&message_data
)) {
225 LOG(ERROR
) << "onWindowAdded message received was not valid JSON.";
232 std::string current_window_title
;
233 message_data
->GetString("title", ¤t_window_title
);
234 if (current_window_title
== kHostProcessWindowTitle
) {
235 LOG(ERROR
) << "Host Process Window is visible, this likely means that the "
236 << "underlying application is in a bad state, YMMV.";
239 std::string main_window_title
= application_details_
.main_window_title
;
240 if (current_window_title
.find_first_of(main_window_title
) == 0) {
241 connection_is_ready_for_tests_
= true;
243 if (timer_
->IsRunning()) {
248 // Now that the main window is visible, give the app some time to settle
249 // before signaling that it is ready to run tests.
250 timer_
->Start(FROM_HERE
, base::TimeDelta::FromSeconds(2),
251 run_loop_
->QuitClosure());
256 } // namespace remoting