summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-08-16 08:12:32 +0000
committersergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-08-16 08:13:46 +0000
commitc44593fa600ad351e927bc838d133b78928daf6e (patch)
tree5e5a7a46221ea64153fc9d9dd9afcafbebd761e6
parentc4f1833e9879510843bfe9ba595b56bd50f74106 (diff)
downloadchromium_src-c44593fa600ad351e927bc838d133b78928daf6e.zip
chromium_src-c44593fa600ad351e927bc838d133b78928daf6e.tar.gz
chromium_src-c44593fa600ad351e927bc838d133b78928daf6e.tar.bz2
Implement network performance simulation for remoting perf tests.
The new FakePacketSocketFactory allows to simulate fake network with given latency/bandwidth parameters. BUG=394067 Review URL: https://codereview.chromium.org/427613005 Cr-Commit-Position: refs/heads/master@{#290128} git-svn-id: svn://svn.chromium.org/chrome/trunk/src@290128 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--remoting/remoting_test.gypi10
-rw-r--r--remoting/test/fake_network_dispatcher.cc88
-rw-r--r--remoting/test/fake_network_dispatcher.h74
-rw-r--r--remoting/test/fake_network_manager.cc46
-rw-r--r--remoting/test/fake_network_manager.h37
-rw-r--r--remoting/test/fake_port_allocator.cc124
-rw-r--r--remoting/test/fake_port_allocator.h47
-rw-r--r--remoting/test/fake_socket_factory.cc328
-rw-r--r--remoting/test/fake_socket_factory.h123
-rw-r--r--remoting/test/leaky_bucket.cc41
-rw-r--r--remoting/test/leaky_bucket.h38
-rw-r--r--remoting/test/protocol_perftest.cc132
12 files changed, 1064 insertions, 24 deletions
diff --git a/remoting/remoting_test.gypi b/remoting/remoting_test.gypi
index 6227b3a..0044e16 100644
--- a/remoting/remoting_test.gypi
+++ b/remoting/remoting_test.gypi
@@ -40,6 +40,16 @@
'signaling/fake_signal_strategy.h',
'signaling/mock_signal_strategy.cc',
'signaling/mock_signal_strategy.h',
+ 'test/fake_network_dispatcher.cc',
+ 'test/fake_network_dispatcher.h',
+ 'test/fake_network_manager.cc',
+ 'test/fake_network_manager.h',
+ 'test/fake_port_allocator.cc',
+ 'test/fake_port_allocator.h',
+ 'test/fake_socket_factory.cc',
+ 'test/fake_socket_factory.h',
+ 'test/leaky_bucket.cc',
+ 'test/leaky_bucket.h',
],
'conditions': [
['enable_remoting_host == 0', {
diff --git a/remoting/test/fake_network_dispatcher.cc b/remoting/test/fake_network_dispatcher.cc
new file mode 100644
index 0000000..d2076ab
--- /dev/null
+++ b/remoting/test/fake_network_dispatcher.cc
@@ -0,0 +1,88 @@
+// Copyright 2014 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/fake_network_dispatcher.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/single_thread_task_runner.h"
+#include "net/base/io_buffer.h"
+
+namespace remoting {
+
+FakeNetworkDispatcher::FakeNetworkDispatcher()
+ : allocated_address_(0) {
+}
+
+FakeNetworkDispatcher::~FakeNetworkDispatcher() {
+ CHECK(nodes_.empty());
+}
+
+rtc::IPAddress FakeNetworkDispatcher::AllocateAddress() {
+ in6_addr addr;
+ memset(&addr, 0, sizeof(addr));
+
+ // fc00::/7 is reserved for unique local addresses.
+ addr.s6_addr[0] = 0xfc;
+
+ // Copy |allocated_address_| to the end of |addr|.
+ ++allocated_address_;
+ for (size_t i = 0; i < sizeof(allocated_address_); ++i) {
+ addr.s6_addr[15 - i] = (allocated_address_ >> (8 * i)) & 0xff;
+ }
+
+ return rtc::IPAddress(addr);
+}
+
+void FakeNetworkDispatcher::AddNode(Node* node) {
+ DCHECK(node->GetThread()->BelongsToCurrentThread());
+
+ base::AutoLock auto_lock(nodes_lock_);
+ DCHECK(nodes_.find(node->GetAddress()) == nodes_.end());
+ nodes_[node->GetAddress()] = node;
+}
+
+void FakeNetworkDispatcher::RemoveNode(Node* node) {
+ DCHECK(node->GetThread()->BelongsToCurrentThread());
+
+ base::AutoLock auto_lock(nodes_lock_);
+ DCHECK(nodes_[node->GetAddress()] == node);
+ nodes_.erase(node->GetAddress());
+}
+
+void FakeNetworkDispatcher::DeliverPacket(
+ const rtc::SocketAddress& from,
+ const rtc::SocketAddress& to,
+ const scoped_refptr<net::IOBuffer>& data,
+ int data_size) {
+ Node* node;
+ {
+ base::AutoLock auto_lock(nodes_lock_);
+
+ NodesMap::iterator node_it = nodes_.find(to.ipaddr());
+ if (node_it == nodes_.end()) {
+ LOG(ERROR) << "Tried to deliver packet to unknown target: "
+ << to.ToString();
+ return;
+ }
+
+ node = node_it->second;
+
+ // Check if |node| belongs to a different thread and post a task in that
+ // case.
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner = node->GetThread();
+ if (!task_runner->BelongsToCurrentThread()) {
+ task_runner->PostTask(FROM_HERE,
+ base::Bind(&FakeNetworkDispatcher::DeliverPacket,
+ this, from, to, data, data_size));
+ return;
+ }
+ }
+
+ // Call ReceivePacket() without lock held. It's safe because at this point we
+ // know that |node| belongs to the current thread.
+ node->ReceivePacket(from, to, data, data_size);
+}
+
+} // namespace remoting
diff --git a/remoting/test/fake_network_dispatcher.h b/remoting/test/fake_network_dispatcher.h
new file mode 100644
index 0000000..44491a4
--- /dev/null
+++ b/remoting/test/fake_network_dispatcher.h
@@ -0,0 +1,74 @@
+// Copyright 2014 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.
+
+#ifndef REMOTING_TEST_FAKE_NETWORK_DISPATCHER_H_
+#define REMOTING_TEST_FAKE_NETWORK_DISPATCHER_H_
+
+#include <map>
+
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "third_party/libjingle/source/talk/p2p/base/packetsocketfactory.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+} // namespace base
+
+namespace net {
+class IOBuffer;
+} // namespace net
+
+namespace remoting {
+
+class FakeNetworkDispatcher
+ : public base::RefCountedThreadSafe<FakeNetworkDispatcher> {
+ public:
+ class Node {
+ public:
+ virtual ~Node() {};
+
+ // Return thread on which ReceivePacket() should be called.
+ virtual const scoped_refptr<base::SingleThreadTaskRunner>& GetThread()
+ const = 0;
+ virtual const rtc::IPAddress& GetAddress() const = 0;
+
+ // Deliver a packet sent by a different node.
+ virtual void ReceivePacket(const rtc::SocketAddress& from,
+ const rtc::SocketAddress& to,
+ const scoped_refptr<net::IOBuffer>& data,
+ int data_size) = 0;
+ };
+
+ FakeNetworkDispatcher();
+
+ rtc::IPAddress AllocateAddress();
+
+ // Must be called on the thread that the |node| works on.
+ void AddNode(Node* node);
+ void RemoveNode(Node* node);
+
+ void DeliverPacket(const rtc::SocketAddress& from,
+ const rtc::SocketAddress& to,
+ const scoped_refptr<net::IOBuffer>& data,
+ int data_size);
+
+ private:
+ typedef std::map<rtc::IPAddress, Node*> NodesMap;
+
+ friend class base::RefCountedThreadSafe<FakeNetworkDispatcher>;
+ virtual ~FakeNetworkDispatcher();
+
+ NodesMap nodes_;
+ base::Lock nodes_lock_;
+
+ // A counter used to allocate unique addresses in AllocateAddress().
+ int allocated_address_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeNetworkDispatcher);
+};
+
+} // namespace remoting
+
+#endif // REMOTING_TEST_FAKE_NETWORK_DISPATCHER_H_
diff --git a/remoting/test/fake_network_manager.cc b/remoting/test/fake_network_manager.cc
new file mode 100644
index 0000000..4139c8e
--- /dev/null
+++ b/remoting/test/fake_network_manager.cc
@@ -0,0 +1,46 @@
+// Copyright 2014 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/fake_network_manager.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "jingle/glue/utils.h"
+#include "third_party/webrtc/base/socketaddress.h"
+
+namespace remoting {
+
+FakeNetworkManager::FakeNetworkManager(const rtc::IPAddress& address)
+ : started_(false),
+ weak_factory_(this) {
+ network_.reset(new rtc::Network("fake", "Fake Network", address, 32));
+ network_->AddIP(address);
+}
+
+FakeNetworkManager::~FakeNetworkManager() {
+}
+
+void FakeNetworkManager::StartUpdating() {
+ started_ = true;
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&FakeNetworkManager::SendNetworksChangedSignal,
+ weak_factory_.GetWeakPtr()));
+}
+
+void FakeNetworkManager::StopUpdating() {
+ started_ = false;
+}
+
+void FakeNetworkManager::GetNetworks(NetworkList* networks) const {
+ networks->clear();
+ networks->push_back(network_.get());
+}
+
+void FakeNetworkManager::SendNetworksChangedSignal() {
+ SignalNetworksChanged();
+}
+
+} // namespace remoting
diff --git a/remoting/test/fake_network_manager.h b/remoting/test/fake_network_manager.h
new file mode 100644
index 0000000..20788b5
--- /dev/null
+++ b/remoting/test/fake_network_manager.h
@@ -0,0 +1,37 @@
+// Copyright 2014 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.
+
+#ifndef REMOTING_TEST_FAKE_NETWORK_MANAGER_H_
+#define REMOTING_TEST_FAKE_NETWORK_MANAGER_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "third_party/webrtc/base/network.h"
+
+namespace remoting {
+
+// FakeNetworkManager always returns one interface with the IP address
+// specified in the constructor.
+class FakeNetworkManager : public rtc::NetworkManager {
+ public:
+ FakeNetworkManager(const rtc::IPAddress& address);
+ virtual ~FakeNetworkManager();
+
+ // rtc::NetworkManager interface.
+ virtual void StartUpdating() OVERRIDE;
+ virtual void StopUpdating() OVERRIDE;
+ virtual void GetNetworks(NetworkList* networks) const OVERRIDE;
+
+ protected:
+ void SendNetworksChangedSignal();
+
+ bool started_;
+ scoped_ptr<rtc::Network> network_;
+
+ base::WeakPtrFactory<FakeNetworkManager> weak_factory_;
+};
+
+} // namespace remoting
+
+#endif // REMOTING_TEST_FAKE_NETWORK_MANAGER_H_
diff --git a/remoting/test/fake_port_allocator.cc b/remoting/test/fake_port_allocator.cc
new file mode 100644
index 0000000..1835823
--- /dev/null
+++ b/remoting/test/fake_port_allocator.cc
@@ -0,0 +1,124 @@
+// Copyright 2014 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/fake_port_allocator.h"
+
+#include "remoting/test/fake_network_dispatcher.h"
+#include "remoting/test/fake_network_manager.h"
+#include "remoting/test/fake_socket_factory.h"
+
+namespace remoting {
+
+namespace {
+
+class FakePortAllocatorSession
+ : public cricket::HttpPortAllocatorSessionBase {
+ public:
+ FakePortAllocatorSession(
+ cricket::HttpPortAllocatorBase* allocator,
+ const std::string& content_name,
+ int component,
+ const std::string& ice_username_fragment,
+ const std::string& ice_password,
+ const std::vector<rtc::SocketAddress>& stun_hosts,
+ const std::vector<std::string>& relay_hosts,
+ const std::string& relay);
+ virtual ~FakePortAllocatorSession();
+
+ // cricket::HttpPortAllocatorBase overrides.
+ virtual void ConfigReady(cricket::PortConfiguration* config) OVERRIDE;
+ virtual void SendSessionRequest(const std::string& host, int port) OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FakePortAllocatorSession);
+};
+
+FakePortAllocatorSession::FakePortAllocatorSession(
+ cricket::HttpPortAllocatorBase* allocator,
+ const std::string& content_name,
+ int component,
+ const std::string& ice_username_fragment,
+ const std::string& ice_password,
+ const std::vector<rtc::SocketAddress>& stun_hosts,
+ const std::vector<std::string>& relay_hosts,
+ const std::string& relay)
+ : HttpPortAllocatorSessionBase(allocator,
+ content_name,
+ component,
+ ice_username_fragment,
+ ice_password,
+ stun_hosts,
+ relay_hosts,
+ relay,
+ std::string()) {
+ set_flags(cricket::PORTALLOCATOR_DISABLE_TCP |
+ cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG |
+ cricket::PORTALLOCATOR_ENABLE_IPV6 |
+ cricket::PORTALLOCATOR_DISABLE_STUN |
+ cricket::PORTALLOCATOR_DISABLE_RELAY);
+}
+
+FakePortAllocatorSession::~FakePortAllocatorSession() {
+}
+
+void FakePortAllocatorSession::ConfigReady(
+ cricket::PortConfiguration* config) {
+ // Filter out non-UDP relay ports, so that we don't try using TCP.
+ for (cricket::PortConfiguration::RelayList::iterator relay =
+ config->relays.begin(); relay != config->relays.end(); ++relay) {
+ cricket::PortList filtered_ports;
+ for (cricket::PortList::iterator port =
+ relay->ports.begin(); port != relay->ports.end(); ++port) {
+ if (port->proto == cricket::PROTO_UDP) {
+ filtered_ports.push_back(*port);
+ }
+ }
+ relay->ports = filtered_ports;
+ }
+ cricket::BasicPortAllocatorSession::ConfigReady(config);
+}
+
+void FakePortAllocatorSession::SendSessionRequest(
+ const std::string& host,
+ int port) {
+ ReceiveSessionResponse(std::string());
+}
+
+} // namespace
+
+// static
+scoped_ptr<FakePortAllocator> FakePortAllocator::Create(
+ scoped_refptr<FakeNetworkDispatcher> fake_network_dispatcher) {
+ scoped_ptr<FakePacketSocketFactory> socket_factory(
+ new FakePacketSocketFactory(fake_network_dispatcher));
+ scoped_ptr<rtc::NetworkManager> network_manager(
+ new FakeNetworkManager(socket_factory->GetAddress()));
+
+ return scoped_ptr<FakePortAllocator>(
+ new FakePortAllocator(network_manager.Pass(), socket_factory.Pass()));
+}
+
+FakePortAllocator::FakePortAllocator(
+ scoped_ptr<rtc::NetworkManager> network_manager,
+ scoped_ptr<FakePacketSocketFactory> socket_factory)
+ : HttpPortAllocatorBase(network_manager.get(),
+ socket_factory.get(),
+ std::string()),
+ network_manager_(network_manager.Pass()),
+ socket_factory_(socket_factory.Pass()) {}
+
+FakePortAllocator::~FakePortAllocator() {
+}
+
+cricket::PortAllocatorSession* FakePortAllocator::CreateSessionInternal(
+ const std::string& content_name,
+ int component,
+ const std::string& ice_username_fragment,
+ const std::string& ice_password) {
+ return new FakePortAllocatorSession(
+ this, content_name, component, ice_username_fragment, ice_password,
+ stun_hosts(), relay_hosts(), relay_token());
+}
+
+} // namespace remoting
diff --git a/remoting/test/fake_port_allocator.h b/remoting/test/fake_port_allocator.h
new file mode 100644
index 0000000..18fd7bd
--- /dev/null
+++ b/remoting/test/fake_port_allocator.h
@@ -0,0 +1,47 @@
+// Copyright 2014 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.
+
+#ifndef REMOTING_TEST_FAKE_PORT_ALLOCATOR_H_
+#define REMOTING_TEST_FAKE_PORT_ALLOCATOR_H_
+
+#include <set>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "third_party/libjingle/source/talk/p2p/client/httpportallocator.h"
+
+namespace remoting {
+
+class FakeNetworkDispatcher;
+class FakePacketSocketFactory;
+
+class FakePortAllocator : public cricket::HttpPortAllocatorBase {
+ public:
+ static scoped_ptr<FakePortAllocator> Create(
+ scoped_refptr<FakeNetworkDispatcher> fake_network_dispatcher);
+
+ virtual ~FakePortAllocator();
+
+ FakePacketSocketFactory* socket_factory() { return socket_factory_.get(); }
+
+ // cricket::BasicPortAllocator overrides.
+ virtual cricket::PortAllocatorSession* CreateSessionInternal(
+ const std::string& content_name,
+ int component,
+ const std::string& ice_username_fragment,
+ const std::string& ice_password) OVERRIDE;
+
+ private:
+ FakePortAllocator(scoped_ptr<rtc::NetworkManager> network_manager,
+ scoped_ptr<FakePacketSocketFactory> socket_factory);
+
+ scoped_ptr<rtc::NetworkManager> network_manager_;
+ scoped_ptr<FakePacketSocketFactory> socket_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakePortAllocator);
+};
+
+} // namespace remoting
+
+#endif // REMOTING_TEST_FAKE_PORT_ALLOCATOR_H_
diff --git a/remoting/test/fake_socket_factory.cc b/remoting/test/fake_socket_factory.cc
new file mode 100644
index 0000000..c9b927d
--- /dev/null
+++ b/remoting/test/fake_socket_factory.cc
@@ -0,0 +1,328 @@
+// Copyright 2014 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.
+
+// MSVC++ requires this to be set before any other includes to get M_PI.
+#define _USE_MATH_DEFINES
+
+#include "remoting/test/fake_socket_factory.h"
+
+#include <math.h>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/location.h"
+#include "base/rand_util.h"
+#include "base/single_thread_task_runner.h"
+#include "base/thread_task_runner_handle.h"
+#include "net/base/io_buffer.h"
+#include "remoting/test/leaky_bucket.h"
+#include "third_party/webrtc/base/asyncpacketsocket.h"
+
+namespace remoting {
+
+namespace {
+
+const int kPortRangeStart = 1024;
+const int kPortRangeEnd = 65535;
+
+double GetNormalRandom(double average, double stddev) {
+ // Based on Box-Muller transform, see
+ // http://en.wikipedia.org/wiki/Box_Muller_transform .
+ return average +
+ stddev * sqrt(-2.0 * log(1.0 - base::RandDouble())) *
+ cos(base::RandDouble() * 2.0 * M_PI);
+}
+
+class FakeUdpSocket : public rtc::AsyncPacketSocket {
+ public:
+ FakeUdpSocket(FakePacketSocketFactory* factory,
+ scoped_refptr<FakeNetworkDispatcher> dispatcher,
+ const rtc::SocketAddress& local_address);
+ virtual ~FakeUdpSocket();
+
+ void ReceivePacket(const rtc::SocketAddress& from,
+ const rtc::SocketAddress& to,
+ const scoped_refptr<net::IOBuffer>& data,
+ int data_size);
+
+ // rtc::AsyncPacketSocket interface.
+ virtual rtc::SocketAddress GetLocalAddress() const OVERRIDE;
+ virtual rtc::SocketAddress GetRemoteAddress() const OVERRIDE;
+ virtual int Send(const void* data, size_t data_size,
+ const rtc::PacketOptions& options) OVERRIDE;
+ virtual int SendTo(const void* data, size_t data_size,
+ const rtc::SocketAddress& address,
+ const rtc::PacketOptions& options) OVERRIDE;
+ virtual int Close() OVERRIDE;
+ virtual State GetState() const OVERRIDE;
+ virtual int GetOption(rtc::Socket::Option option, int* value) OVERRIDE;
+ virtual int SetOption(rtc::Socket::Option option, int value) OVERRIDE;
+ virtual int GetError() const OVERRIDE;
+ virtual void SetError(int error) OVERRIDE;
+
+ private:
+ FakePacketSocketFactory* factory_;
+ scoped_refptr<FakeNetworkDispatcher> dispatcher_;
+ rtc::SocketAddress local_address_;
+ State state_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeUdpSocket);
+};
+
+FakeUdpSocket::FakeUdpSocket(FakePacketSocketFactory* factory,
+ scoped_refptr<FakeNetworkDispatcher> dispatcher,
+ const rtc::SocketAddress& local_address)
+ : factory_(factory),
+ dispatcher_(dispatcher),
+ local_address_(local_address),
+ state_(STATE_BOUND) {
+}
+
+FakeUdpSocket::~FakeUdpSocket() {
+ factory_->OnSocketDestroyed(local_address_.port());
+}
+
+void FakeUdpSocket::ReceivePacket(const rtc::SocketAddress& from,
+ const rtc::SocketAddress& to,
+ const scoped_refptr<net::IOBuffer>& data,
+ int data_size) {
+ SignalReadPacket(
+ this, data->data(), data_size, from, rtc::CreatePacketTime(0));
+}
+
+rtc::SocketAddress FakeUdpSocket::GetLocalAddress() const {
+ return local_address_;
+}
+
+rtc::SocketAddress FakeUdpSocket::GetRemoteAddress() const {
+ NOTREACHED();
+ return rtc::SocketAddress();
+}
+
+int FakeUdpSocket::Send(const void* data, size_t data_size,
+ const rtc::PacketOptions& options) {
+ NOTREACHED();
+ return EINVAL;
+}
+
+int FakeUdpSocket::SendTo(const void* data, size_t data_size,
+ const rtc::SocketAddress& address,
+ const rtc::PacketOptions& options) {
+ scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(data_size);
+ memcpy(buffer->data(), data, data_size);
+ dispatcher_->DeliverPacket(local_address_, address, buffer, data_size);
+ return data_size;
+}
+
+int FakeUdpSocket::Close() {
+ state_ = STATE_CLOSED;
+ return 0;
+}
+
+rtc::AsyncPacketSocket::State FakeUdpSocket::GetState() const {
+ return state_;
+}
+
+int FakeUdpSocket::GetOption(rtc::Socket::Option option, int* value) {
+ NOTIMPLEMENTED();
+ return -1;
+}
+
+int FakeUdpSocket::SetOption(rtc::Socket::Option option, int value) {
+ NOTIMPLEMENTED();
+ return -1;
+}
+
+int FakeUdpSocket::GetError() const {
+ return 0;
+}
+
+void FakeUdpSocket::SetError(int error) {
+ NOTREACHED();
+}
+
+} // namespace
+
+FakePacketSocketFactory::PendingPacket::PendingPacket()
+ : data_size(0) {
+}
+
+FakePacketSocketFactory::PendingPacket::PendingPacket(
+ const rtc::SocketAddress& from,
+ const rtc::SocketAddress& to,
+ const scoped_refptr<net::IOBuffer>& data,
+ int data_size)
+ : from(from), to(to), data(data), data_size(data_size) {
+}
+
+FakePacketSocketFactory::PendingPacket::~PendingPacket() {
+}
+
+FakePacketSocketFactory::FakePacketSocketFactory(
+ FakeNetworkDispatcher* dispatcher)
+ : task_runner_(base::ThreadTaskRunnerHandle::Get()),
+ dispatcher_(dispatcher),
+ address_(dispatcher_->AllocateAddress()),
+ out_of_order_rate_(0.0),
+ next_port_(kPortRangeStart),
+ weak_factory_(this) {
+ dispatcher_->AddNode(this);
+}
+
+FakePacketSocketFactory::~FakePacketSocketFactory() {
+ CHECK(udp_sockets_.empty());
+ dispatcher_->RemoveNode(this);
+}
+
+void FakePacketSocketFactory::OnSocketDestroyed(int port) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ udp_sockets_.erase(port);
+}
+
+void FakePacketSocketFactory::SetBandwidth(int bandwidth, int max_buffer) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ if (bandwidth <= 0) {
+ leaky_bucket_.reset();
+ } else {
+ leaky_bucket_.reset(new LeakyBucket(max_buffer, bandwidth));
+ }
+}
+
+void FakePacketSocketFactory::SetLatency(base::TimeDelta average,
+ base::TimeDelta stddev) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ latency_average_ = average;
+ latency_stddev_ = stddev;
+}
+
+rtc::AsyncPacketSocket* FakePacketSocketFactory::CreateUdpSocket(
+ const rtc::SocketAddress& local_address,
+ int min_port, int max_port) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+
+ int port = -1;
+ if (min_port > 0 && max_port > 0) {
+ for (int i = min_port; i <= max_port; ++i) {
+ if (udp_sockets_.find(i) == udp_sockets_.end()) {
+ port = i;
+ break;
+ }
+ }
+ if (port < 0)
+ return NULL;
+ } else {
+ do {
+ port = next_port_;
+ next_port_ =
+ (next_port_ >= kPortRangeEnd) ? kPortRangeStart : (next_port_ + 1);
+ } while (udp_sockets_.find(port) != udp_sockets_.end());
+ }
+
+ CHECK(local_address.ipaddr() == address_);
+
+ FakeUdpSocket* result =
+ new FakeUdpSocket(this, dispatcher_,
+ rtc::SocketAddress(local_address.ipaddr(), port));
+
+ udp_sockets_[port] =
+ base::Bind(&FakeUdpSocket::ReceivePacket, base::Unretained(result));
+
+ return result;
+}
+
+rtc::AsyncPacketSocket* FakePacketSocketFactory::CreateServerTcpSocket(
+ const rtc::SocketAddress& local_address,
+ int min_port, int max_port,
+ int opts) {
+ return NULL;
+}
+
+rtc::AsyncPacketSocket* FakePacketSocketFactory::CreateClientTcpSocket(
+ const rtc::SocketAddress& local_address,
+ const rtc::SocketAddress& remote_address,
+ const rtc::ProxyInfo& proxy_info,
+ const std::string& user_agent,
+ int opts) {
+ return NULL;
+}
+
+rtc::AsyncResolverInterface*
+FakePacketSocketFactory::CreateAsyncResolver() {
+ return NULL;
+}
+
+const scoped_refptr<base::SingleThreadTaskRunner>&
+FakePacketSocketFactory::GetThread() const {
+ return task_runner_;
+}
+
+const rtc::IPAddress& FakePacketSocketFactory::GetAddress() const {
+ return address_;
+}
+
+void FakePacketSocketFactory::ReceivePacket(
+ const rtc::SocketAddress& from,
+ const rtc::SocketAddress& to,
+ const scoped_refptr<net::IOBuffer>& data,
+ int data_size) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ DCHECK(to.ipaddr() == address_);
+
+ base::TimeDelta delay;
+
+ if (leaky_bucket_) {
+ delay = leaky_bucket_->AddPacket(data_size);
+ if (delay.is_max()) {
+ // Drop the packet.
+ return;
+ }
+ }
+
+ if (latency_average_ > base::TimeDelta()) {
+ delay += base::TimeDelta::FromMillisecondsD(
+ GetNormalRandom(latency_average_.InMillisecondsF(),
+ latency_stddev_.InMillisecondsF()));
+ }
+ if (delay < base::TimeDelta())
+ delay = base::TimeDelta();
+
+ // Put the packet to the |pending_packets_| and post a task for
+ // DoReceivePackets(). Note that the DoReceivePackets() task posted here may
+ // deliver a different packet, not the one added to the queue here. This
+ // would happen if another task gets posted with a shorted delay or when
+ // |out_of_order_rate_| is greater than 0. It's implemented this way to
+ // decouple latency variability from out-of-order delivery.
+ PendingPacket packet(from, to, data, data_size);
+ pending_packets_.push_back(packet);
+ task_runner_->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&FakePacketSocketFactory::DoReceivePacket,
+ weak_factory_.GetWeakPtr()),
+ delay);
+}
+
+void FakePacketSocketFactory::DoReceivePacket() {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+
+ PendingPacket packet;
+ if (pending_packets_.size() > 1 && base::RandDouble() < out_of_order_rate_) {
+ std::list<PendingPacket>::iterator it = pending_packets_.begin();
+ ++it;
+ packet = *it;
+ pending_packets_.erase(it);
+ } else {
+ packet = pending_packets_.front();
+ pending_packets_.pop_front();
+ }
+
+ UdpSocketsMap::iterator iter = udp_sockets_.find(packet.to.port());
+ if (iter == udp_sockets_.end()) {
+ // Invalid port number.
+ return;
+ }
+
+ iter->second.Run(packet.from, packet.to, packet.data, packet.data_size);
+}
+
+} // namespace remoting
diff --git a/remoting/test/fake_socket_factory.h b/remoting/test/fake_socket_factory.h
new file mode 100644
index 0000000..c7efdab
--- /dev/null
+++ b/remoting/test/fake_socket_factory.h
@@ -0,0 +1,123 @@
+// Copyright 2014 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.
+
+#ifndef REMOTING_TEST_FAKE_SOCKET_FACTORY_H_
+#define REMOTING_TEST_FAKE_SOCKET_FACTORY_H_
+
+#include <list>
+
+#include "base/callback_forward.h"
+#include "base/compiler_specific.h"
+#include "base/memory/weak_ptr.h"
+#include "remoting/test/fake_network_dispatcher.h"
+#include "third_party/libjingle/source/talk/p2p/base/packetsocketfactory.h"
+
+namespace remoting {
+
+class FakeNetworkDispatcher;
+class LeakyBucket;
+
+class FakePacketSocketFactory : public rtc::PacketSocketFactory,
+ public FakeNetworkDispatcher::Node {
+ public:
+ // |dispatcher| must outlive the factory.
+ explicit FakePacketSocketFactory(FakeNetworkDispatcher* dispatcher);
+ virtual ~FakePacketSocketFactory();
+
+ void OnSocketDestroyed(int port);
+
+ // |bandwidth| - simulated link bandwidth in bytes/second. 0 indicates that
+ // bandwidth is unlimited.
+ // |max_buffer| - size of buffers in bytes. Ignored when |bandwidth| is 0.
+ void SetBandwidth(int bandwidth, int max_buffer);
+
+ // Specifies parameters for simulated network latency. Latency is generated
+ // with normal distribution around |average| with the given |stddev|. Random
+ // latency calculated based on these parameters is added to the buffering
+ // delay (which is calculated based on the parameters passed to
+ // SetBandwidth()). I.e. total latency for each packet is calculated using the
+ // following formula
+ //
+ // l = NormalRand(average, stddev) + bytes_buffered / bandwidth .
+ //
+ // Where bytes_buffered is the current level in the leaky bucket used to
+ // control bandwidth.
+ void SetLatency(base::TimeDelta average, base::TimeDelta stddev);
+
+ void set_out_of_order_rate(double out_of_order_rate) {
+ out_of_order_rate_ = out_of_order_rate;
+ }
+
+ // rtc::PacketSocketFactory interface.
+ virtual rtc::AsyncPacketSocket* CreateUdpSocket(
+ const rtc::SocketAddress& local_address,
+ int min_port, int max_port) OVERRIDE;
+ virtual rtc::AsyncPacketSocket* CreateServerTcpSocket(
+ const rtc::SocketAddress& local_address,
+ int min_port, int max_port,
+ int opts) OVERRIDE;
+ virtual rtc::AsyncPacketSocket* CreateClientTcpSocket(
+ const rtc::SocketAddress& local_address,
+ const rtc::SocketAddress& remote_address,
+ const rtc::ProxyInfo& proxy_info,
+ const std::string& user_agent,
+ int opts) OVERRIDE;
+ virtual rtc::AsyncResolverInterface* CreateAsyncResolver() OVERRIDE;
+
+ // FakeNetworkDispatcher::Node interface.
+ virtual const scoped_refptr<base::SingleThreadTaskRunner>& GetThread()
+ const OVERRIDE;
+ virtual const rtc::IPAddress& GetAddress() const OVERRIDE;
+ virtual void ReceivePacket(const rtc::SocketAddress& from,
+ const rtc::SocketAddress& to,
+ const scoped_refptr<net::IOBuffer>& data,
+ int data_size) OVERRIDE;
+
+ private:
+ struct PendingPacket {
+ PendingPacket();
+ PendingPacket(
+ const rtc::SocketAddress& from,
+ const rtc::SocketAddress& to,
+ const scoped_refptr<net::IOBuffer>& data,
+ int data_size);
+ ~PendingPacket();
+
+ rtc::SocketAddress from;
+ rtc::SocketAddress to;
+ scoped_refptr<net::IOBuffer> data;
+ int data_size;
+ };
+
+ typedef base::Callback<void(const rtc::SocketAddress& from,
+ const rtc::SocketAddress& to,
+ const scoped_refptr<net::IOBuffer>& data,
+ int data_size)> ReceiveCallback;
+ typedef std::map<uint16_t, ReceiveCallback> UdpSocketsMap;
+
+ void DoReceivePacket();
+
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+ scoped_refptr<FakeNetworkDispatcher> dispatcher_;
+
+ rtc::IPAddress address_;
+
+ scoped_ptr<LeakyBucket> leaky_bucket_;
+ base::TimeDelta latency_average_;
+ base::TimeDelta latency_stddev_;
+ double out_of_order_rate_;
+
+ UdpSocketsMap udp_sockets_;
+ uint16_t next_port_;
+
+ std::list<PendingPacket> pending_packets_;
+
+ base::WeakPtrFactory<FakePacketSocketFactory> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakePacketSocketFactory);
+};
+
+} // namespace remoting
+
+#endif // REMOTING_TEST_FAKE_SOCKET_FACTORY_H_
diff --git a/remoting/test/leaky_bucket.cc b/remoting/test/leaky_bucket.cc
new file mode 100644
index 0000000..ee8a62b
--- /dev/null
+++ b/remoting/test/leaky_bucket.cc
@@ -0,0 +1,41 @@
+// Copyright 2014 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/leaky_bucket.h"
+
+#include "base/logging.h"
+
+namespace remoting {
+
+LeakyBucket::LeakyBucket(double depth, double rate)
+ : depth_(depth),
+ rate_(rate),
+ level_(0.0),
+ last_update_(base::TimeTicks::Now()) {
+}
+
+LeakyBucket::~LeakyBucket() {
+}
+
+base::TimeDelta LeakyBucket::AddPacket(int size) {
+ UpdateLevel();
+
+ double new_level = level_ + size;
+ if (new_level > depth_)
+ return base::TimeDelta::Max();
+
+ base::TimeDelta result = base::TimeDelta::FromSecondsD(level_ / rate_);
+ level_ = new_level;
+ return result;
+}
+
+void LeakyBucket::UpdateLevel() {
+ base::TimeTicks now = base::TimeTicks::Now();
+ level_ -= rate_ * (now - last_update_).InSecondsF();
+ if (level_ < 0.0)
+ level_ = 0.0;
+ last_update_ = now;
+}
+
+} // namespace remoting
diff --git a/remoting/test/leaky_bucket.h b/remoting/test/leaky_bucket.h
new file mode 100644
index 0000000..5b54b40
--- /dev/null
+++ b/remoting/test/leaky_bucket.h
@@ -0,0 +1,38 @@
+// Copyright 2014 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.
+
+#ifndef REMOTING_TEST_LEAKY_BUCKET_H_
+#define REMOTING_TEST_LEAKY_BUCKET_H_
+
+#include "base/basictypes.h"
+#include "base/time/time.h"
+
+namespace remoting {
+
+class LeakyBucket {
+ public:
+ // |depth| is in bytes. |rate| is specified in bytes/second.
+ LeakyBucket(double depth, double rate);
+ ~LeakyBucket();
+
+ // Adds a packet of the given |size| to the bucket and returns packet delay.
+ // Returns TimeDelta::Max() if the packet overflows the bucket, in which case
+ // it should be dropped.
+ base::TimeDelta AddPacket(int size);
+
+ private:
+ void UpdateLevel();
+
+ double depth_;
+ double rate_;
+
+ double level_;
+ base::TimeTicks last_update_;
+
+ DISALLOW_COPY_AND_ASSIGN(LeakyBucket);
+};
+
+} // namespace remoting
+
+#endif // REMOTING_TEST_LEAKY_BUCKET_H_
diff --git a/remoting/test/protocol_perftest.cc b/remoting/test/protocol_perftest.cc
index 438cbb8..2387ddb 100644
--- a/remoting/test/protocol_perftest.cc
+++ b/remoting/test/protocol_perftest.cc
@@ -5,6 +5,7 @@
#include "base/base64.h"
#include "base/file_util.h"
#include "base/message_loop/message_loop.h"
+#include "base/rand_util.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/synchronization/waitable_event.h"
@@ -22,13 +23,15 @@
#include "remoting/host/chromoting_host_context.h"
#include "remoting/host/fake_desktop_environment.h"
#include "remoting/host/video_scheduler.h"
-#include "remoting/protocol/chromium_port_allocator.h"
#include "remoting/protocol/jingle_session_manager.h"
#include "remoting/protocol/libjingle_transport_factory.h"
#include "remoting/protocol/me2me_host_authenticator_factory.h"
#include "remoting/protocol/negotiating_client_authenticator.h"
#include "remoting/protocol/session_config.h"
#include "remoting/signaling/fake_signal_strategy.h"
+#include "remoting/test/fake_network_dispatcher.h"
+#include "remoting/test/fake_port_allocator.h"
+#include "remoting/test/fake_socket_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace remoting {
@@ -39,10 +42,31 @@ const char kHostJid[] = "host_jid@example.com/host";
const char kHostOwner[] = "jane.doe@example.com";
const char kClientJid[] = "jane.doe@example.com/client";
-class ProtocolPerfTest : public testing::Test,
- public ClientUserInterface,
- public VideoRenderer,
- public HostStatusObserver {
+struct NetworkPerformanceParams {
+ NetworkPerformanceParams(int bandwidth,
+ int max_buffers,
+ double latency_average_ms,
+ double latency_stddev_ms,
+ double out_of_order_rate)
+ : bandwidth(bandwidth),
+ max_buffers(max_buffers),
+ latency_average(base::TimeDelta::FromMillisecondsD(latency_average_ms)),
+ latency_stddev(base::TimeDelta::FromMillisecondsD(latency_stddev_ms)),
+ out_of_order_rate(out_of_order_rate) {}
+
+ int bandwidth;
+ int max_buffers;
+ base::TimeDelta latency_average;
+ base::TimeDelta latency_stddev;
+ double out_of_order_rate;
+};
+
+class ProtocolPerfTest
+ : public testing::Test,
+ public testing::WithParamInterface<NetworkPerformanceParams>,
+ public ClientUserInterface,
+ public VideoRenderer,
+ public HostStatusObserver {
public:
ProtocolPerfTest()
: host_thread_("host"),
@@ -54,6 +78,7 @@ class ProtocolPerfTest : public testing::Test,
capture_thread_.Start();
encode_thread_.Start();
}
+
virtual ~ProtocolPerfTest() {
host_thread_.message_loop_proxy()->DeleteSoon(FROM_HERE, host_.release());
host_thread_.message_loop_proxy()->DeleteSoon(FROM_HERE,
@@ -162,6 +187,8 @@ class ProtocolPerfTest : public testing::Test,
// host is started on |host_thread_| while the client works on the main
// thread.
void StartHostAndClient(protocol::ChannelConfig::Codec video_codec) {
+ fake_network_dispatcher_ = new FakeNetworkDispatcher();
+
client_signaling_.reset(new FakeSignalStrategy(kClientJid));
jingle_glue::JingleThreadWrapper::EnsureForCurrentMessageLoop();
@@ -189,15 +216,18 @@ class ProtocolPerfTest : public testing::Test,
protocol::NetworkSettings network_settings(
protocol::NetworkSettings::NAT_TRAVERSAL_OUTGOING);
- // TODO(sergeyu): Replace with a fake port allocator.
- scoped_ptr<cricket::HttpPortAllocatorBase> host_port_allocator =
- protocol::ChromiumPortAllocator::Create(NULL, network_settings)
- .PassAs<cricket::HttpPortAllocatorBase>();
-
+ scoped_ptr<FakePortAllocator> port_allocator(
+ FakePortAllocator::Create(fake_network_dispatcher_));
+ port_allocator->socket_factory()->SetBandwidth(GetParam().bandwidth,
+ GetParam().max_buffers);
+ port_allocator->socket_factory()->SetLatency(GetParam().latency_average,
+ GetParam().latency_stddev);
+ port_allocator->socket_factory()->set_out_of_order_rate(
+ GetParam().out_of_order_rate);
scoped_ptr<protocol::TransportFactory> host_transport_factory(
new protocol::LibjingleTransportFactory(
host_signaling_.get(),
- host_port_allocator.Pass(),
+ port_allocator.PassAs<cricket::HttpPortAllocatorBase>(),
network_settings));
scoped_ptr<protocol::SessionManager> session_manager(
@@ -257,15 +287,19 @@ class ProtocolPerfTest : public testing::Test,
client_context_.reset(
new ClientContext(base::ThreadTaskRunnerHandle::Get()));
- // TODO(sergeyu): Replace with a fake port allocator
- scoped_ptr<cricket::HttpPortAllocatorBase> client_port_allocator =
- protocol::ChromiumPortAllocator::Create(NULL, network_settings)
- .PassAs<cricket::HttpPortAllocatorBase>();
-
+ scoped_ptr<FakePortAllocator> port_allocator(
+ FakePortAllocator::Create(fake_network_dispatcher_));
+ port_allocator->socket_factory()->SetBandwidth(GetParam().bandwidth,
+ GetParam().max_buffers);
+ port_allocator->socket_factory()->SetLatency(GetParam().latency_average,
+ GetParam().latency_stddev);
+ port_allocator->socket_factory()->set_out_of_order_rate(
+ GetParam().out_of_order_rate);
scoped_ptr<protocol::TransportFactory> client_transport_factory(
- new protocol::LibjingleTransportFactory(client_signaling_.get(),
- client_port_allocator.Pass(),
- network_settings));
+ new protocol::LibjingleTransportFactory(
+ client_signaling_.get(),
+ port_allocator.PassAs<cricket::HttpPortAllocatorBase>(),
+ network_settings));
std::vector<protocol::AuthenticationMethod> auth_methods;
auth_methods.push_back(protocol::AuthenticationMethod::Spake2(
@@ -294,6 +328,8 @@ class ProtocolPerfTest : public testing::Test,
base::MessageLoopForIO message_loop_;
+ scoped_refptr<FakeNetworkDispatcher> fake_network_dispatcher_;
+
base::Thread host_thread_;
base::Thread capture_thread_;
base::Thread encode_thread_;
@@ -321,7 +357,40 @@ class ProtocolPerfTest : public testing::Test,
DISALLOW_COPY_AND_ASSIGN(ProtocolPerfTest);
};
-TEST_F(ProtocolPerfTest, StreamFrameRate) {
+INSTANTIATE_TEST_CASE_P(
+ NoDelay,
+ ProtocolPerfTest,
+ ::testing::Values(NetworkPerformanceParams(0, 0, 0, 0, 0.0)));
+
+INSTANTIATE_TEST_CASE_P(
+ HighLatency,
+ ProtocolPerfTest,
+ ::testing::Values(NetworkPerformanceParams(0, 0, 300, 30, 0.0),
+ NetworkPerformanceParams(0, 0, 30, 10, 0.0)));
+
+INSTANTIATE_TEST_CASE_P(
+ OutOfOrder,
+ ProtocolPerfTest,
+ ::testing::Values(NetworkPerformanceParams(0, 0, 2, 0, 0.01),
+ NetworkPerformanceParams(0, 0, 30, 1, 0.01),
+ NetworkPerformanceParams(0, 0, 30, 1, 0.1),
+ NetworkPerformanceParams(0, 0, 300, 20, 0.01),
+ NetworkPerformanceParams(0, 0, 300, 20, 0.1)));
+
+INSTANTIATE_TEST_CASE_P(
+ LimitedBandwidth,
+ ProtocolPerfTest,
+ ::testing::Values(
+ // 100 MBps
+ NetworkPerformanceParams(800000000, 800000000, 2, 1, 0.0),
+ // 8 MBps
+ NetworkPerformanceParams(1000000, 300000, 30, 5, 0.01),
+ NetworkPerformanceParams(1000000, 2000000, 30, 5, 0.01),
+ // 800 kBps
+ NetworkPerformanceParams(100000, 30000, 130, 5, 0.01),
+ NetworkPerformanceParams(100000, 200000, 130, 5, 0.01)));
+
+TEST_P(ProtocolPerfTest, StreamFrameRate) {
StartHostAndClient(protocol::ChannelConfig::CODEC_VP8);
ASSERT_NO_FATAL_FAILURE(WaitConnected());
@@ -338,6 +407,8 @@ TEST_F(ProtocolPerfTest, StreamFrameRate) {
LOG(INFO) << "Maximum latency: " << latency.InMillisecondsF() << "ms";
}
+const int kIntermittentFrameSize = 100 * 1000;
+
// Frame generator that rewrites the whole screen every 60th frame. Should only
// be used with the VERBATIM codec as the allocated frame may contain arbitrary
// data.
@@ -349,8 +420,8 @@ class IntermittentChangeFrameGenerator
scoped_ptr<webrtc::DesktopFrame> GenerateFrame(
webrtc::DesktopCapturer::Callback* callback) {
- const int kWidth = 800;
- const int kHeight = 600;
+ const int kWidth = 1000;
+ const int kHeight = kIntermittentFrameSize / kWidth / 4;
bool fresh_frame = false;
if (frame_index_ % 60 == 0 || !current_frame_) {
@@ -379,7 +450,7 @@ class IntermittentChangeFrameGenerator
DISALLOW_COPY_AND_ASSIGN(IntermittentChangeFrameGenerator);
};
-TEST_F(ProtocolPerfTest, IntermittentChanges) {
+TEST_P(ProtocolPerfTest, IntermittentChanges) {
desktop_environment_factory_.set_frame_generator(
base::Bind(&IntermittentChangeFrameGenerator::GenerateFrame,
new IntermittentChangeFrameGenerator()));
@@ -389,14 +460,27 @@ TEST_F(ProtocolPerfTest, IntermittentChanges) {
ReceiveFrame(NULL);
- for (int i = 0; i < 5; ++i) {
+ base::TimeDelta expected = GetParam().latency_average;
+ if (GetParam().bandwidth > 0) {
+ expected += base::TimeDelta::FromSecondsD(kIntermittentFrameSize /
+ GetParam().bandwidth);
+ }
+ LOG(INFO) << "Expected: " << expected.InMillisecondsF() << "ms";
+
+ base::TimeDelta sum;
+
+ const int kFrames = 5;
+ for (int i = 0; i < kFrames; ++i) {
base::TimeDelta latency;
ReceiveFrame(&latency);
LOG(INFO) << "Latency: " << latency.InMillisecondsF()
<< "ms Encode: " << last_video_packet_->encode_time_ms()
<< "ms Capture: " << last_video_packet_->capture_time_ms()
<< "ms";
+ sum += latency;
}
+
+ LOG(INFO) << "Average: " << (sum / kFrames).InMillisecondsF();
}
} // namespace remoting