Index: remoting/test/app_remoting_connected_client_tests.cc |
diff --git a/remoting/test/app_remoting_connected_client_tests.cc b/remoting/test/app_remoting_connected_client_tests.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..cd10085fa342a2937f2440341f01b323f5a4b70a |
--- /dev/null |
+++ b/remoting/test/app_remoting_connected_client_tests.cc |
@@ -0,0 +1,256 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "remoting/test/app_remoting_connected_client_tests.h" |
+ |
+#include "base/json/json_reader.h" |
+#include "base/logging.h" |
+#include "base/run_loop.h" |
+#include "base/thread_task_runner_handle.h" |
+#include "base/values.h" |
+#include "remoting/protocol/host_stub.h" |
+#include "remoting/test/app_remoting_test_driver_environment.h" |
+#include "remoting/test/remote_application_data.h" |
+#include "remoting/test/test_chromoting_client.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+namespace { |
+const int kDefaultDPI = 96; |
+const int kDefaultWidth = 1024; |
+const int kDefaultHeight = 768; |
+ |
+void SimpleHostMessageReceivedHandler( |
+ const std::string& target_message_type, |
+ const std::string& target_message_data, |
+ const base::Closure& done_closure, |
+ bool* message_received, |
+ const remoting::protocol::ExtensionMessage& message) { |
+ if (message.type() == target_message_type && |
+ message.data() == target_message_data) { |
+ *message_received = true; |
+ done_closure.Run(); |
+ } |
+} |
+} |
Wez
2015/03/16 22:19:08
nit: This is a non-trivial namespace declaration,
joedow
2015/03/18 20:13:08
Done.
|
+ |
+namespace remoting { |
+namespace test { |
+ |
+scoped_ptr<base::MessageLoopForIO> |
+ AppRemotingConnectedClientTests::message_loop_; |
Wez
2015/03/16 22:19:08
Does this line really need wrapping?
joedow
2015/03/18 20:13:08
It does :(
|
+scoped_ptr<TestChromotingClient> AppRemotingConnectedClientTests::client_; |
+bool AppRemotingConnectedClientTests::connection_is_ready_for_tests_; |
+ |
+AppRemotingConnectedClientTests::AppRemotingConnectedClientTests() |
+ : application_info_(GetRemoteApplicationInfoMap().at(GetParam())) { |
+} |
+ |
+AppRemotingConnectedClientTests::~AppRemotingConnectedClientTests() { |
+} |
+ |
+void AppRemotingConnectedClientTests::SetUp() { |
+ // NOTE: Code here will be called immediately after the constructor (right |
+ // before each test). |
Wez
2015/03/16 22:19:09
Which of these properties is the important one to
joedow
2015/03/18 20:13:08
Done. Removed since I got rid of the static metho
|
+ DCHECK(client_); |
Wez
2015/03/16 22:19:09
No point DCHECKing a pointer immediately before de
joedow
2015/03/18 20:13:08
Done.
|
+ client_->AddRemoteConnectionObserver(this); |
+ |
+ // If the client has not attempted to create a connection in the past, then |
+ // start one now. |
+ if (client_->connection_to_host_state() == |
+ protocol::ConnectionToHost::INITIALIZING && |
+ client_->connection_error_code() == protocol::OK) { |
+ StartConnection(); |
+ } |
+ |
+ if (!connection_is_ready_for_tests_) { |
+ FAIL() << "Remote host connection could not be completed."; |
+ client_->EndConnection(); |
+ } |
+} |
+ |
+void AppRemotingConnectedClientTests::TearDown() { |
+ // NOTE: Code here will be called immediately after each test (right |
+ // before the destructor). |
+ DCHECK(client_); |
Wez
2015/03/16 22:19:09
See above re DCHECK
joedow
2015/03/18 20:13:08
Done.
|
+ client_->RemoveRemoteConnectionObserver(this); |
+} |
+ |
+void AppRemotingConnectedClientTests::SetUpTestCase() { |
+ // NOTE: Code here will be called before any instance constructors are called. |
+ DCHECK(!message_loop_); |
+ message_loop_.reset(new base::MessageLoopForIO); |
+ |
+ DCHECK(!client_); |
+ client_.reset(new TestChromotingClient()); |
+} |
+ |
+void AppRemotingConnectedClientTests::TearDownTestCase() { |
+ // NOTE: Code here will be called immediately after all tests are done. |
+ connection_is_ready_for_tests_ = false; |
+ |
+ // The client must be destroyed before the message loop as some of its |
+ // members are destroyed via DeleteSoon on the message loop's TaskRunner. |
Wez
2015/03/16 22:19:09
Unless you actually run the message-loop after des
joedow
2015/03/18 20:13:08
I had thought this was happening in the D'Tor for
|
+ client_.reset(); |
+ message_loop_.reset(); |
+} |
+ |
+bool AppRemotingConnectedClientTests::VerifyResponseForSimpleHostMessage( |
+ const std::string& message_request_title, |
+ const std::string& message_response_title, |
+ const std::string& message_payload, |
+ const base::TimeDelta& max_wait_time) { |
+ base::RunLoop run_loop; |
+ bool message_received = false; |
+ |
+ done_closure_ = run_loop.QuitClosure(); |
Wez
2015/03/16 22:19:08
Why are you saving the QuitClosure() for a RunLoop
joedow
2015/03/18 20:13:09
Done.
|
+ host_message_received_callback_ = |
+ base::Bind(&SimpleHostMessageReceivedHandler, message_response_title, |
+ message_payload, done_closure_, &message_received); |
+ |
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(FROM_HERE, done_closure_, |
+ max_wait_time); |
Wez
2015/03/16 22:19:09
Cleaner to use a base::Timer for this?
joedow
2015/03/18 20:13:08
Done.
|
+ |
+ protocol::ExtensionMessage message; |
+ message.set_type(message_request_title); |
+ message.set_data(message_payload); |
+ client_->host_stub()->DeliverClientMessage(message); |
+ |
+ run_loop.Run(); |
+ host_message_received_callback_.Reset(); |
+ |
+ return message_received; |
+} |
+ |
+void AppRemotingConnectedClientTests::StartConnection() { |
+ DCHECK(client_); |
+ |
+ RemoteHostInfo remote_host_info; |
+ remoting::test::AppRemotingSharedData->GetRemoteHostInfoForApplicationId( |
+ application_info_.application_id, &remote_host_info); |
+ |
+ if (!remote_host_info.IsReadyForConnection()) { |
+ LOG(ERROR) << "Remote Host is unavailable for connections."; |
+ return; |
+ } |
+ |
+ base::RunLoop run_loop; |
+ |
+ done_closure_ = run_loop.QuitClosure(); |
Wez
2015/03/16 22:19:09
See above, re why this needs saving.
joedow
2015/03/18 20:13:08
Done.
|
+ host_message_received_callback_ = |
+ base::Bind(&AppRemotingConnectedClientTests::AddWindowMessageHandler, |
+ base::Unretained(this), application_info_.main_window_name); |
+ |
+ // We will wait up to 30 seconds to complete the remote connection and for the |
+ // main application window to become visible. |
+ base::TimeDelta wait_time = base::TimeDelta::FromSeconds(30); |
+ |
+ // Post the QuitClosure with a max timeout to ensure we don't run forever. |
+ // The QuitClosure will NOP if the connection was created before the timeout. |
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(FROM_HERE, done_closure_, |
+ wait_time); |
Wez
2015/03/16 22:19:09
See above re base::Timer
joedow
2015/03/18 20:13:08
Done.
|
+ |
+ client_->StartConnection(AppRemotingSharedData->user_name(), |
+ AppRemotingSharedData->access_token(), |
+ remote_host_info); |
+ |
+ run_loop.Run(); |
+ host_message_received_callback_.Reset(); |
+} |
+ |
+void AppRemotingConnectedClientTests::ConnectionStateChanged( |
Wez
2015/03/16 22:19:08
The RemoteConnectionObserver APIs are called by th
joedow
2015/03/18 20:13:08
Done.
|
+ protocol::ConnectionToHost::State state, |
+ protocol::ErrorCode error_code) { |
+ if (state != protocol::ConnectionToHost::CONNECTED || |
+ error_code != protocol::OK) { |
+ connection_is_ready_for_tests_ = false; |
+ } |
+ |
+ if (!done_closure_.is_null() && |
+ (state == protocol::ConnectionToHost::CLOSED || |
+ state == protocol::ConnectionToHost::FAILED || |
+ error_code != protocol::OK)) { |
Wez
2015/03/16 22:19:08
Why are these two connection-state checks differen
joedow
2015/03/18 20:13:08
Done.
|
+ done_closure_.Run(); |
+ } |
+} |
+ |
+void AppRemotingConnectedClientTests::ConnectionReady(bool ready) { |
Wez
2015/03/16 22:19:09
Under what circumstances will we receive this call
joedow
2015/03/18 20:13:08
Done.
|
+ if (ready) { |
+ SendClientConnectionDetailsToHost(); |
+ } |
+} |
+ |
+void AppRemotingConnectedClientTests::HostMessageReceived( |
+ const protocol::ExtensionMessage& message) { |
+ if (!host_message_received_callback_.is_null()) { |
Wez
2015/03/16 22:19:09
Do we really want to ignore host messages received
joedow
2015/03/18 20:13:08
I've updated this method so that we can add defaul
|
+ host_message_received_callback_.Run(message); |
+ } |
+} |
+ |
+void AppRemotingConnectedClientTests::SendClientConnectionDetailsToHost() { |
+ // First send an access token which will be used for GDrive access. |
+ protocol::ExtensionMessage message; |
+ message.set_type("accessToken"); |
+ message.set_data(AppRemotingSharedData->access_token()); |
+ |
+ DVLOG(1) << "Sending access token to host"; |
+ client_->host_stub()->DeliverClientMessage(message); |
+ |
+ // next send the host a description of the client screen size. |
Wez
2015/03/16 22:19:09
nit: Capitalization
joedow
2015/03/18 20:13:08
Done.
|
+ protocol::ClientResolution client_resolution; |
+ client_resolution.set_width(kDefaultWidth); |
+ client_resolution.set_height(kDefaultHeight); |
+ client_resolution.set_x_dpi(kDefaultDPI); |
+ client_resolution.set_y_dpi(kDefaultDPI); |
+ client_resolution.set_dips_width(kDefaultWidth); |
+ client_resolution.set_dips_height(kDefaultHeight); |
+ |
+ DVLOG(1) << "Sending ClientResolution details to host"; |
+ client_->host_stub()->NotifyClientResolution(client_resolution); |
+ |
+ // Finally send a message to start sending us video packets. |
+ protocol::VideoControl video_control; |
+ video_control.set_enable(true); |
+ |
Wez
2015/03/16 22:19:08
nit: No need for this blank line.
joedow
2015/03/18 20:13:08
Done.
|
+ video_control.set_lossless_encode(true); |
+ video_control.set_lossless_color(true); |
Wez
2015/03/16 22:19:08
By default these are currently off (false).
joedow
2015/03/18 20:13:09
Done.
|
+ |
+ DVLOG(1) << "Sending enable VideoControl message to host"; |
+ client_->host_stub()->ControlVideo(video_control); |
+} |
+ |
+void AppRemotingConnectedClientTests::AddWindowMessageHandler( |
+ const std::string& main_window_name, |
Wez
2015/03/16 22:19:08
It seems strange to pass main_window_name in as a
joedow
2015/03/18 20:13:08
This is an artifact of some refactoring I did, you
|
+ const remoting::protocol::ExtensionMessage& message) { |
+ if (message.type() == "onWindowAdded") { |
+ const base::DictionaryValue* message_data = nullptr; |
+ scoped_ptr<base::Value> host_message( |
+ base::JSONReader::Read(message.data())); |
+ if (!host_message.get() || !host_message->GetAsDictionary(&message_data)) { |
+ LOG(ERROR) << "Message received was not valid JSON."; |
Wez
2015/03/16 22:19:08
Should we terminate the test (e.g. via |done_callb
joedow
2015/03/18 20:13:08
Done.
|
+ return; |
+ } |
+ |
+ std::string current_window_title; |
+ message_data->GetString("title", ¤t_window_title); |
+ |
+ if (current_window_title == kHostProcessWindowTitle) { |
+ LOG(WARNING) << "Host Process Window is visible, this may mean that the " |
+ << "underlying application is in a bad state, YMMV."; |
Wez
2015/03/16 22:19:08
I'm surprised that this is ever a state we get int
joedow
2015/03/18 20:13:08
I saw this last year when we had created a snapsho
|
+ } |
+ |
+ std::string main_window_title = main_window_name; |
+ if (current_window_title.find_first_of(main_window_title) == 0) { |
+ connection_is_ready_for_tests_ = true; |
+ |
+ // Now that the main window is visible, give the app some time to settle |
+ // before signaling that it is ready to run tests. |
+ base::TimeDelta wait_time = base::TimeDelta::FromSeconds(5); |
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( |
+ FROM_HERE, done_closure_, wait_time); |
Wez
2015/03/16 22:19:08
Consider replacing this with a base::Timer member
joedow
2015/03/18 20:13:08
Done.
|
+ } |
+ } |
Wez
2015/03/16 22:19:08
Do we want to completely ignore all other window m
joedow
2015/03/18 20:13:08
I'd like to keep this method simple for now, we ca
|
+} |
+ |
+} // namespace test |
+} // namespace remoting |