1 // Copyright 2013 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 "base/json/json_writer.h"
6 #include "base/strings/string_split.h"
7 #include "chrome/browser/browser_process.h"
8 #include "chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_api.h"
9 #include "chrome/browser/extensions/extension_apitest.h"
10 #include "chrome/browser/extensions/extension_function_test_utils.h"
11 #include "chrome/browser/extensions/extension_tab_util.h"
12 #include "chrome/browser/media/webrtc_log_uploader.h"
13 #include "chrome/browser/ui/tabs/tab_strip_model.h"
14 #include "content/public/browser/notification_service.h"
15 #include "content/public/test/test_utils.h"
16 #include "extensions/common/test_util.h"
18 using extensions::Extension
;
19 using extensions::WebrtcLoggingPrivateDiscardFunction
;
20 using extensions::WebrtcLoggingPrivateSetMetaDataFunction
;
21 using extensions::WebrtcLoggingPrivateStartFunction
;
22 using extensions::WebrtcLoggingPrivateStartRtpDumpFunction
;
23 using extensions::WebrtcLoggingPrivateStopFunction
;
24 using extensions::WebrtcLoggingPrivateStopRtpDumpFunction
;
25 using extensions::WebrtcLoggingPrivateStoreFunction
;
26 using extensions::WebrtcLoggingPrivateUploadFunction
;
27 using extensions::WebrtcLoggingPrivateUploadStoredFunction
;
29 namespace utils
= extension_function_test_utils
;
33 static const char kTestLoggingSessionId
[] = "0123456789abcdef";
34 static const char kTestLoggingUrl
[] = "dummy url string";
36 std::string
ParamsToString(const base::ListValue
& parameters
) {
37 std::string parameter_string
;
38 EXPECT_TRUE(base::JSONWriter::Write(parameters
, ¶meter_string
));
39 return parameter_string
;
42 void InitializeTestMetaData(base::ListValue
* parameters
) {
43 base::DictionaryValue
* meta_data_entry
= new base::DictionaryValue();
44 meta_data_entry
->SetString("key", "app_session_id");
45 meta_data_entry
->SetString("value", kTestLoggingSessionId
);
46 base::ListValue
* meta_data
= new base::ListValue();
47 meta_data
->Append(meta_data_entry
);
48 meta_data_entry
= new base::DictionaryValue();
49 meta_data_entry
->SetString("key", "url");
50 meta_data_entry
->SetString("value", kTestLoggingUrl
);
51 meta_data
->Append(meta_data_entry
);
52 parameters
->Append(meta_data
);
55 class WebrtcLoggingPrivateApiTest
: public ExtensionApiTest
{
58 void SetUp() override
{
59 ExtensionApiTest::SetUp();
60 extension_
= extensions::test_util::CreateEmptyExtension();
64 scoped_refptr
<T
> CreateFunction() {
65 scoped_refptr
<T
> function(new T());
66 function
->set_extension(extension_
.get());
67 function
->set_has_callback(true);
71 content::WebContents
* web_contents() {
72 return browser()->tab_strip_model()->GetActiveWebContents();
75 void AppendTabIdAndUrl(base::ListValue
* parameters
) {
76 base::DictionaryValue
* request_info
= new base::DictionaryValue();
77 request_info
->SetInteger(
78 "tabId", extensions::ExtensionTabUtil::GetTabId(web_contents()));
79 parameters
->Append(request_info
);
80 parameters
->AppendString(web_contents()->GetURL().GetOrigin().spec());
83 bool RunFunction(UIThreadExtensionFunction
* function
,
84 const base::ListValue
& parameters
,
85 bool expect_results
) {
86 scoped_ptr
<base::Value
> result(utils::RunFunctionAndReturnSingleResult(
87 function
, ParamsToString(parameters
), browser()));
89 EXPECT_TRUE(result
.get());
90 return result
.get() != nullptr;
93 EXPECT_FALSE(result
.get());
94 return result
.get() == nullptr;
97 template<typename Function
>
98 bool RunFunction(const base::ListValue
& parameters
, bool expect_results
) {
99 scoped_refptr
<Function
> function(CreateFunction
<Function
>());
100 return RunFunction(function
.get(), parameters
, expect_results
);
103 template<typename Function
>
104 bool RunNoArgsFunction(bool expect_results
) {
105 base::ListValue params
;
106 AppendTabIdAndUrl(¶ms
);
107 scoped_refptr
<Function
> function(CreateFunction
<Function
>());
108 return RunFunction(function
.get(), params
, expect_results
);
111 bool StartLogging() {
112 return RunNoArgsFunction
<WebrtcLoggingPrivateStartFunction
>(false);
116 return RunNoArgsFunction
<WebrtcLoggingPrivateStopFunction
>(false);
120 return RunNoArgsFunction
<WebrtcLoggingPrivateDiscardFunction
>(false);
124 return RunNoArgsFunction
<WebrtcLoggingPrivateUploadFunction
>(true);
127 bool SetMetaData(const base::ListValue
& data
) {
128 return RunFunction
<WebrtcLoggingPrivateSetMetaDataFunction
>(data
, false);
131 bool StartRtpDump(bool incoming
, bool outgoing
) {
132 base::ListValue params
;
133 AppendTabIdAndUrl(¶ms
);
134 params
.AppendBoolean(incoming
);
135 params
.AppendBoolean(outgoing
);
136 return RunFunction
<WebrtcLoggingPrivateStartRtpDumpFunction
>(params
, false);
139 bool StopRtpDump(bool incoming
, bool outgoing
) {
140 base::ListValue params
;
141 AppendTabIdAndUrl(¶ms
);
142 params
.AppendBoolean(incoming
);
143 params
.AppendBoolean(outgoing
);
144 return RunFunction
<WebrtcLoggingPrivateStopRtpDumpFunction
>(params
, false);
147 bool StoreLog(const std::string
& log_id
) {
148 base::ListValue params
;
149 AppendTabIdAndUrl(¶ms
);
150 params
.AppendString(log_id
);
151 return RunFunction
<WebrtcLoggingPrivateStoreFunction
>(params
, false);
154 bool UploadStoredLog(const std::string
& log_id
) {
155 base::ListValue params
;
156 AppendTabIdAndUrl(¶ms
);
157 params
.AppendString(log_id
);
158 return RunFunction
<WebrtcLoggingPrivateUploadStoredFunction
>(params
, true);
162 scoped_refptr
<Extension
> extension_
;
165 // Helper class to temporarily tell the uploader to save the multipart buffer to
166 // a test string instead of uploading.
167 class ScopedOverrideUploadBuffer
{
169 ScopedOverrideUploadBuffer() {
170 g_browser_process
->webrtc_log_uploader()->
171 OverrideUploadWithBufferForTesting(&multipart_
);
174 ~ScopedOverrideUploadBuffer() {
175 g_browser_process
->webrtc_log_uploader()->
176 OverrideUploadWithBufferForTesting(nullptr);
179 const std::string
& multipart() const { return multipart_
; }
182 std::string multipart_
;
187 IN_PROC_BROWSER_TEST_F(WebrtcLoggingPrivateApiTest
, TestStartStopDiscard
) {
188 ScopedOverrideUploadBuffer buffer_override
;
190 EXPECT_TRUE(StartLogging());
191 EXPECT_TRUE(StopLogging());
192 EXPECT_TRUE(DiscardLog());
194 EXPECT_TRUE(buffer_override
.multipart().empty());
197 // Tests WebRTC diagnostic logging. Sets up the browser to save the multipart
198 // contents to a buffer instead of uploading it, then verifies it after a calls.
199 // Example of multipart contents:
200 // ------**--yradnuoBgoLtrapitluMklaTelgooG--**----
201 // Content-Disposition: form-data; name="prod"
204 // ------**--yradnuoBgoLtrapitluMklaTelgooG--**----
205 // Content-Disposition: form-data; name="ver"
208 // ------**--yradnuoBgoLtrapitluMklaTelgooG--**----
209 // Content-Disposition: form-data; name="guid"
212 // ------**--yradnuoBgoLtrapitluMklaTelgooG--**----
213 // Content-Disposition: form-data; name="type"
216 // ------**--yradnuoBgoLtrapitluMklaTelgooG--**----
217 // Content-Disposition: form-data; name="app_session_id"
220 // ------**--yradnuoBgoLtrapitluMklaTelgooG--**----
221 // Content-Disposition: form-data; name="url"
223 // http://127.0.0.1:43213/webrtc/webrtc_jsep01_test.html
224 // ------**--yradnuoBgoLtrapitluMklaTelgooG--**----
225 // Content-Disposition: form-data; name="webrtc_log"; filename="webrtc_log.gz"
226 // Content-Type: application/gzip
228 // <compressed data (zip)>
229 // ------**--yradnuoBgoLtrapitluMklaTelgooG--**------
231 IN_PROC_BROWSER_TEST_F(WebrtcLoggingPrivateApiTest
, TestStartStopUpload
) {
232 ScopedOverrideUploadBuffer buffer_override
;
234 base::ListValue parameters
;
235 AppendTabIdAndUrl(¶meters
);
236 InitializeTestMetaData(¶meters
);
238 SetMetaData(parameters
);
244 std::string multipart
= buffer_override
.multipart();
245 ASSERT_FALSE(multipart
.empty());
247 // Check multipart data.
249 const char boundary
[] = "------**--yradnuoBgoLtrapitluMklaTelgooG--**----";
251 // Remove the compressed data, it may contain "\r\n". Just verify that its
253 const char zip_content_type
[] = "Content-Type: application/gzip";
254 size_t zip_pos
= multipart
.find(&zip_content_type
[0]);
255 ASSERT_NE(std::string::npos
, zip_pos
);
256 // Move pos to where the zip begins. - 1 to remove '\0', + 4 for two "\r\n".
257 zip_pos
+= sizeof(zip_content_type
) + 3;
258 size_t zip_length
= multipart
.find(boundary
, zip_pos
);
259 ASSERT_NE(std::string::npos
, zip_length
);
260 // Calculate length, adjust for a "\r\n".
261 zip_length
-= zip_pos
+ 2;
262 ASSERT_GT(zip_length
, 0u);
263 multipart
.erase(zip_pos
, zip_length
);
265 // Check the multipart contents.
266 std::vector
<std::string
> multipart_lines
;
267 base::SplitStringUsingSubstr(multipart
, "\r\n", &multipart_lines
);
268 ASSERT_EQ(31, static_cast<int>(multipart_lines
.size()));
270 EXPECT_STREQ(&boundary
[0], multipart_lines
[0].c_str());
271 EXPECT_STREQ("Content-Disposition: form-data; name=\"prod\"",
272 multipart_lines
[1].c_str());
273 EXPECT_TRUE(multipart_lines
[2].empty());
274 EXPECT_NE(std::string::npos
, multipart_lines
[3].find("Chrome"));
276 EXPECT_STREQ(&boundary
[0], multipart_lines
[4].c_str());
277 EXPECT_STREQ("Content-Disposition: form-data; name=\"ver\"",
278 multipart_lines
[5].c_str());
279 EXPECT_TRUE(multipart_lines
[6].empty());
280 // Just check that the version contains a dot.
281 EXPECT_NE(std::string::npos
, multipart_lines
[7].find('.'));
283 EXPECT_STREQ(&boundary
[0], multipart_lines
[8].c_str());
284 EXPECT_STREQ("Content-Disposition: form-data; name=\"guid\"",
285 multipart_lines
[9].c_str());
286 EXPECT_TRUE(multipart_lines
[10].empty());
287 EXPECT_STREQ("0", multipart_lines
[11].c_str());
289 EXPECT_STREQ(&boundary
[0], multipart_lines
[12].c_str());
290 EXPECT_STREQ("Content-Disposition: form-data; name=\"type\"",
291 multipart_lines
[13].c_str());
292 EXPECT_TRUE(multipart_lines
[14].empty());
293 EXPECT_STREQ("webrtc_log", multipart_lines
[15].c_str());
295 EXPECT_STREQ(&boundary
[0], multipart_lines
[16].c_str());
296 EXPECT_STREQ("Content-Disposition: form-data; name=\"app_session_id\"",
297 multipart_lines
[17].c_str());
298 EXPECT_TRUE(multipart_lines
[18].empty());
299 EXPECT_STREQ(kTestLoggingSessionId
, multipart_lines
[19].c_str());
301 EXPECT_STREQ(&boundary
[0], multipart_lines
[20].c_str());
302 EXPECT_STREQ("Content-Disposition: form-data; name=\"url\"",
303 multipart_lines
[21].c_str());
304 EXPECT_TRUE(multipart_lines
[22].empty());
305 EXPECT_STREQ(kTestLoggingUrl
, multipart_lines
[23].c_str());
307 EXPECT_STREQ(&boundary
[0], multipart_lines
[24].c_str());
308 EXPECT_STREQ("Content-Disposition: form-data; name=\"webrtc_log\";"
309 " filename=\"webrtc_log.gz\"",
310 multipart_lines
[25].c_str());
311 EXPECT_STREQ("Content-Type: application/gzip",
312 multipart_lines
[26].c_str());
313 EXPECT_TRUE(multipart_lines
[27].empty());
314 EXPECT_TRUE(multipart_lines
[28].empty()); // The removed zip part.
315 std::string final_delimiter
= boundary
;
316 final_delimiter
+= "--";
317 EXPECT_STREQ(final_delimiter
.c_str(), multipart_lines
[29].c_str());
318 EXPECT_TRUE(multipart_lines
[30].empty());
321 IN_PROC_BROWSER_TEST_F(WebrtcLoggingPrivateApiTest
, TestStartStopRtpDump
) {
322 // TODO(tommi): As is, these tests are missing verification of the actual
323 // RTP dump data. We should fix that, e.g. use SetDumpWriterForTesting.
324 StartRtpDump(true, true);
325 StopRtpDump(true, true);
328 // Tests trying to store a log when a log is not being captured.
329 // We should get a failure callback in this case.
330 IN_PROC_BROWSER_TEST_F(WebrtcLoggingPrivateApiTest
, TestStoreWithoutLog
) {
331 base::ListValue parameters
;
332 AppendTabIdAndUrl(¶meters
);
333 parameters
.AppendString("MyLogId");
334 scoped_refptr
<WebrtcLoggingPrivateStoreFunction
> store(
335 CreateFunction
<WebrtcLoggingPrivateStoreFunction
>());
336 const std::string error
= utils::RunFunctionAndReturnError(
337 store
.get(), ParamsToString(parameters
), browser());
338 ASSERT_FALSE(error
.empty());
341 IN_PROC_BROWSER_TEST_F(WebrtcLoggingPrivateApiTest
, TestStartStopStore
) {
342 ASSERT_TRUE(StartLogging());
343 ASSERT_TRUE(StopLogging());
344 EXPECT_TRUE(StoreLog("MyLogID"));
347 IN_PROC_BROWSER_TEST_F(WebrtcLoggingPrivateApiTest
,
348 TestStartStopStoreAndUpload
) {
349 static const char kLogId
[] = "TestStartStopStoreAndUpload";
350 ASSERT_TRUE(StartLogging());
351 ASSERT_TRUE(StopLogging());
352 ASSERT_TRUE(StoreLog(kLogId
));
354 ScopedOverrideUploadBuffer buffer_override
;
355 EXPECT_TRUE(UploadStoredLog(kLogId
));
356 EXPECT_NE(std::string::npos
,
357 buffer_override
.multipart().find("filename=\"webrtc_log.gz\""));
360 IN_PROC_BROWSER_TEST_F(WebrtcLoggingPrivateApiTest
,
361 TestStartStopStoreAndUploadWithRtp
) {
362 static const char kLogId
[] = "TestStartStopStoreAndUploadWithRtp";
363 ASSERT_TRUE(StartLogging());
364 ASSERT_TRUE(StartRtpDump(true, true));
365 ASSERT_TRUE(StopLogging());
366 ASSERT_TRUE(StopRtpDump(true, true));
367 ASSERT_TRUE(StoreLog(kLogId
));
369 ScopedOverrideUploadBuffer buffer_override
;
370 EXPECT_TRUE(UploadStoredLog(kLogId
));
371 EXPECT_NE(std::string::npos
,
372 buffer_override
.multipart().find("filename=\"webrtc_log.gz\""));
375 IN_PROC_BROWSER_TEST_F(WebrtcLoggingPrivateApiTest
,
376 TestStartStopStoreAndUploadWithMetaData
) {
377 static const char kLogId
[] = "TestStartStopStoreAndUploadWithRtp";
378 ASSERT_TRUE(StartLogging());
380 base::ListValue parameters
;
381 AppendTabIdAndUrl(¶meters
);
382 InitializeTestMetaData(¶meters
);
383 SetMetaData(parameters
);
385 ASSERT_TRUE(StopLogging());
386 ASSERT_TRUE(StoreLog(kLogId
));
388 ScopedOverrideUploadBuffer buffer_override
;
389 EXPECT_TRUE(UploadStoredLog(kLogId
));
390 EXPECT_NE(std::string::npos
,
391 buffer_override
.multipart().find("filename=\"webrtc_log.gz\""));
392 EXPECT_NE(std::string::npos
,
393 buffer_override
.multipart().find(kTestLoggingUrl
));