| // Copyright 2013 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 <stddef.h> |
| |
| #include <memory> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/command_line.h" |
| #include "base/json/json_writer.h" |
| #include "base/strings/string_split.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/test/scoped_command_line.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_api.h" |
| #include "chrome/browser/extensions/extension_apitest.h" |
| #include "chrome/browser/extensions/extension_function_test_utils.h" |
| #include "chrome/browser/extensions/extension_tab_util.h" |
| #include "chrome/browser/media/webrtc/webrtc_event_log_manager.h" |
| #include "chrome/browser/media/webrtc/webrtc_event_log_manager_common.h" |
| #include "chrome/browser/media/webrtc/webrtc_log_uploader.h" |
| #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| #include "chrome/common/chrome_features.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "components/policy/core/browser/browser_policy_connector.h" |
| #include "components/policy/core/common/mock_configuration_policy_provider.h" |
| #include "components/policy/policy_constants.h" |
| #include "content/public/browser/notification_service.h" |
| #include "content/public/browser/render_view_host.h" |
| #include "content/public/test/test_utils.h" |
| #include "extensions/common/extension_builder.h" |
| #include "net/test/embedded_test_server/embedded_test_server.h" |
| #include "net/test/embedded_test_server/http_request.h" |
| #include "net/test/embedded_test_server/http_response.h" |
| #include "third_party/zlib/google/compression_utils.h" |
| |
| using compression::GzipUncompress; |
| using extensions::Extension; |
| using extensions::WebrtcLoggingPrivateDiscardFunction; |
| using extensions::WebrtcLoggingPrivateSetMetaDataFunction; |
| using extensions::WebrtcLoggingPrivateStartAudioDebugRecordingsFunction; |
| using extensions::WebrtcLoggingPrivateStartEventLoggingFunction; |
| using extensions::WebrtcLoggingPrivateStartFunction; |
| using extensions::WebrtcLoggingPrivateStartRtpDumpFunction; |
| using extensions::WebrtcLoggingPrivateStopAudioDebugRecordingsFunction; |
| using extensions::WebrtcLoggingPrivateStopFunction; |
| using extensions::WebrtcLoggingPrivateStopRtpDumpFunction; |
| using extensions::WebrtcLoggingPrivateStoreFunction; |
| using extensions::WebrtcLoggingPrivateUploadFunction; |
| using extensions::WebrtcLoggingPrivateUploadStoredFunction; |
| using webrtc_event_logging::kMaxOutputPeriodMs; |
| using webrtc_event_logging::kMaxRemoteLogFileSizeBytes; |
| using webrtc_event_logging::kStartRemoteLoggingFailureAlreadyLogging; |
| using webrtc_event_logging::kStartRemoteLoggingFailureFeatureDisabled; |
| using webrtc_event_logging::kStartRemoteLoggingFailureMaxSizeTooLarge; |
| using webrtc_event_logging::kStartRemoteLoggingFailureMaxSizeTooSmall; |
| using webrtc_event_logging::kStartRemoteLoggingFailureOutputPeriodMsTooLarge; |
| using webrtc_event_logging:: |
| kStartRemoteLoggingFailureUnknownOrInactivePeerConnection; |
| using webrtc_event_logging::kStartRemoteLoggingFailureUnlimitedSizeDisallowed; |
| using webrtc_event_logging::kWebRtcEventLogManagerUnlimitedFileSize; |
| using webrtc_event_logging::WebRtcEventLogManager; |
| |
| namespace utils = extension_function_test_utils; |
| |
| namespace { |
| |
| static const char kTestLoggingSessionIdKey[] = "app_session_id"; |
| static const char kTestLoggingSessionIdValue[] = "0123456789abcdef"; |
| static const char kTestLoggingUrl[] = "dummy url string"; |
| |
| constexpr int kWebAppId = 15; // Arbitrary. |
| |
| constexpr char kTestUploadUrlPath[] = "/upload_webrtc_log"; |
| constexpr char kTestReportId[] = "report_id"; |
| |
| std::string ParamsToString(const base::ListValue& parameters) { |
| std::string parameter_string; |
| EXPECT_TRUE(base::JSONWriter::Write(parameters, ¶meter_string)); |
| return parameter_string; |
| } |
| |
| void InitializeTestMetaData(base::ListValue* parameters) { |
| std::unique_ptr<base::DictionaryValue> meta_data_entry( |
| new base::DictionaryValue()); |
| meta_data_entry->SetString("key", kTestLoggingSessionIdKey); |
| meta_data_entry->SetString("value", kTestLoggingSessionIdValue); |
| std::unique_ptr<base::ListValue> meta_data(new base::ListValue()); |
| meta_data->Append(std::move(meta_data_entry)); |
| meta_data_entry.reset(new base::DictionaryValue()); |
| meta_data_entry->SetString("key", "url"); |
| meta_data_entry->SetString("value", kTestLoggingUrl); |
| meta_data->Append(std::move(meta_data_entry)); |
| parameters->Append(std::move(meta_data)); |
| } |
| |
| class WebrtcLoggingPrivateApiTest : public extensions::ExtensionApiTest { |
| protected: |
| void SetUp() override { |
| extensions::ExtensionApiTest::SetUp(); |
| extension_ = extensions::ExtensionBuilder("Test").Build(); |
| } |
| |
| template<typename T> |
| scoped_refptr<T> CreateFunction() { |
| scoped_refptr<T> function(new T()); |
| function->set_extension(extension_.get()); |
| function->set_has_callback(true); |
| return function; |
| } |
| |
| // Overriding can use incognito session instead, etc. |
| virtual Browser* GetBrowser() { return browser(); } |
| |
| content::WebContents* web_contents() { |
| return GetBrowser()->tab_strip_model()->GetActiveWebContents(); |
| } |
| |
| bool SetupTestServerLogUploading() { |
| embedded_test_server()->RegisterRequestHandler( |
| base::BindRepeating(&WebrtcLoggingPrivateApiTest::HandleServerRequest, |
| base::Unretained(this))); |
| const bool start_result = StartEmbeddedTestServer(); |
| g_browser_process->webrtc_log_uploader()->SetUploadUrlForTesting( |
| embedded_test_server()->GetURL(kTestUploadUrlPath)); |
| return start_result; |
| } |
| |
| std::unique_ptr<net::test_server::HttpResponse> HandleServerRequest( |
| const net::test_server::HttpRequest& request) { |
| if (request.relative_url == kTestUploadUrlPath) { |
| upload_request_content_ = request.content; |
| std::unique_ptr<net::test_server::BasicHttpResponse> response( |
| new net::test_server::BasicHttpResponse); |
| response->set_code(net::HTTP_OK); |
| response->set_content(kTestReportId); |
| return std::move(response); |
| } |
| |
| return nullptr; |
| } |
| |
| void AppendTabIdAndUrl(base::ListValue* parameters) { |
| std::unique_ptr<base::DictionaryValue> request_info( |
| new base::DictionaryValue()); |
| request_info->SetInteger( |
| "tabId", extensions::ExtensionTabUtil::GetTabId(web_contents())); |
| parameters->Append(std::move(request_info)); |
| parameters->AppendString(web_contents()->GetURL().GetOrigin().spec()); |
| } |
| |
| // This function implicitly expects the function to succeed (test failure |
| // initiated otherwise). |
| // Returns the value (NOT whether it had succeeded or failed). |
| // TODO(crbug.com/829419): Return success/failure of the executed function. |
| template <typename Function> |
| std::unique_ptr<base::Value> RunFunction(const base::ListValue& parameters) { |
| scoped_refptr<Function> function(CreateFunction<Function>()); |
| std::unique_ptr<base::Value> result(utils::RunFunctionAndReturnSingleResult( |
| function.get(), ParamsToString(parameters), GetBrowser())); |
| return result; |
| } |
| |
| // This function implicitly expects the function to succeed (test failure |
| // initiated otherwise). |
| // Returns the value (NOT whether it had succeeded or failed). |
| // TODO(crbug.com/829419): Return success/failure of the executed function. |
| template <typename Function> |
| std::unique_ptr<base::Value> RunNoArgsFunction() { |
| base::ListValue params; |
| AppendTabIdAndUrl(¶ms); |
| scoped_refptr<Function> function(CreateFunction<Function>()); |
| std::unique_ptr<base::Value> result(utils::RunFunctionAndReturnSingleResult( |
| function.get(), ParamsToString(params), GetBrowser())); |
| return result; |
| } |
| |
| template <typename Function> |
| void RunFunctionAndExpectError(const base::ListValue& parameters, |
| const std::string& expected_error) { |
| DCHECK(!expected_error.empty()); |
| scoped_refptr<Function> function(CreateFunction<Function>()); |
| const std::string error_message = utils::RunFunctionAndReturnError( |
| function.get(), ParamsToString(parameters), GetBrowser()); |
| EXPECT_EQ(error_message, expected_error); |
| } |
| |
| // This function implicitly expects the function to succeed (test failure |
| // initiated otherwise). |
| // Returns whether the function that was run returned a value, or avoided |
| // returning a value, according to expectation. |
| // TODO(crbug.com/829419): Return success/failure of the executed function. |
| bool StartLogging() { |
| constexpr bool value_expected = false; |
| std::unique_ptr<base::Value> value = |
| RunNoArgsFunction<WebrtcLoggingPrivateStartFunction>(); |
| return value_expected == (value != nullptr); |
| } |
| |
| // This function implicitly expects the function to succeed (test failure |
| // initiated otherwise). |
| // Returns whether the function that was run returned a value, or avoided |
| // returning a value, according to expectation. |
| // TODO(crbug.com/829419): Return success/failure of the executed function. |
| bool StopLogging() { |
| constexpr bool value_expected = false; |
| std::unique_ptr<base::Value> value = |
| RunNoArgsFunction<WebrtcLoggingPrivateStopFunction>(); |
| return value_expected == (value != nullptr); |
| } |
| |
| // This function implicitly expects the function to succeed (test failure |
| // initiated otherwise). |
| // Returns whether the function that was run returned a value, or avoided |
| // returning a value, according to expectation. |
| // TODO(crbug.com/829419): Return success/failure of the executed function. |
| bool DiscardLog() { |
| constexpr bool value_expected = false; |
| std::unique_ptr<base::Value> value = |
| RunNoArgsFunction<WebrtcLoggingPrivateDiscardFunction>(); |
| return value_expected == (value != nullptr); |
| } |
| |
| // This function implicitly expects the function to succeed (test failure |
| // initiated otherwise). |
| // Returns whether the function that was run returned a value, or avoided |
| // returning a value, according to expectation. |
| // TODO(crbug.com/829419): Return success/failure of the executed function. |
| bool UploadLog(std::string* report_id) { |
| constexpr bool value_expected = true; |
| std::unique_ptr<base::Value> value = |
| RunNoArgsFunction<WebrtcLoggingPrivateUploadFunction>(); |
| const bool value_returned = value != nullptr; |
| if (value_returned) |
| *report_id = *value->FindStringKey("reportId"); |
| return value_expected == value_returned; |
| } |
| |
| // This function implicitly expects the function to succeed (test failure |
| // initiated otherwise). |
| // Returns whether the function that was run returned a value, or avoided |
| // returning a value, according to expectation. |
| // TODO(crbug.com/829419): Return success/failure of the executed function. |
| bool SetMetaData(const base::ListValue& data) { |
| constexpr bool value_expected = false; |
| std::unique_ptr<base::Value> value = |
| RunFunction<WebrtcLoggingPrivateSetMetaDataFunction>(data); |
| return value_expected == (value != nullptr); |
| } |
| |
| // This function implicitly expects the function to succeed (test failure |
| // initiated otherwise). |
| // Returns whether the function that was run returned a value, or avoided |
| // returning a value, according to expectation. |
| // TODO(crbug.com/829419): Return success/failure of the executed function. |
| bool StartRtpDump(bool incoming, bool outgoing) { |
| base::ListValue params; |
| AppendTabIdAndUrl(¶ms); |
| params.AppendBoolean(incoming); |
| params.AppendBoolean(outgoing); |
| constexpr bool value_expected = false; |
| std::unique_ptr<base::Value> value = |
| RunFunction<WebrtcLoggingPrivateStartRtpDumpFunction>(params); |
| return value_expected == (value != nullptr); |
| } |
| |
| // This function implicitly expects the function to succeed (test failure |
| // initiated otherwise). |
| // Returns whether the function that was run returned a value, or avoided |
| // returning a value, according to expectation. |
| // TODO(crbug.com/829419): Return success/failure of the executed function. |
| bool StopRtpDump(bool incoming, bool outgoing) { |
| base::ListValue params; |
| AppendTabIdAndUrl(¶ms); |
| params.AppendBoolean(incoming); |
| params.AppendBoolean(outgoing); |
| constexpr bool value_expected = false; |
| std::unique_ptr<base::Value> value = |
| RunFunction<WebrtcLoggingPrivateStopRtpDumpFunction>(params); |
| return value_expected == (value != nullptr); |
| } |
| |
| // This function implicitly expects the function to succeed (test failure |
| // initiated otherwise). |
| // Returns whether the function that was run returned a value, or avoided |
| // returning a value, according to expectation. |
| // TODO(crbug.com/829419): Return success/failure of the executed function. |
| bool StoreLog(const std::string& log_id) { |
| base::ListValue params; |
| AppendTabIdAndUrl(¶ms); |
| params.AppendString(log_id); |
| constexpr bool value_expected = false; |
| std::unique_ptr<base::Value> value = |
| RunFunction<WebrtcLoggingPrivateStoreFunction>(params); |
| return value_expected == (value != nullptr); |
| } |
| |
| // This function implicitly expects the function to succeed (test failure |
| // initiated otherwise). |
| // Returns whether the function that was run returned a value, or avoided |
| // returning a value, according to expectation. |
| // TODO(crbug.com/829419): Return success/failure of the executed function. |
| bool UploadStoredLog(const std::string& log_id, std::string* report_id) { |
| base::ListValue params; |
| AppendTabIdAndUrl(¶ms); |
| params.AppendString(log_id); |
| constexpr bool value_expected = true; |
| std::unique_ptr<base::Value> value = |
| RunFunction<WebrtcLoggingPrivateUploadStoredFunction>(params); |
| const bool value_returned = value != nullptr; |
| if (value_returned) |
| *report_id = *value->FindStringKey("reportId"); |
| return value_expected == value_returned; |
| } |
| |
| // This function implicitly expects the function to succeed (test failure |
| // initiated otherwise). |
| // Returns whether the function that was run returned a value, or avoided |
| // returning a value, according to expectation. |
| // TODO(crbug.com/829419): Return success/failure of the executed function. |
| bool StartAudioDebugRecordings(int seconds) { |
| base::ListValue params; |
| AppendTabIdAndUrl(¶ms); |
| params.AppendInteger(seconds); |
| constexpr bool value_expected = true; |
| std::unique_ptr<base::Value> value = |
| RunFunction<WebrtcLoggingPrivateStartAudioDebugRecordingsFunction>( |
| params); |
| return value_expected == (value != nullptr); |
| } |
| |
| // This function implicitly expects the function to succeed (test failure |
| // initiated otherwise). |
| // Returns whether the function that was run returned a value, or avoided |
| // returning a value, according to expectation. |
| // TODO(crbug.com/829419): Return success/failure of the executed function. |
| bool StopAudioDebugRecordings() { |
| base::ListValue params; |
| AppendTabIdAndUrl(¶ms); |
| constexpr bool value_expected = true; |
| std::unique_ptr<base::Value> value = |
| RunFunction<WebrtcLoggingPrivateStopAudioDebugRecordingsFunction>( |
| params); |
| return value_expected == (value != nullptr); |
| } |
| |
| // This function expects the function to succeed or fail according to |
| // |expect_success| (test failure initiated otherwise). It also implicitly |
| // expects that no value would be returned. |
| // TODO(crbug.com/829419): Return success/failure of the executed function. |
| void StartEventLogging(const std::string& session_id, |
| int max_log_size_bytes, |
| int output_period_ms, |
| int web_app_id, |
| bool expect_success, |
| const std::string& expected_error = std::string()) { |
| DCHECK_EQ(expect_success, expected_error.empty()); |
| |
| base::ListValue params; |
| AppendTabIdAndUrl(¶ms); |
| params.AppendString(session_id); |
| params.AppendInteger(max_log_size_bytes); |
| params.AppendInteger(output_period_ms); |
| params.AppendInteger(web_app_id); |
| |
| if (expect_success) { |
| scoped_refptr<WebrtcLoggingPrivateStartEventLoggingFunction> function( |
| CreateFunction<WebrtcLoggingPrivateStartEventLoggingFunction>()); |
| |
| std::unique_ptr<base::Value> result( |
| utils::RunFunctionAndReturnSingleResult( |
| function.get(), ParamsToString(params), GetBrowser())); |
| |
| ASSERT_TRUE(result); |
| ASSERT_TRUE(result->is_dict()); |
| ASSERT_EQ(result->DictSize(), 1u); |
| |
| const base::Value* val = |
| result->FindKeyOfType("logId", base::Value::Type::STRING); |
| ASSERT_TRUE(val); |
| |
| const std::string& log_id = val->GetString(); |
| EXPECT_EQ(log_id.size(), 32u); |
| EXPECT_EQ(log_id.find_first_not_of("0123456789ABCDEF"), |
| std::string::npos); |
| } else { |
| RunFunctionAndExpectError<WebrtcLoggingPrivateStartEventLoggingFunction>( |
| params, expected_error); |
| } |
| } |
| |
| void SetUpPeerConnection(const std::string& session_id = "") { |
| auto* manager = WebRtcEventLogManager::GetInstance(); |
| auto* rph = web_contents()->GetRenderViewHost()->GetProcess(); |
| |
| const int render_process_id = rph->GetID(); |
| const int lid = 0; |
| |
| manager->PeerConnectionAdded(render_process_id, lid, base::NullCallback()); |
| |
| if (!session_id.empty()) { |
| manager->PeerConnectionSessionIdSet(render_process_id, lid, session_id, |
| base::NullCallback()); |
| } |
| } |
| |
| base::test::ScopedFeatureList scoped_feature_list_; |
| base::test::ScopedCommandLine scoped_command_line_; |
| scoped_refptr<const Extension> extension_; |
| |
| // The content of the upload request that reached the test server. |
| std::string upload_request_content_; |
| }; |
| |
| } // namespace |
| |
| IN_PROC_BROWSER_TEST_F(WebrtcLoggingPrivateApiTest, TestStartStopDiscard) { |
| EXPECT_TRUE(StartLogging()); |
| EXPECT_TRUE(StopLogging()); |
| EXPECT_TRUE(DiscardLog()); |
| } |
| |
| // Tests WebRTC diagnostic logging. Sets up the browser to save the multipart |
| // contents to a buffer instead of uploading it, then verifies it after a calls. |
| // Example of multipart contents: |
| // ------**--yradnuoBgoLtrapitluMklaTelgooG--**---- |
| // Content-Disposition: form-data; name="prod" |
| // |
| // Chrome_Linux |
| // ------**--yradnuoBgoLtrapitluMklaTelgooG--**---- |
| // Content-Disposition: form-data; name="ver" |
| // |
| // 30.0.1554.0 |
| // ------**--yradnuoBgoLtrapitluMklaTelgooG--**---- |
| // Content-Disposition: form-data; name="guid" |
| // |
| // 0 |
| // ------**--yradnuoBgoLtrapitluMklaTelgooG--**---- |
| // Content-Disposition: form-data; name="type" |
| // |
| // webrtc_log |
| // ------**--yradnuoBgoLtrapitluMklaTelgooG--**---- |
| // Content-Disposition: form-data; name="app_session_id" |
| // |
| // 0123456789abcdef |
| // ------**--yradnuoBgoLtrapitluMklaTelgooG--**---- |
| // Content-Disposition: form-data; name="url" |
| // |
| // http://127.0.0.1:43213/webrtc/webrtc_jsep01_test.html |
| // ------**--yradnuoBgoLtrapitluMklaTelgooG--**---- |
| // Content-Disposition: form-data; name="webrtc_log"; filename="webrtc_log.gz" |
| // Content-Type: application/gzip |
| // |
| // <compressed data (zip)> |
| // ------**--yradnuoBgoLtrapitluMklaTelgooG--**------ |
| // |
| IN_PROC_BROWSER_TEST_F(WebrtcLoggingPrivateApiTest, TestStartStopUpload) { |
| ASSERT_TRUE(SetupTestServerLogUploading()); |
| |
| base::ListValue parameters; |
| AppendTabIdAndUrl(¶meters); |
| InitializeTestMetaData(¶meters); |
| |
| std::string report_id; |
| |
| SetMetaData(parameters); |
| StartLogging(); |
| StopLogging(); |
| UploadLog(&report_id); |
| |
| ASSERT_FALSE(upload_request_content_.empty()); |
| EXPECT_STREQ(kTestReportId, report_id.c_str()); |
| |
| // Check multipart data. |
| |
| const char boundary[] = "------**--yradnuoBgoLtrapitluMklaTelgooG--**----"; |
| |
| // Move the compressed data to its own string, since it may contain "\r\n" and |
| // it makes the tests below easier. |
| const char zip_content_type[] = "Content-Type: application/gzip"; |
| size_t zip_pos = upload_request_content_.find(&zip_content_type[0]); |
| ASSERT_NE(std::string::npos, zip_pos); |
| // Move pos to where the zip begins. - 1 to remove '\0', + 4 for two "\r\n". |
| zip_pos += sizeof(zip_content_type) + 3; |
| size_t zip_length = upload_request_content_.find(boundary, zip_pos); |
| ASSERT_NE(std::string::npos, zip_length); |
| // Calculate length, adjust for a "\r\n". |
| zip_length -= zip_pos + 2; |
| ASSERT_GT(zip_length, 0u); |
| std::string log_part = upload_request_content_.substr(zip_pos, zip_length); |
| upload_request_content_.erase(zip_pos, zip_length); |
| |
| // Uncompress log and verify contents. |
| EXPECT_TRUE(GzipUncompress(log_part, &log_part)); |
| EXPECT_GT(log_part.length(), 0u); |
| // Verify that meta data exists. |
| EXPECT_NE(std::string::npos, log_part.find(base::StringPrintf("%s: %s", |
| kTestLoggingSessionIdKey, kTestLoggingSessionIdValue))); |
| // Verify that the basic info generated at logging startup exists. |
| EXPECT_NE(std::string::npos, log_part.find("Chrome version:")); |
| EXPECT_NE(std::string::npos, log_part.find("Cpu brand:")); |
| |
| // Check the multipart contents. |
| std::vector<std::string> multipart_lines = |
| base::SplitStringUsingSubstr(upload_request_content_, "\r\n", |
| base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); |
| ASSERT_EQ(31, static_cast<int>(multipart_lines.size())); |
| |
| EXPECT_STREQ(&boundary[0], multipart_lines[0].c_str()); |
| EXPECT_STREQ("Content-Disposition: form-data; name=\"prod\"", |
| multipart_lines[1].c_str()); |
| EXPECT_TRUE(multipart_lines[2].empty()); |
| EXPECT_NE(std::string::npos, multipart_lines[3].find("Chrome")); |
| |
| EXPECT_STREQ(&boundary[0], multipart_lines[4].c_str()); |
| EXPECT_STREQ("Content-Disposition: form-data; name=\"ver\"", |
| multipart_lines[5].c_str()); |
| EXPECT_TRUE(multipart_lines[6].empty()); |
| // Just check that the version contains a dot. |
| EXPECT_NE(std::string::npos, multipart_lines[7].find('.')); |
| |
| EXPECT_STREQ(&boundary[0], multipart_lines[8].c_str()); |
| EXPECT_STREQ("Content-Disposition: form-data; name=\"guid\"", |
| multipart_lines[9].c_str()); |
| EXPECT_TRUE(multipart_lines[10].empty()); |
| EXPECT_STREQ("0", multipart_lines[11].c_str()); |
| |
| EXPECT_STREQ(&boundary[0], multipart_lines[12].c_str()); |
| EXPECT_STREQ("Content-Disposition: form-data; name=\"type\"", |
| multipart_lines[13].c_str()); |
| EXPECT_TRUE(multipart_lines[14].empty()); |
| EXPECT_STREQ("webrtc_log", multipart_lines[15].c_str()); |
| |
| EXPECT_STREQ(&boundary[0], multipart_lines[16].c_str()); |
| EXPECT_STREQ("Content-Disposition: form-data; name=\"app_session_id\"", |
| multipart_lines[17].c_str()); |
| EXPECT_TRUE(multipart_lines[18].empty()); |
| EXPECT_STREQ(kTestLoggingSessionIdValue, multipart_lines[19].c_str()); |
| |
| EXPECT_STREQ(&boundary[0], multipart_lines[20].c_str()); |
| EXPECT_STREQ("Content-Disposition: form-data; name=\"url\"", |
| multipart_lines[21].c_str()); |
| EXPECT_TRUE(multipart_lines[22].empty()); |
| EXPECT_STREQ(kTestLoggingUrl, multipart_lines[23].c_str()); |
| |
| EXPECT_STREQ(&boundary[0], multipart_lines[24].c_str()); |
| EXPECT_STREQ("Content-Disposition: form-data; name=\"webrtc_log\";" |
| " filename=\"webrtc_log.gz\"", |
| multipart_lines[25].c_str()); |
| EXPECT_STREQ("Content-Type: application/gzip", |
| multipart_lines[26].c_str()); |
| EXPECT_TRUE(multipart_lines[27].empty()); |
| EXPECT_TRUE(multipart_lines[28].empty()); // The removed zip part. |
| std::string final_delimiter = boundary; |
| final_delimiter += "--"; |
| EXPECT_STREQ(final_delimiter.c_str(), multipart_lines[29].c_str()); |
| EXPECT_TRUE(multipart_lines[30].empty()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(WebrtcLoggingPrivateApiTest, TestStartStopRtpDump) { |
| // TODO(tommi): As is, these tests are missing verification of the actual |
| // RTP dump data. We should fix that, e.g. use SetDumpWriterForTesting. |
| StartRtpDump(true, true); |
| StopRtpDump(true, true); |
| } |
| |
| // Tests trying to store a log when a log is not being captured. |
| // We should get a failure callback in this case. |
| IN_PROC_BROWSER_TEST_F(WebrtcLoggingPrivateApiTest, TestStoreWithoutLog) { |
| base::ListValue parameters; |
| AppendTabIdAndUrl(¶meters); |
| parameters.AppendString("MyLogId"); |
| scoped_refptr<WebrtcLoggingPrivateStoreFunction> store( |
| CreateFunction<WebrtcLoggingPrivateStoreFunction>()); |
| const std::string error = utils::RunFunctionAndReturnError( |
| store.get(), ParamsToString(parameters), GetBrowser()); |
| ASSERT_FALSE(error.empty()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(WebrtcLoggingPrivateApiTest, TestStartStopStore) { |
| ASSERT_TRUE(StartLogging()); |
| ASSERT_TRUE(StopLogging()); |
| EXPECT_TRUE(StoreLog("MyLogID")); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(WebrtcLoggingPrivateApiTest, |
| TestStartStopStoreAndUpload) { |
| ASSERT_TRUE(SetupTestServerLogUploading()); |
| |
| static const char kLogId[] = "TestStartStopStoreAndUpload"; |
| ASSERT_TRUE(StartLogging()); |
| ASSERT_TRUE(StopLogging()); |
| ASSERT_TRUE(StoreLog(kLogId)); |
| |
| std::string report_id; |
| EXPECT_TRUE(UploadStoredLog(kLogId, &report_id)); |
| EXPECT_NE(std::string::npos, |
| upload_request_content_.find("filename=\"webrtc_log.gz\"")); |
| EXPECT_STREQ(kTestReportId, report_id.c_str()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(WebrtcLoggingPrivateApiTest, |
| TestStartStopStoreAndUploadWithRtp) { |
| ASSERT_TRUE(SetupTestServerLogUploading()); |
| |
| static const char kLogId[] = "TestStartStopStoreAndUploadWithRtp"; |
| ASSERT_TRUE(StartLogging()); |
| ASSERT_TRUE(StartRtpDump(true, true)); |
| ASSERT_TRUE(StopLogging()); |
| ASSERT_TRUE(StopRtpDump(true, true)); |
| ASSERT_TRUE(StoreLog(kLogId)); |
| |
| std::string report_id; |
| EXPECT_TRUE(UploadStoredLog(kLogId, &report_id)); |
| EXPECT_NE(std::string::npos, |
| upload_request_content_.find("filename=\"webrtc_log.gz\"")); |
| EXPECT_STREQ(kTestReportId, report_id.c_str()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(WebrtcLoggingPrivateApiTest, |
| TestStartStopStoreAndUploadWithMetaData) { |
| ASSERT_TRUE(SetupTestServerLogUploading()); |
| |
| static const char kLogId[] = "TestStartStopStoreAndUploadWithRtp"; |
| ASSERT_TRUE(StartLogging()); |
| |
| base::ListValue parameters; |
| AppendTabIdAndUrl(¶meters); |
| InitializeTestMetaData(¶meters); |
| SetMetaData(parameters); |
| |
| ASSERT_TRUE(StopLogging()); |
| ASSERT_TRUE(StoreLog(kLogId)); |
| |
| std::string report_id; |
| EXPECT_TRUE(UploadStoredLog(kLogId, &report_id)); |
| EXPECT_NE(std::string::npos, |
| upload_request_content_.find("filename=\"webrtc_log.gz\"")); |
| EXPECT_NE(std::string::npos, upload_request_content_.find(kTestLoggingUrl)); |
| EXPECT_STREQ(kTestReportId, report_id.c_str()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(WebrtcLoggingPrivateApiTest, |
| TestStartStopAudioDebugRecordings) { |
| // TODO(guidou): These tests are missing verification of the actual AEC dump |
| // data. This will be fixed with a separate browser test. |
| // See crbug.com/569957. |
| base::CommandLine::ForCurrentProcess()->AppendSwitch( |
| switches::kEnableAudioDebugRecordingsFromExtension); |
| ASSERT_TRUE(StartAudioDebugRecordings(0)); |
| ASSERT_TRUE(StopAudioDebugRecordings()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(WebrtcLoggingPrivateApiTest, |
| TestStartTimedAudioDebugRecordings) { |
| base::CommandLine::ForCurrentProcess()->AppendSwitch( |
| switches::kEnableAudioDebugRecordingsFromExtension); |
| ASSERT_TRUE(StartAudioDebugRecordings(1)); |
| } |
| |
| #if !defined(OS_ANDROID) |
| |
| // Fixture for various tests over StartEventLogging. Intended to be sub-classed |
| // to test different scenarios. |
| class WebrtcLoggingPrivateApiStartEventLoggingTestBase |
| : public WebrtcLoggingPrivateApiTest { |
| public: |
| ~WebrtcLoggingPrivateApiStartEventLoggingTestBase() override = default; |
| |
| protected: |
| void SetUp() override { |
| SetUpFeatures(); |
| WebrtcLoggingPrivateApiTest::SetUp(); |
| } |
| |
| void SetUpFeatures() { |
| std::vector<base::Feature> enabled; |
| std::vector<base::Feature> disabled; |
| |
| if (WebRtcEventLogCollectionFeature()) { |
| enabled.push_back(features::kWebRtcRemoteEventLog); |
| } else { |
| disabled.push_back(features::kWebRtcRemoteEventLog); |
| } |
| |
| enabled.push_back(features::kWebRtcRemoteEventLogGzipped); |
| |
| scoped_feature_list_.InitWithFeatures(enabled, disabled); |
| } |
| |
| void SetUpInProcessBrowserTestFixture() override { |
| EXPECT_CALL(provider_, IsInitializationComplete(testing::_)) |
| .WillRepeatedly(testing::Return(true)); |
| |
| policy::BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_); |
| policy::PolicyMap values; |
| |
| values.Set(policy::key::kWebRtcEventLogCollectionAllowed, |
| policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER, |
| policy::POLICY_SOURCE_ENTERPRISE_DEFAULT, |
| std::make_unique<base::Value>(WebRtcEventLogCollectionPolicy()), |
| nullptr); |
| |
| provider_.UpdateChromePolicy(values); |
| } |
| |
| // Whether the test should have WebRTC remote-bound event logging generally |
| // enabled (default behavior), or disabled (Finch kill-switch engaged). |
| virtual bool WebRtcEventLogCollectionFeature() const = 0; |
| |
| // Whether the test should simulate running on a user profile which |
| // has the kWebRtcEventLogCollectionAllowed policy configured or not. |
| virtual bool WebRtcEventLogCollectionPolicy() const = 0; |
| |
| private: |
| policy::MockConfigurationPolicyProvider provider_; |
| }; |
| |
| // Test StartEventLogging's behavior when the feature is active (kill-switch |
| // from Finch *not* engaged, working in a profile where the policy is |
| // configured). |
| class WebrtcLoggingPrivateApiStartEventLoggingTestFeatureAndPolicyEnabled |
| : public WebrtcLoggingPrivateApiStartEventLoggingTestBase, |
| public testing::WithParamInterface<bool> { |
| public: |
| ~WebrtcLoggingPrivateApiStartEventLoggingTestFeatureAndPolicyEnabled() |
| override = default; |
| |
| bool WebRtcEventLogCollectionFeature() const override { return true; } |
| |
| bool WebRtcEventLogCollectionPolicy() const override { return true; } |
| }; |
| |
| // Also covers StartEventLoggingForLegalWebAppIdSucceeds scenario. |
| IN_PROC_BROWSER_TEST_P( |
| WebrtcLoggingPrivateApiStartEventLoggingTestFeatureAndPolicyEnabled, |
| StartEventLoggingForKnownPeerConnectionSucceeds) { |
| const std::string session_id = "id"; |
| SetUpPeerConnection(session_id); |
| const int max_size_bytes = kMaxRemoteLogFileSizeBytes; |
| constexpr bool expect_success = true; |
| const int output_period_ms = GetParam() ? kMaxOutputPeriodMs : 0; |
| StartEventLogging(session_id, max_size_bytes, output_period_ms, kWebAppId, |
| expect_success); |
| } |
| |
| IN_PROC_BROWSER_TEST_F( |
| WebrtcLoggingPrivateApiStartEventLoggingTestFeatureAndPolicyEnabled, |
| StartEventLoggingWithUnlimitedSizeFails) { |
| const std::string session_id = "id"; |
| SetUpPeerConnection(session_id); |
| const int max_size_bytes = kWebRtcEventLogManagerUnlimitedFileSize; |
| constexpr bool expect_success = false; |
| const std::string error_message = |
| kStartRemoteLoggingFailureUnlimitedSizeDisallowed; |
| StartEventLogging(session_id, max_size_bytes, 0, kWebAppId, expect_success, |
| error_message); |
| } |
| |
| IN_PROC_BROWSER_TEST_F( |
| WebrtcLoggingPrivateApiStartEventLoggingTestFeatureAndPolicyEnabled, |
| StartEventLoggingWithTooSmallMaxSize) { |
| const std::string session_id = "id"; |
| SetUpPeerConnection(session_id); |
| const int max_size_bytes = 1; |
| constexpr bool expect_success = false; |
| const std::string error_message = kStartRemoteLoggingFailureMaxSizeTooSmall; |
| StartEventLogging(session_id, max_size_bytes, 0, kWebAppId, expect_success, |
| error_message); |
| } |
| |
| IN_PROC_BROWSER_TEST_F( |
| WebrtcLoggingPrivateApiStartEventLoggingTestFeatureAndPolicyEnabled, |
| StartEventLoggingWithExcessiveMaxSizeFails) { |
| const std::string session_id = "id"; |
| SetUpPeerConnection(session_id); |
| const int max_size_bytes = kMaxRemoteLogFileSizeBytes + 1; |
| constexpr bool expect_success = false; |
| const std::string error_message = kStartRemoteLoggingFailureMaxSizeTooLarge; |
| StartEventLogging(session_id, max_size_bytes, 0, kWebAppId, expect_success, |
| error_message); |
| } |
| |
| IN_PROC_BROWSER_TEST_F( |
| WebrtcLoggingPrivateApiStartEventLoggingTestFeatureAndPolicyEnabled, |
| StartEventLoggingWithTooLargeOutputPeriodMsFails) { |
| const std::string session_id = "id"; |
| SetUpPeerConnection(session_id); |
| const int output_period_ms = kMaxOutputPeriodMs + 1; |
| constexpr bool expect_success = false; |
| const std::string error_message = |
| kStartRemoteLoggingFailureOutputPeriodMsTooLarge; |
| StartEventLogging(session_id, kMaxRemoteLogFileSizeBytes, output_period_ms, |
| kWebAppId, expect_success, error_message); |
| } |
| |
| IN_PROC_BROWSER_TEST_F( |
| WebrtcLoggingPrivateApiStartEventLoggingTestFeatureAndPolicyEnabled, |
| StartEventLoggingForNeverAddedPeerConnectionFails) { |
| // Note that manager->PeerConnectionAdded() is not called. |
| const std::string session_id = "id"; |
| const int max_size_bytes = kMaxRemoteLogFileSizeBytes; |
| constexpr bool expect_success = false; |
| const std::string error_message = |
| kStartRemoteLoggingFailureUnknownOrInactivePeerConnection; |
| StartEventLogging(session_id, max_size_bytes, 0, kWebAppId, expect_success, |
| error_message); |
| } |
| |
| IN_PROC_BROWSER_TEST_F( |
| WebrtcLoggingPrivateApiStartEventLoggingTestFeatureAndPolicyEnabled, |
| StartEventLoggingForWrongSessionIdFails) { |
| const std::string session_id_1 = "id1"; |
| const std::string session_id_2 = "id2"; |
| |
| SetUpPeerConnection(session_id_1); |
| const int max_size_bytes = kMaxRemoteLogFileSizeBytes; |
| constexpr bool expect_success = false; |
| const std::string error_message = |
| kStartRemoteLoggingFailureUnknownOrInactivePeerConnection; |
| StartEventLogging(session_id_2, max_size_bytes, 0, kWebAppId, expect_success, |
| error_message); |
| } |
| |
| IN_PROC_BROWSER_TEST_F( |
| WebrtcLoggingPrivateApiStartEventLoggingTestFeatureAndPolicyEnabled, |
| StartEventLoggingIfSessionIdNeverSetFails) { |
| SetUpPeerConnection(); // Note lack of session ID. |
| const int max_size_bytes = kMaxRemoteLogFileSizeBytes; |
| constexpr bool expect_success = false; |
| const std::string error_message = |
| kStartRemoteLoggingFailureUnknownOrInactivePeerConnection; |
| StartEventLogging("session_id", max_size_bytes, 0, kWebAppId, expect_success, |
| error_message); |
| } |
| |
| IN_PROC_BROWSER_TEST_F( |
| WebrtcLoggingPrivateApiStartEventLoggingTestFeatureAndPolicyEnabled, |
| StartEventLoggingIfSessionIdNeverSetFailsForEmptySessionId) { |
| SetUpPeerConnection(); // Note lack of session ID. |
| const int max_size_bytes = kMaxRemoteLogFileSizeBytes; |
| constexpr bool expect_success = false; |
| const std::string error_message = |
| kStartRemoteLoggingFailureUnknownOrInactivePeerConnection; |
| StartEventLogging("", max_size_bytes, 0, kWebAppId, expect_success, |
| error_message); |
| } |
| |
| IN_PROC_BROWSER_TEST_F( |
| WebrtcLoggingPrivateApiStartEventLoggingTestFeatureAndPolicyEnabled, |
| StartEventLogginWithEmptySessionIdFails) { |
| SetUpPeerConnection("session_id"); |
| const int max_size_bytes = kMaxRemoteLogFileSizeBytes; |
| constexpr bool expect_success = false; |
| const std::string error_message = |
| kStartRemoteLoggingFailureUnknownOrInactivePeerConnection; |
| StartEventLogging("", max_size_bytes, 0, kWebAppId, expect_success, |
| error_message); |
| } |
| |
| IN_PROC_BROWSER_TEST_F( |
| WebrtcLoggingPrivateApiStartEventLoggingTestFeatureAndPolicyEnabled, |
| StartEventLoggingForAlreadyLoggedPeerConnectionFails) { |
| const std::string session_id = "id"; |
| SetUpPeerConnection(session_id); |
| |
| const int max_size_bytes = kMaxRemoteLogFileSizeBytes; |
| |
| // First call succeeds. |
| { |
| constexpr bool expect_success = true; |
| StartEventLogging(session_id, max_size_bytes, 0, kWebAppId, expect_success); |
| } |
| |
| // Second call fails. |
| { |
| constexpr bool expect_success = false; |
| const std::string error_message = kStartRemoteLoggingFailureAlreadyLogging; |
| StartEventLogging(session_id, max_size_bytes, 0, kWebAppId, expect_success, |
| error_message); |
| } |
| } |
| |
| IN_PROC_BROWSER_TEST_F( |
| WebrtcLoggingPrivateApiStartEventLoggingTestFeatureAndPolicyEnabled, |
| StartEventLoggingForTooLowWebAppIdFails) { |
| const std::string session_id = "id"; |
| SetUpPeerConnection(session_id); |
| const int max_size_bytes = kMaxRemoteLogFileSizeBytes; |
| const size_t web_app_id = |
| webrtc_event_logging::kMinWebRtcEventLogWebAppId - 1; |
| ASSERT_LT(web_app_id, webrtc_event_logging::kMinWebRtcEventLogWebAppId); |
| constexpr bool expect_success = false; |
| const std::string error_message = |
| webrtc_event_logging::kStartRemoteLoggingFailureIllegalWebAppId; |
| StartEventLogging(session_id, max_size_bytes, 0, web_app_id, expect_success, |
| error_message); |
| } |
| |
| IN_PROC_BROWSER_TEST_F( |
| WebrtcLoggingPrivateApiStartEventLoggingTestFeatureAndPolicyEnabled, |
| StartEventLoggingForTooHighWebAppIdFails) { |
| const std::string session_id = "id"; |
| SetUpPeerConnection(session_id); |
| const int max_size_bytes = kMaxRemoteLogFileSizeBytes; |
| const size_t web_app_id = |
| webrtc_event_logging::kMaxWebRtcEventLogWebAppId + 1; |
| ASSERT_GT(web_app_id, webrtc_event_logging::kMaxWebRtcEventLogWebAppId); |
| constexpr bool expect_success = false; |
| const std::string error_message = |
| webrtc_event_logging::kStartRemoteLoggingFailureIllegalWebAppId; |
| StartEventLogging(session_id, max_size_bytes, 0, web_app_id, expect_success, |
| error_message); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| _, |
| WebrtcLoggingPrivateApiStartEventLoggingTestFeatureAndPolicyEnabled, |
| ::testing::Bool()); |
| |
| // Testing with either the feature or the policy disabled (not both). |
| class WebrtcLoggingPrivateApiStartEventLoggingTestFeatureOrPolicyDisabled |
| : public WebrtcLoggingPrivateApiStartEventLoggingTestBase, |
| public ::testing::WithParamInterface<bool> { |
| public: |
| WebrtcLoggingPrivateApiStartEventLoggingTestFeatureOrPolicyDisabled() |
| : feature_enabled_(GetParam()), policy_enabled_(!feature_enabled_) {} |
| |
| ~WebrtcLoggingPrivateApiStartEventLoggingTestFeatureOrPolicyDisabled() |
| override = default; |
| |
| protected: |
| bool WebRtcEventLogCollectionFeature() const override { |
| return feature_enabled_; |
| } |
| |
| bool WebRtcEventLogCollectionPolicy() const override { |
| return policy_enabled_; |
| } |
| |
| private: |
| const bool feature_enabled_; |
| const bool policy_enabled_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_P( |
| WebrtcLoggingPrivateApiStartEventLoggingTestFeatureOrPolicyDisabled, |
| StartEventLoggingFails) { |
| const std::string session_id = "id"; |
| SetUpPeerConnection(session_id); |
| const int max_size_bytes = kMaxRemoteLogFileSizeBytes; |
| constexpr bool expect_success = false; |
| const std::string error_message = kStartRemoteLoggingFailureFeatureDisabled; |
| StartEventLogging(session_id, max_size_bytes, 0, kWebAppId, expect_success, |
| error_message); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| FeatureEnabled, |
| WebrtcLoggingPrivateApiStartEventLoggingTestFeatureOrPolicyDisabled, |
| ::testing::Bool()); |
| |
| // Make sure that, even if both the feature and the policy enable remote-bound |
| // event logging, it will be blocked for incognito sessions. |
| class WebrtcLoggingPrivateApiStartEventLoggingTestInIncognitoMode |
| : public WebrtcLoggingPrivateApiStartEventLoggingTestBase { |
| public: |
| ~WebrtcLoggingPrivateApiStartEventLoggingTestInIncognitoMode() override = |
| default; |
| |
| protected: |
| Browser* GetBrowser() override { |
| if (!browser_) { |
| browser_ = CreateIncognitoBrowser(); |
| } |
| return browser_; |
| } |
| |
| bool WebRtcEventLogCollectionFeature() const override { return true; } |
| |
| bool WebRtcEventLogCollectionPolicy() const override { return true; } |
| |
| private: |
| Browser* browser_{nullptr}; // Does not own the object. |
| }; |
| |
| IN_PROC_BROWSER_TEST_F( |
| WebrtcLoggingPrivateApiStartEventLoggingTestInIncognitoMode, |
| StartEventLoggingFails) { |
| const std::string session_id = "id"; |
| SetUpPeerConnection(session_id); |
| const int max_size_bytes = kMaxRemoteLogFileSizeBytes; |
| constexpr bool expect_success = false; |
| const std::string error_message = kStartRemoteLoggingFailureFeatureDisabled; |
| StartEventLogging(session_id, max_size_bytes, 0, kWebAppId, expect_success, |
| error_message); |
| } |
| |
| #endif // !defined(OS_ANDROID) |