1 // Copyright 2018 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.
4
5 #include "components/mirroring/service/session_monitor.h"
6
7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "base/json/json_reader.h"
10 #include "base/json/json_writer.h"
11 #include "base/macros.h"
12 #include "base/test/mock_callback.h"
13 #include "base/test/task_environment.h"
14 #include "base/time/default_tick_clock.h"
15 #include "components/mirroring/service/message_dispatcher.h"
16 #include "components/mirroring/service/mirror_settings.h"
17 #include "components/mirroring/service/value_util.h"
18 #include "components/mirroring/service/wifi_status_monitor.h"
19 #include "media/cast/cast_environment.h"
20 #include "media/cast/test/utility/net_utility.h"
21 #include "mojo/public/cpp/bindings/receiver.h"
22 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
23 #include "net/base/ip_endpoint.h"
24 #include "services/network/test/test_url_loader_factory.h"
25 #include "testing/gmock/include/gmock/gmock.h"
26 #include "testing/gtest/include/gtest/gtest.h"
27
28 using mirroring::mojom::CastMessage;
29 using mirroring::mojom::SessionError;
30
31 namespace mirroring {
32
33 namespace {
34
35 constexpr int kRetentionBytes = 512 * 1024; // 512k.
36
VerifyStringValue(const base::Value & raw_value,const std::string & key,const std::string & expected_value)37 void VerifyStringValue(const base::Value& raw_value,
38 const std::string& key,
39 const std::string& expected_value) {
40 std::string data;
41 EXPECT_TRUE(GetString(raw_value, key, &data));
42 EXPECT_EQ(expected_value, data);
43 }
44
VerifyBoolValue(const base::Value & raw_value,const std::string & key,bool expected_value)45 void VerifyBoolValue(const base::Value& raw_value,
46 const std::string& key,
47 bool expected_value) {
48 bool data;
49 EXPECT_TRUE(GetBool(raw_value, key, &data));
50 EXPECT_EQ(expected_value, data);
51 }
52
VerifyDoubleValue(const base::Value & raw_value,const std::string & key,double expected_value)53 void VerifyDoubleValue(const base::Value& raw_value,
54 const std::string& key,
55 double expected_value) {
56 double data;
57 EXPECT_TRUE(GetDouble(raw_value, key, &data));
58 EXPECT_NEAR(expected_value, data, DBL_EPSILON * 2);
59 }
60
VerifyWifiStatus(const base::Value & raw_value,double starting_snr,int starting_speed,int num_of_status)61 void VerifyWifiStatus(const base::Value& raw_value,
62 double starting_snr,
63 int starting_speed,
64 int num_of_status) {
65 EXPECT_TRUE(raw_value.is_dict());
66 auto* found = raw_value.FindKey("tags");
67 EXPECT_TRUE(found && found->is_dict());
68 auto* wifi_status = found->FindKey("receiverWifiStatus");
69 EXPECT_TRUE(wifi_status && wifi_status->is_list());
70 base::Value::ConstListView status_list = wifi_status->GetList();
71 EXPECT_EQ(num_of_status, static_cast<int>(status_list.size()));
72 for (int i = 0; i < num_of_status; ++i) {
73 double snr = -1;
74 int32_t speed = -1;
75 int32_t timestamp = 0;
76 EXPECT_TRUE(GetDouble(status_list[i], "wifiSnr", &snr));
77 EXPECT_EQ(starting_snr + i, snr);
78 EXPECT_TRUE(GetInt(status_list[i], "wifiSpeed", &speed));
79 EXPECT_EQ(starting_speed + i, speed);
80 EXPECT_TRUE(GetInt(status_list[i], "timestamp", ×tamp));
81 }
82 }
83
84 } // namespace
85
86 class SessionMonitorTest : public mojom::CastMessageChannel,
87 public ::testing::Test {
88 public:
SessionMonitorTest()89 SessionMonitorTest()
90 : receiver_address_(media::cast::test::GetFreeLocalPort().address()),
91 message_dispatcher_(receiver_.BindNewPipeAndPassRemote(),
92 inbound_channel_.BindNewPipeAndPassReceiver(),
93 error_callback_.Get()) {}
~SessionMonitorTest()94 ~SessionMonitorTest() override {}
95
96 protected:
97 // mojom::CastMessageChannel implementation.
98 MOCK_METHOD1(Send, void(mojom::CastMessagePtr));
99
CreateSessionMonitor(int max_bytes,std::string * expected_settings)100 void CreateSessionMonitor(int max_bytes, std::string* expected_settings) {
101 EXPECT_CALL(*this, Send(::testing::_)).Times(::testing::AtLeast(1));
102 mojo::PendingRemote<network::mojom::URLLoaderFactory> url_loader_factory;
103 auto test_url_loader_factory =
104 std::make_unique<network::TestURLLoaderFactory>();
105 url_loader_factory_ = test_url_loader_factory.get();
106 mojo::MakeSelfOwnedReceiver(
107 std::move(test_url_loader_factory),
108 url_loader_factory.InitWithNewPipeAndPassReceiver());
109 MirrorSettings mirror_settings;
110 base::Value session_tags(base::Value::Type::DICTIONARY);
111 base::Value settings = mirror_settings.ToDictionaryValue();
112 if (expected_settings)
113 EXPECT_TRUE(base::JSONWriter::Write(settings, expected_settings));
114 session_tags.SetKey("mirrorSettings", std::move(settings));
115 session_tags.SetKey("receiverProductName", base::Value("ChromeCast"));
116 session_tags.SetKey("shouldCaptureAudio", base::Value(true));
117 session_tags.SetKey("shouldCaptureVideo", base::Value(true));
118 session_monitor_ = std::make_unique<SessionMonitor>(
119 max_bytes, receiver_address_, std::move(session_tags),
120 std::move(url_loader_factory));
121 }
122
123 // Generates and sends |num_of_responses| WiFi status.
SendWifiStatus(double starting_snr,int starting_speed,int num_of_responses)124 void SendWifiStatus(double starting_snr,
125 int starting_speed,
126 int num_of_responses) {
127 for (int i = 0; i < num_of_responses; ++i) {
128 CastMessage message;
129 message.message_namespace = mojom::kWebRtcNamespace;
130 message.json_format_data =
131 "{\"seqNum\":" +
132 std::to_string(message_dispatcher_.GetNextSeqNumber()) +
133 ","
134 "\"type\": \"STATUS_RESPONSE\","
135 "\"result\": \"ok\","
136 "\"status\": {"
137 "\"wifiSnr\":" +
138 std::to_string(starting_snr + i) +
139 ","
140 "\"wifiSpeed\": [1234, 5678, 3000, " +
141 std::to_string(starting_speed + i) +
142 "],"
143 "\"wifiFcsError\": [12, 13, 12, 12]}" // This will be ignored.
144 "}";
145 inbound_channel_->Send(message.Clone());
146 task_environment_.RunUntilIdle();
147 }
148 }
149
StartStreamingSession()150 void StartStreamingSession() {
151 cast_environment_ = new media::cast::CastEnvironment(
152 base::DefaultTickClock::GetInstance(),
153 task_environment_.GetMainThreadTaskRunner(),
154 task_environment_.GetMainThreadTaskRunner(),
155 task_environment_.GetMainThreadTaskRunner());
156 EXPECT_TRUE(session_monitor_);
157 auto wifi_status_monitor =
158 std::make_unique<WifiStatusMonitor>(&message_dispatcher_);
159 session_monitor_->StartStreamingSession(
160 cast_environment_, std::move(wifi_status_monitor),
161 SessionMonitor::AUDIO_AND_VIDEO, false /* is_remoting */);
162 task_environment_.RunUntilIdle();
163 }
164
StopStreamingSession()165 void StopStreamingSession() {
166 EXPECT_TRUE(session_monitor_);
167 session_monitor_->StopStreamingSession();
168 cast_environment_ = nullptr;
169 task_environment_.RunUntilIdle();
170 }
171
AssembleBundleAndVerify(const std::vector<int32_t> & bundle_sizes)172 std::vector<SessionMonitor::EventsAndStats> AssembleBundleAndVerify(
173 const std::vector<int32_t>& bundle_sizes) {
174 std::vector<SessionMonitor::EventsAndStats> bundles =
175 session_monitor_->AssembleBundlesAndClear(bundle_sizes);
176 task_environment_.RunUntilIdle();
177 EXPECT_EQ(bundle_sizes.size(), bundles.size());
178 for (size_t i = 0; i < bundles.size(); ++i) {
179 EXPECT_FALSE(bundles[i].first.empty());
180 EXPECT_FALSE(bundles[i].second.empty());
181 EXPECT_LE(
182 static_cast<int>(bundles[i].first.size() + bundles[i].second.size()),
183 bundle_sizes[i]);
184 }
185 return bundles;
186 }
187
ReadStats(const std::string & stats_string)188 base::Value ReadStats(const std::string& stats_string) {
189 std::unique_ptr<base::Value> stats_ptr =
190 base::JSONReader::ReadDeprecated(stats_string);
191 EXPECT_TRUE(stats_ptr);
192 base::Value stats = base::Value::FromUniquePtrValue(std::move(stats_ptr));
193 EXPECT_TRUE(stats.is_list());
194 return stats;
195 }
196
SendReceiverSetupInfo(const std::string & setup_info)197 void SendReceiverSetupInfo(const std::string& setup_info) {
198 url_loader_factory_->AddResponse(
199 "http://" + receiver_address_.ToString() + ":8008/setup/eureka_info",
200 setup_info);
201 task_environment_.RunUntilIdle();
202 }
203
TakeSnapshot()204 void TakeSnapshot() {
205 ASSERT_TRUE(session_monitor_);
206 session_monitor_->TakeSnapshot();
207 task_environment_.RunUntilIdle();
208 }
209
ReportError(SessionError error)210 void ReportError(SessionError error) {
211 ASSERT_TRUE(session_monitor_);
212 session_monitor_->OnStreamingError(error);
213 task_environment_.RunUntilIdle();
214 }
215
216 private:
217 base::test::TaskEnvironment task_environment_;
218 const net::IPAddress receiver_address_;
219 mojo::Receiver<mojom::CastMessageChannel> receiver_{this};
220 mojo::Remote<mojom::CastMessageChannel> inbound_channel_;
221 base::MockCallback<MessageDispatcher::ErrorCallback> error_callback_;
222 MessageDispatcher message_dispatcher_;
223 network::TestURLLoaderFactory* url_loader_factory_ = nullptr;
224 std::unique_ptr<SessionMonitor> session_monitor_;
225 scoped_refptr<media::cast::CastEnvironment> cast_environment_ = nullptr;
226
227 DISALLOW_COPY_AND_ASSIGN(SessionMonitorTest);
228 };
229
TEST_F(SessionMonitorTest,ProvidesExpectedTags)230 TEST_F(SessionMonitorTest, ProvidesExpectedTags) {
231 std::string expected_settings;
232 CreateSessionMonitor(kRetentionBytes, &expected_settings);
233 StartStreamingSession();
234 SendWifiStatus(34, 2000, 5);
235 std::vector<int32_t> bundle_sizes({kRetentionBytes});
236 std::vector<SessionMonitor::EventsAndStats> bundles =
237 AssembleBundleAndVerify(bundle_sizes);
238
239 base::Value stats = ReadStats(bundles[0].second);
240 base::Value::ConstListView stats_list = stats.GetList();
241 ASSERT_EQ(1u, stats_list.size());
242 // Verify tags.
243 EXPECT_TRUE(stats_list[0].is_dict());
244 auto* found = stats_list[0].FindKey("video");
245 EXPECT_TRUE(found && found->is_dict());
246 found = stats_list[0].FindKey("audio");
247 EXPECT_TRUE(found && found->is_dict());
248 found = stats_list[0].FindKey("tags");
249 EXPECT_TRUE(found && found->is_dict());
250 // Verify session tags.
251 VerifyStringValue(*found, "activity", "audio+video streaming");
252 VerifyStringValue(*found, "receiverProductName", "ChromeCast");
253 VerifyBoolValue(*found, "shouldCaptureAudio", true);
254 VerifyBoolValue(*found, "shouldCaptureVideo", true);
255 auto* settings = found->FindKey("mirrorSettings");
256 EXPECT_TRUE(settings && settings->is_dict());
257 std::string settings_string;
258 EXPECT_TRUE(base::JSONWriter::Write(*settings, &settings_string));
259 EXPECT_EQ(expected_settings, settings_string);
260 VerifyWifiStatus(stats_list[0], 34, 2000, 5);
261 }
262
263 // Test for multiple streaming sessions.
TEST_F(SessionMonitorTest,MultipleSessions)264 TEST_F(SessionMonitorTest, MultipleSessions) {
265 CreateSessionMonitor(kRetentionBytes, nullptr);
266 StartStreamingSession();
267 StopStreamingSession();
268 // Starts the second streaming session.
269 StartStreamingSession();
270 StopStreamingSession();
271 std::vector<int32_t> bundle_sizes({kRetentionBytes});
272 std::vector<SessionMonitor::EventsAndStats> bundles =
273 AssembleBundleAndVerify(bundle_sizes);
274 base::Value stats = ReadStats(bundles[0].second);
275 base::Value::ConstListView stats_list = stats.GetList();
276 // There should be two sessions in the recorded stats.
277 EXPECT_EQ(2u, stats_list.size());
278 }
279
TEST_F(SessionMonitorTest,ConfigureMaxRetentionBytes)280 TEST_F(SessionMonitorTest, ConfigureMaxRetentionBytes) {
281 // 2500 is an estimate number of bytes for a snapshot that includes tags and
282 // five WiFi status records.
283 CreateSessionMonitor(2500, nullptr);
284 StartStreamingSession();
285 SendWifiStatus(34, 2000, 5);
286 StopStreamingSession();
287 StartStreamingSession();
288 SendWifiStatus(54, 3000, 5);
289 StopStreamingSession();
290 std::vector<int32_t> bundle_sizes({kRetentionBytes});
291 std::vector<SessionMonitor::EventsAndStats> bundles =
292 AssembleBundleAndVerify(bundle_sizes);
293 base::Value stats = ReadStats(bundles[0].second);
294 base::Value::ConstListView stats_list = stats.GetList();
295 // Expect to only record the second session.
296 ASSERT_EQ(1u, stats_list.size());
297 VerifyWifiStatus(stats_list[0], 54, 3000, 5);
298 }
299
TEST_F(SessionMonitorTest,AssembleBundlesWithVaryingSizes)300 TEST_F(SessionMonitorTest, AssembleBundlesWithVaryingSizes) {
301 CreateSessionMonitor(kRetentionBytes, nullptr);
302 StartStreamingSession();
303 SendWifiStatus(34, 2000, 5);
304 StopStreamingSession();
305 StartStreamingSession();
306 SendWifiStatus(54, 3000, 5);
307 StopStreamingSession();
308 std::vector<int32_t> bundle_sizes({2500, kRetentionBytes});
309 std::vector<SessionMonitor::EventsAndStats> bundles =
310 AssembleBundleAndVerify(bundle_sizes);
311
312 // Expect the first bundle has only one session.
313 base::Value stats = ReadStats(bundles[0].second);
314 base::Value::ConstListView stats_list = stats.GetList();
315 // Expect to only record the second session.
316 ASSERT_EQ(1u, stats_list.size());
317 VerifyWifiStatus(stats_list[0], 54, 3000, 5);
318
319 // Expect the second bundle has both sessions.
320 stats = ReadStats(bundles[1].second);
321 base::Value::ConstListView stats_list2 = stats.GetList();
322 ASSERT_EQ(2u, stats_list2.size());
323 VerifyWifiStatus(stats_list2[0], 34, 2000, 5);
324 VerifyWifiStatus(stats_list2[1], 54, 3000, 5);
325 }
326
TEST_F(SessionMonitorTest,ErrorTags)327 TEST_F(SessionMonitorTest, ErrorTags) {
328 CreateSessionMonitor(kRetentionBytes, nullptr);
329 StartStreamingSession();
330 TakeSnapshot(); // Take the first snapshot.
331 ReportError(SessionError::VIDEO_CAPTURE_ERROR);
332 ReportError(SessionError::RTP_STREAM_ERROR);
333 TakeSnapshot(); // Take the second snapshot.
334 StopStreamingSession(); // Take the third snapshot.
335
336 std::vector<int32_t> bundle_sizes({kRetentionBytes});
337 std::vector<SessionMonitor::EventsAndStats> bundles =
338 AssembleBundleAndVerify(bundle_sizes);
339 base::Value stats = ReadStats(bundles[0].second);
340 base::Value::ConstListView stats_list = stats.GetList();
341 // There should be three snapshots in the bundle.
342 ASSERT_EQ(3u, stats_list.size());
343
344 // The first and the third snapshots should have no error tags.
345 auto* tags = stats_list[0].FindKey("tags");
346 ASSERT_TRUE(tags);
347 EXPECT_FALSE(tags->FindKey("streamingErrorTime"));
348 EXPECT_FALSE(tags->FindKey("streamingErrorMessage"));
349 tags = stats_list[2].FindKey("tags");
350 ASSERT_TRUE(tags);
351 EXPECT_FALSE(tags->FindKey("streamingErrorTime"));
352 EXPECT_FALSE(tags->FindKey("streamingErrorMessage"));
353
354 // The second snapshot should have the error tags. Only the first error is
355 // recorded.
356 tags = stats_list[1].FindKey("tags");
357 ASSERT_TRUE(tags && tags->FindKey("streamingErrorTime"));
358 VerifyStringValue(*tags, "streamingErrorMessage", "Video capture error");
359 }
360
TEST_F(SessionMonitorTest,ReceiverSetupInfo)361 TEST_F(SessionMonitorTest, ReceiverSetupInfo) {
362 CreateSessionMonitor(kRetentionBytes, nullptr);
363 StartStreamingSession();
364 // This snapshot should have no receiver setup info tags.
365 TakeSnapshot();
366
367 const std::string receiver_setup_info =
368 "{"
369 "\"cast_build_revision\": \"1.26.0.1\","
370 "\"connected\": true,"
371 "\"ethernet_connected\": false,"
372 "\"has_update\": false,"
373 "\"uptime\": 13253.6 }";
374
375 SendReceiverSetupInfo(receiver_setup_info);
376
377 // A final snapshot is taken and should have receiver setup info tags.
378 StopStreamingSession();
379
380 std::vector<int32_t> bundle_sizes({kRetentionBytes});
381 std::vector<SessionMonitor::EventsAndStats> bundles =
382 AssembleBundleAndVerify(bundle_sizes);
383 base::Value stats = ReadStats(bundles[0].second);
384 base::Value::ConstListView stats_list = stats.GetList();
385 // There should be two snapshots in the bundle.
386 EXPECT_EQ(2u, stats_list.size());
387
388 // The first snapshot should have no receiver setup info tags.
389 auto* tags = stats_list[0].FindKey("tags");
390 ASSERT_TRUE(tags);
391 EXPECT_FALSE(tags->FindKey("receiverVersion"));
392 EXPECT_FALSE(tags->FindKey("receiverConnected"));
393 EXPECT_FALSE(tags->FindKey("receiverOnEthernet"));
394 EXPECT_FALSE(tags->FindKey("receiverHasUpdatePending"));
395 EXPECT_FALSE(tags->FindKey("receiverUptimeSeconds"));
396
397 // The second snapshot should have the receiver setup info tags.
398 tags = stats_list[1].FindKey("tags");
399 ASSERT_TRUE(tags);
400 VerifyStringValue(*tags, "receiverVersion", "1.26.0.1");
401 VerifyBoolValue(*tags, "receiverConnected", true);
402 VerifyBoolValue(*tags, "receiverOnEthernet", false);
403 VerifyBoolValue(*tags, "receiverHasUpdatePending", false);
404 VerifyDoubleValue(*tags, "receiverUptimeSeconds", 13253.6);
405 }
406
407 } // namespace mirroring
408