blob: 181c8e3519804dae71eee58bcb06b1296b6b74b2 [file] [log] [blame]
// Copyright 2017 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 "content/browser/webrtc/webrtc_event_log_manager.h"
#include <algorithm>
#include <list>
#include <memory>
#include <numeric>
#include <queue>
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/optional.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/synchronization/waitable_event.h"
#include "base/test/gtest_util.h"
#include "base/test/simple_test_clock.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "content/browser/webrtc/webrtc_remote_event_log_manager.h"
#include "content/public/test/mock_render_process_host.h"
#include "content/public/test/test_browser_context.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
// TODO(eladalon): Add unit tests for incognito mode. https://crbug.com/775415
#if defined(OS_WIN)
#define IntToStringType base::IntToString16
#else
#define IntToStringType base::IntToString
#endif
namespace content {
using ::testing::_;
using ::testing::Invoke;
using ::testing::NiceMock;
using ::testing::StrictMock;
using PeerConnectionKey = WebRtcEventLogPeerConnectionKey;
namespace {
const int kMaxActiveRemoteLogFiles =
static_cast<int>(kMaxActiveRemoteBoundWebRtcEventLogs);
const int kMaxPendingRemoteLogFiles =
static_cast<int>(kMaxPendingRemoteBoundWebRtcEventLogs);
// This implementation does not upload files, nor prtends to have finished an
// upload. Most importantly, it does not get rid of the locally-stored log file
// after finishing a simulated upload; this is useful because it keeps the file
// on disk, where unit tests may inspect it.
class NullWebRtcEventLogUploader : public WebRtcEventLogUploader {
public:
~NullWebRtcEventLogUploader() override = default;
class Factory : public WebRtcEventLogUploader::Factory {
public:
~Factory() override = default;
std::unique_ptr<WebRtcEventLogUploader> Create(
const base::FilePath& log_file,
WebRtcEventLogUploaderObserver* observer) override {
return std::make_unique<NullWebRtcEventLogUploader>();
}
};
};
class MockWebRtcLocalEventLogsObserver : public WebRtcLocalEventLogsObserver {
public:
~MockWebRtcLocalEventLogsObserver() override = default;
MOCK_METHOD2(OnLocalLogStarted,
void(PeerConnectionKey, const base::FilePath&));
MOCK_METHOD1(OnLocalLogStopped, void(PeerConnectionKey));
};
class MockWebRtcRemoteEventLogsObserver : public WebRtcRemoteEventLogsObserver {
public:
~MockWebRtcRemoteEventLogsObserver() override = default;
MOCK_METHOD2(OnRemoteLogStarted,
void(PeerConnectionKey, const base::FilePath&));
MOCK_METHOD1(OnRemoteLogStopped, void(PeerConnectionKey));
};
} // namespace
class WebRtcEventLogManagerTest : public ::testing::TestWithParam<bool> {
public:
WebRtcEventLogManagerTest()
: run_loop_(std::make_unique<base::RunLoop>()),
uploader_run_loop_(std::make_unique<base::RunLoop>()),
manager_(WebRtcEventLogManager::CreateSingletonInstance()) {
EXPECT_TRUE(base::CreateNewTempDirectory(FILE_PATH_LITERAL(""),
&local_logs_base_dir_));
if (local_logs_base_dir_.empty()) {
EXPECT_TRUE(false);
return;
}
local_logs_base_path_ =
local_logs_base_dir_.Append(FILE_PATH_LITERAL("local_event_logs"));
}
void SetUp() override {
SetWebRtcEventLogUploaderFactoryForTesting(
std::make_unique<NullWebRtcEventLogUploader::Factory>());
browser_context_ = CreateBrowserContext();
rph_ = std::make_unique<MockRenderProcessHost>(browser_context_.get());
SetLocalLogsObserver(&local_observer_);
SetRemoteLogsObserver(&remote_observer_);
}
~WebRtcEventLogManagerTest() override {
if (manager_) {
WaitForPendingTasks();
DestroyUnitUnderTest();
}
if (!local_logs_base_dir_.empty()) {
EXPECT_TRUE(base::DeleteFile(local_logs_base_dir_, true));
}
// Guard against unexpected state changes.
EXPECT_TRUE(webrtc_state_change_instructions_.empty());
}
void DestroyUnitUnderTest() {
manager_.reset();
}
void WaitForReply() {
run_loop_->Run();
run_loop_.reset(new base::RunLoop); // Allow re-blocking.
}
void VoidReply() { run_loop_->QuitWhenIdle(); }
base::OnceClosure VoidReplyClosure() {
return base::BindOnce(&WebRtcEventLogManagerTest::VoidReply,
base::Unretained(this));
}
void BoolReply(bool* output, bool value) {
*output = value;
run_loop_->QuitWhenIdle();
}
base::OnceCallback<void(bool)> BoolReplyClosure(bool* output) {
return base::BindOnce(&WebRtcEventLogManagerTest::BoolReply,
base::Unretained(this), output);
}
void BoolPairReply(std::pair<bool, bool>* output,
std::pair<bool, bool> value) {
*output = value;
run_loop_->QuitWhenIdle();
}
base::OnceCallback<void(std::pair<bool, bool>)> BoolPairReplyClosure(
std::pair<bool, bool>* output) {
return base::BindOnce(&WebRtcEventLogManagerTest::BoolPairReply,
base::Unretained(this), output);
}
bool PeerConnectionAdded(int render_process_id, int lid) {
bool result;
manager_->PeerConnectionAdded(render_process_id, lid,
BoolReplyClosure(&result));
WaitForReply();
return result;
}
bool PeerConnectionRemoved(int render_process_id, int lid) {
bool result;
manager_->PeerConnectionRemoved(render_process_id, lid,
BoolReplyClosure(&result));
WaitForReply();
return result;
}
bool EnableLocalLogging(
size_t max_size_bytes = kWebRtcEventLogManagerUnlimitedFileSize) {
return EnableLocalLogging(local_logs_base_path_, max_size_bytes);
}
bool EnableLocalLogging(
base::FilePath local_logs_base_path,
size_t max_size_bytes = kWebRtcEventLogManagerUnlimitedFileSize) {
bool result;
manager_->EnableLocalLogging(local_logs_base_path, max_size_bytes,
BoolReplyClosure(&result));
WaitForReply();
return result;
}
bool DisableLocalLogging() {
bool result;
manager_->DisableLocalLogging(BoolReplyClosure(&result));
WaitForReply();
return result;
}
bool StartRemoteLogging(int render_process_id,
int lid,
size_t max_size_bytes = kArbitraryVeryLargeFileSize) {
bool result;
manager_->StartRemoteLogging(render_process_id, lid, max_size_bytes,
BoolReplyClosure(&result));
WaitForReply();
return result;
}
void SetLocalLogsObserver(WebRtcLocalEventLogsObserver* observer) {
manager_->SetLocalLogsObserver(observer, VoidReplyClosure());
WaitForReply();
}
void SetRemoteLogsObserver(WebRtcRemoteEventLogsObserver* observer) {
manager_->SetRemoteLogsObserver(observer, VoidReplyClosure());
WaitForReply();
}
void SetWebRtcEventLogUploaderFactoryForTesting(
std::unique_ptr<WebRtcEventLogUploader::Factory> factory) {
manager_->SetWebRtcEventLogUploaderFactoryForTesting(std::move(factory));
}
std::pair<bool, bool> OnWebRtcEventLogWrite(int render_process_id,
int lid,
const std::string& message) {
std::pair<bool, bool> result;
manager_->OnWebRtcEventLogWrite(render_process_id, lid, message,
BoolPairReplyClosure(&result));
WaitForReply();
return result;
}
void FreezeClockAt(const base::Time::Exploded& frozen_time_exploded) {
base::Time frozen_time;
ASSERT_TRUE(
base::Time::FromLocalExploded(frozen_time_exploded, &frozen_time));
frozen_clock_.SetNow(frozen_time);
manager_->SetClockForTesting(&frozen_clock_);
}
void SetWebRtcEventLoggingState(PeerConnectionKey key,
bool event_logging_enabled) {
webrtc_state_change_instructions_.emplace(key, event_logging_enabled);
}
void ExpectWebRtcStateChangeInstruction(int render_process_id,
int lid,
bool enabled) {
ASSERT_FALSE(webrtc_state_change_instructions_.empty());
auto& instruction = webrtc_state_change_instructions_.front();
EXPECT_EQ(instruction.key, PeerConnectionKey(render_process_id, lid));
EXPECT_EQ(instruction.enabled, enabled);
webrtc_state_change_instructions_.pop();
}
void SetPeerConnectionTrackerProxyForTesting(
std::unique_ptr<WebRtcEventLogManager::PeerConnectionTrackerProxy>
pc_tracker_proxy) {
manager_->SetPeerConnectionTrackerProxyForTesting(
std::move(pc_tracker_proxy));
}
std::unique_ptr<TestBrowserContext> CreateBrowserContext(
base::FilePath local_logs_base_path = base::FilePath()) {
auto browser_context =
std::make_unique<TestBrowserContext>(local_logs_base_path);
// Blocks on the unit under test's task runner, so that we won't proceed
// with the test (e.g. check that files were created) before finished
// processing this even (which is signaled to it from
// BrowserContext::EnableForBrowserContext).
WaitForPendingTasks();
return browser_context;
}
base::FilePath GetLogsDirectoryPath(
const base::FilePath& browser_context_dir) {
return WebRtcRemoteEventLogManager::GetLogsDirectoryPath(
browser_context_dir);
}
// Initiate an arbitrary synchronous operation, allowing any tasks pending
// on the manager's internal task queue to be completed.
// If given a RunLoop, we first block on it. The reason to do both is that
// with the RunLoop we wait on some tasks which we know also post additional
// tasks, then, after that chain is completed, we also wait for any potential
// leftovers. For example, the run loop could wait for n-1 files to be
// uploaded, then it is released when the last one's upload is initiated,
// then we wait for the last file's upload to be completed.
void WaitForPendingTasks(base::RunLoop* run_loop = nullptr) {
if (run_loop) {
run_loop->Run();
}
base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
manager_->GetTaskRunnerForTesting()->PostTask(
FROM_HERE,
base::BindOnce([](base::WaitableEvent* event) { event->Signal(); },
&event));
event.Wait();
}
void SuppressUploading() {
if (!upload_suppressing_browser_context_) { // First suppression.
upload_suppressing_browser_context_ = CreateBrowserContext();
}
DCHECK(!upload_suppressing_rph_) << "Uploading already suppressed.";
upload_suppressing_rph_ = std::make_unique<MockRenderProcessHost>(
upload_suppressing_browser_context_.get());
ASSERT_TRUE(PeerConnectionAdded(upload_suppressing_rph_->GetID(), 0));
}
void UnsuppressUploading() {
DCHECK(upload_suppressing_rph_) << "Uploading not suppressed.";
ASSERT_TRUE(PeerConnectionRemoved(upload_suppressing_rph_->GetID(), 0));
upload_suppressing_rph_.reset();
}
void ExpectFileContents(const base::FilePath& file_path,
const std::string& expected_contents) {
std::string file_contents;
ASSERT_TRUE(base::ReadFileToString(file_path, &file_contents));
EXPECT_EQ(file_contents, expected_contents);
}
static const size_t kArbitraryVeryLargeFileSize = 1000 * 1000 * 1000;
// Testing utilities.
content::TestBrowserThreadBundle test_browser_thread_bundle_;
base::SimpleTestClock frozen_clock_;
// The main loop, which allows waiting for the operations invoked on the
// unit-under-test to be completed. Do not use this object directly from the
// tests, since that would be error-prone. (Specifically, one must not produce
// two events that could produce replies, without waiting on the first reply
// in between.)
std::unique_ptr<base::RunLoop> run_loop_;
// Allows waiting for upload operations.
std::unique_ptr<base::RunLoop> uploader_run_loop_;
// Unit under test.
std::unique_ptr<WebRtcEventLogManager> manager_;
// Default BrowserContext and RenderProcessHost, to be used by tests which
// do not require anything fancy (such as seeding the BrowserContext with
// pre-existing logs files from a previous session, or working with multiple
// BrowserContext objects).
std::unique_ptr<TestBrowserContext> browser_context_;
std::unique_ptr<MockRenderProcessHost> rph_;
// Used for suppressing the upload of finished files, by creating an active
// remote-bound log associated with an independent BrowserContext which
// does not otherwise interfere with the test.
std::unique_ptr<TestBrowserContext> upload_suppressing_browser_context_;
std::unique_ptr<MockRenderProcessHost> upload_suppressing_rph_;
// The directory where we'll save local log files.
base::FilePath local_logs_base_dir_;
// local_logs_base_dir_ + log files' name prefix.
base::FilePath local_logs_base_path_;
// WebRtcEventLogManager instructs WebRTC, via PeerConnectionTracker, to
// only send WebRTC messages for certain peer connections. Some tests make
// sure that this is done correctly, by waiting for these notifications, then
// testing them.
// Because a single action - disabling of local logging - could crease a
// series of such instructions, we keep a queue of them. However, were one
// to actually test that scenario, one would have to account for the lack
// of a guarantee over the order in which these instructions are produced.
struct WebRtcStateChangeInstruction {
WebRtcStateChangeInstruction(PeerConnectionKey key, bool enabled)
: key(key), enabled(enabled) {}
PeerConnectionKey key;
bool enabled;
};
std::queue<WebRtcStateChangeInstruction> webrtc_state_change_instructions_;
// Observers for local/remote logging being started/stopped. By having them
// here, we achieve two goals:
// 1. Reduce boilerplate in the tests themselves.
// 2. Avoid lifetime issues, where the observer might be deallocated before
// a RenderProcessHost is deallocated (which can potentially trigger a
// callback on the observer).
NiceMock<MockWebRtcLocalEventLogsObserver> local_observer_;
NiceMock<MockWebRtcRemoteEventLogsObserver> remote_observer_;
};
namespace {
// Common default/arbitrary values.
static constexpr int kPeerConnectionId = 478;
auto SaveFilePathTo(base::Optional<base::FilePath>* output) {
return [output](PeerConnectionKey ignored_key, base::FilePath file_path) {
*output = file_path;
};
}
auto SaveKeyAndFilePathTo(base::Optional<PeerConnectionKey>* key_output,
base::Optional<base::FilePath>* file_path_output) {
return [key_output, file_path_output](PeerConnectionKey key,
base::FilePath file_path) {
*key_output = key;
*file_path_output = file_path;
};
}
class PeerConnectionTrackerProxyForTesting
: public WebRtcEventLogManager::PeerConnectionTrackerProxy {
public:
explicit PeerConnectionTrackerProxyForTesting(WebRtcEventLogManagerTest* test)
: test_(test) {}
~PeerConnectionTrackerProxyForTesting() override = default;
void SetWebRtcEventLoggingState(WebRtcEventLogPeerConnectionKey key,
bool event_logging_enabled) override {
test_->SetWebRtcEventLoggingState(key, event_logging_enabled);
}
private:
WebRtcEventLogManagerTest* const test_;
};
#if defined(OS_POSIX) && !defined(OS_FUCHSIA)
void RemoveWritePermissionsFromDirectory(const base::FilePath& path) {
int permissions;
ASSERT_TRUE(base::GetPosixFilePermissions(path, &permissions));
constexpr int write_permissions = base::FILE_PERMISSION_WRITE_BY_USER |
base::FILE_PERMISSION_WRITE_BY_GROUP |
base::FILE_PERMISSION_WRITE_BY_OTHERS;
permissions &= ~write_permissions;
ASSERT_TRUE(base::SetPosixFilePermissions(path, permissions));
}
#endif // defined(OS_POSIX) && !defined(OS_FUCHSIA)
// Production code uses the WebRtcEventLogUploaderObserver interface to
// pass notifications of an upload's completion from the concrete uploader
// to the remote-logs-manager. In unit tests, these notifications are
// intercepted, inspected and then passed onwards to the remote-logs-manager.
// This class simplifies this by getting a hold of the message, sending it
// to the unit test's observer, then allowing it to proceed to its normal
// destination.
class InterceptingWebRtcEventLogUploaderObserver
: public WebRtcEventLogUploaderObserver {
public:
InterceptingWebRtcEventLogUploaderObserver(
WebRtcEventLogUploaderObserver* intercpeting_observer,
WebRtcEventLogUploaderObserver* normal_observer)
: intercpeting_observer_(intercpeting_observer),
normal_observer_(normal_observer) {}
~InterceptingWebRtcEventLogUploaderObserver() override = default;
void OnWebRtcEventLogUploadComplete(const base::FilePath& file_path,
bool upload_successful) override {
intercpeting_observer_->OnWebRtcEventLogUploadComplete(file_path,
upload_successful);
normal_observer_->OnWebRtcEventLogUploadComplete(file_path,
upload_successful);
}
private:
WebRtcEventLogUploaderObserver* const intercpeting_observer_;
WebRtcEventLogUploaderObserver* const normal_observer_;
};
// The factory for the following fake uploader produces a sequence of uploaders
// which fail the test if given a file other than that which they expect. The
// factory itself likewise fails the test if destroyed before producing all
// expected uploaders, or if it's asked for more uploaders than it expects to
// create. This allows us to test for sequences of uploads.
class FileListExpectingWebRtcEventLogUploader : public WebRtcEventLogUploader {
public:
// The logic is in the factory; the uploader just reports success so that the
// next file may become eligible for uploading.
explicit FileListExpectingWebRtcEventLogUploader(
const base::FilePath& log_file,
bool result,
WebRtcEventLogUploaderObserver* observer) {
observer->OnWebRtcEventLogUploadComplete(log_file, result);
}
~FileListExpectingWebRtcEventLogUploader() override = default;
class Factory : public WebRtcEventLogUploader::Factory {
public:
Factory(std::list<base::FilePath>* expected_files,
bool result,
base::RunLoop* run_loop)
: result_(result), run_loop_(run_loop) {
expected_files_.swap(*expected_files);
}
~Factory() override { EXPECT_TRUE(expected_files_.empty()); }
std::unique_ptr<WebRtcEventLogUploader> Create(
const base::FilePath& log_file,
WebRtcEventLogUploaderObserver* observer) override {
if (expected_files_.empty()) {
EXPECT_FALSE(true); // More files uploaded than expected.
} else {
EXPECT_EQ(log_file, expected_files_.front());
base::DeleteFile(log_file, false);
expected_files_.pop_front();
}
if (expected_files_.empty()) {
run_loop_->QuitWhenIdle();
}
return std::make_unique<FileListExpectingWebRtcEventLogUploader>(
log_file, result_, observer);
}
private:
std::list<base::FilePath> expected_files_;
const bool result_;
base::RunLoop* const run_loop_;
};
};
} // namespace
TEST_F(WebRtcEventLogManagerTest, LocalLogPeerConnectionAddedReturnsTrue) {
EXPECT_TRUE(PeerConnectionAdded(rph_->GetID(), kPeerConnectionId));
}
TEST_F(WebRtcEventLogManagerTest,
LocalLogPeerConnectionAddedReturnsFalseIfAlreadyAdded) {
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), kPeerConnectionId));
EXPECT_FALSE(PeerConnectionAdded(rph_->GetID(), kPeerConnectionId));
}
TEST_F(WebRtcEventLogManagerTest, LocalLogPeerConnectionRemovedReturnsTrue) {
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), kPeerConnectionId));
EXPECT_TRUE(PeerConnectionRemoved(rph_->GetID(), kPeerConnectionId));
}
TEST_F(WebRtcEventLogManagerTest,
LocalLogPeerConnectionRemovedReturnsFalseIfNeverAdded) {
EXPECT_FALSE(PeerConnectionRemoved(rph_->GetID(), kPeerConnectionId));
}
TEST_F(WebRtcEventLogManagerTest,
LocalLogPeerConnectionRemovedReturnsFalseIfAlreadyRemoved) {
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), kPeerConnectionId));
ASSERT_TRUE(PeerConnectionRemoved(rph_->GetID(), kPeerConnectionId));
EXPECT_FALSE(PeerConnectionRemoved(rph_->GetID(), kPeerConnectionId));
}
TEST_F(WebRtcEventLogManagerTest, LocalLogEnableLocalLoggingReturnsTrue) {
EXPECT_TRUE(EnableLocalLogging());
}
TEST_F(WebRtcEventLogManagerTest,
LocalLogEnableLocalLoggingReturnsFalseIfCalledWhenAlreadyEnabled) {
ASSERT_TRUE(EnableLocalLogging());
EXPECT_FALSE(EnableLocalLogging());
}
TEST_F(WebRtcEventLogManagerTest, LocalLogDisableLocalLoggingReturnsTrue) {
ASSERT_TRUE(EnableLocalLogging());
EXPECT_TRUE(DisableLocalLogging());
}
TEST_F(WebRtcEventLogManagerTest,
LocalLogDisableLocalLoggingReturnsIfNeverEnabled) {
EXPECT_FALSE(DisableLocalLogging());
}
TEST_F(WebRtcEventLogManagerTest,
LocalLogDisableLocalLoggingReturnsIfAlreadyDisabled) {
ASSERT_TRUE(EnableLocalLogging());
ASSERT_TRUE(DisableLocalLogging());
EXPECT_FALSE(DisableLocalLogging());
}
TEST_F(WebRtcEventLogManagerTest,
OnWebRtcEventLogWriteReturnsFalseAndFalseWhenAllLoggingDisabled) {
// Note that EnableLocalLogging() and StartRemoteLogging() weren't called.
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), kPeerConnectionId));
EXPECT_EQ(OnWebRtcEventLogWrite(rph_->GetID(), kPeerConnectionId, "log"),
std::make_pair(false, false));
}
TEST_F(WebRtcEventLogManagerTest,
OnWebRtcEventLogWriteReturnsFalseAndFalseForUnknownPeerConnection) {
ASSERT_TRUE(EnableLocalLogging());
// Note that PeerConnectionAdded() wasn't called.
EXPECT_EQ(OnWebRtcEventLogWrite(rph_->GetID(), kPeerConnectionId, "log"),
std::make_pair(false, false));
}
TEST_F(WebRtcEventLogManagerTest,
OnWebRtcEventLogWriteReturnsLocalTrueWhenPcKnownAndLocalLoggingOn) {
ASSERT_TRUE(EnableLocalLogging());
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), kPeerConnectionId));
EXPECT_EQ(OnWebRtcEventLogWrite(rph_->GetID(), kPeerConnectionId, "log"),
std::make_pair(true, false));
}
TEST_F(WebRtcEventLogManagerTest,
OnWebRtcEventLogWriteReturnsRemoteTrueWhenPcKnownAndRemoteLogging) {
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), kPeerConnectionId));
ASSERT_TRUE(StartRemoteLogging(rph_->GetID(), kPeerConnectionId));
EXPECT_EQ(OnWebRtcEventLogWrite(rph_->GetID(), kPeerConnectionId, "log"),
std::make_pair(false, true));
}
TEST_F(WebRtcEventLogManagerTest,
OnWebRtcEventLogWriteReturnsTrueAndTrueeWhenAllLoggingEnabled) {
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), kPeerConnectionId));
ASSERT_TRUE(EnableLocalLogging());
ASSERT_TRUE(StartRemoteLogging(rph_->GetID(), kPeerConnectionId));
EXPECT_EQ(OnWebRtcEventLogWrite(rph_->GetID(), kPeerConnectionId, "log"),
std::make_pair(true, true));
}
TEST_F(WebRtcEventLogManagerTest,
OnLocalLogStartedNotCalledIfLocalLoggingEnabledWithoutPeerConnections) {
EXPECT_CALL(local_observer_, OnLocalLogStarted(_, _)).Times(0);
ASSERT_TRUE(EnableLocalLogging());
}
TEST_F(WebRtcEventLogManagerTest,
OnLocalLogStoppedNotCalledIfLocalLoggingDisabledWithoutPeerConnections) {
EXPECT_CALL(local_observer_, OnLocalLogStopped(_)).Times(0);
ASSERT_TRUE(EnableLocalLogging());
ASSERT_TRUE(DisableLocalLogging());
}
TEST_F(WebRtcEventLogManagerTest,
OnLocalLogStartedCalledForPeerConnectionAddedAndLocalLoggingEnabled) {
PeerConnectionKey peer_connection(rph_->GetID(), kPeerConnectionId);
EXPECT_CALL(local_observer_, OnLocalLogStarted(peer_connection, _)).Times(1);
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), kPeerConnectionId));
ASSERT_TRUE(EnableLocalLogging());
}
TEST_F(WebRtcEventLogManagerTest,
OnLocalLogStartedCalledForLocalLoggingEnabledAndPeerConnectionAdded) {
PeerConnectionKey peer_connection(rph_->GetID(), kPeerConnectionId);
EXPECT_CALL(local_observer_, OnLocalLogStarted(peer_connection, _)).Times(1);
ASSERT_TRUE(EnableLocalLogging());
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), kPeerConnectionId));
}
TEST_F(WebRtcEventLogManagerTest,
OnLocalLogStoppedCalledAfterLocalLoggingDisabled) {
PeerConnectionKey peer_connection(rph_->GetID(), kPeerConnectionId);
EXPECT_CALL(local_observer_, OnLocalLogStopped(peer_connection)).Times(1);
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), kPeerConnectionId));
ASSERT_TRUE(EnableLocalLogging());
ASSERT_TRUE(DisableLocalLogging());
}
TEST_F(WebRtcEventLogManagerTest,
OnLocalLogStoppedCalledAfterPeerConnectionRemoved) {
PeerConnectionKey peer_connection(rph_->GetID(), kPeerConnectionId);
EXPECT_CALL(local_observer_, OnLocalLogStopped(peer_connection)).Times(1);
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), kPeerConnectionId));
ASSERT_TRUE(EnableLocalLogging());
ASSERT_TRUE(PeerConnectionRemoved(rph_->GetID(), kPeerConnectionId));
}
TEST_F(WebRtcEventLogManagerTest, LocalLogCreatesEmptyFileWhenStarted) {
const PeerConnectionKey key(rph_->GetID(), kPeerConnectionId);
base::Optional<base::FilePath> file_path;
ON_CALL(local_observer_, OnLocalLogStarted(key, _))
.WillByDefault(Invoke(SaveFilePathTo(&file_path)));
ASSERT_TRUE(EnableLocalLogging());
ASSERT_TRUE(PeerConnectionAdded(key.render_process_id, key.lid));
ASSERT_TRUE(file_path);
ASSERT_FALSE(file_path->empty());
// Make sure the file would be closed, so that we could safely read it.
ASSERT_TRUE(PeerConnectionRemoved(key.render_process_id, key.lid));
ExpectFileContents(*file_path, "");
}
TEST_F(WebRtcEventLogManagerTest, LocalLogCreateAndWriteToFile) {
const PeerConnectionKey key(rph_->GetID(), kPeerConnectionId);
base::Optional<base::FilePath> file_path;
ON_CALL(local_observer_, OnLocalLogStarted(key, _))
.WillByDefault(Invoke(SaveFilePathTo(&file_path)));
ASSERT_TRUE(EnableLocalLogging());
ASSERT_TRUE(PeerConnectionAdded(key.render_process_id, key.lid));
ASSERT_TRUE(file_path);
ASSERT_FALSE(file_path->empty());
const std::string log = "To strive, to seek, to find, and not to yield.";
ASSERT_EQ(OnWebRtcEventLogWrite(rph_->GetID(), kPeerConnectionId, log),
std::make_pair(true, false));
// Make sure the file would be closed, so that we could safely read it.
ASSERT_TRUE(PeerConnectionRemoved(key.render_process_id, key.lid));
ExpectFileContents(*file_path, log);
}
TEST_F(WebRtcEventLogManagerTest, LocalLogMultipleWritesToSameFile) {
const PeerConnectionKey key(rph_->GetID(), kPeerConnectionId);
base::Optional<base::FilePath> file_path;
ON_CALL(local_observer_, OnLocalLogStarted(key, _))
.WillByDefault(Invoke(SaveFilePathTo(&file_path)));
ASSERT_TRUE(EnableLocalLogging());
ASSERT_TRUE(PeerConnectionAdded(key.render_process_id, key.lid));
ASSERT_TRUE(file_path);
ASSERT_FALSE(file_path->empty());
const std::string logs[] = {"Old age hath yet his honour and his toil;",
"Death closes all: but something ere the end,",
"Some work of noble note, may yet be done,",
"Not unbecoming men that strove with Gods."};
for (const std::string& log : logs) {
ASSERT_EQ(OnWebRtcEventLogWrite(key.render_process_id, key.lid, log),
std::make_pair(true, false));
}
// Make sure the file would be closed, so that we could safely read it.
ASSERT_TRUE(PeerConnectionRemoved(key.render_process_id, key.lid));
ExpectFileContents(
*file_path,
std::accumulate(std::begin(logs), std::end(logs), std::string()));
}
TEST_F(WebRtcEventLogManagerTest, LocalLogFileSizeLimitNotExceeded) {
const PeerConnectionKey key(rph_->GetID(), kPeerConnectionId);
base::Optional<base::FilePath> file_path;
ON_CALL(local_observer_, OnLocalLogStarted(key, _))
.WillByDefault(Invoke(SaveFilePathTo(&file_path)));
const std::string log = "There lies the port; the vessel puffs her sail:";
const size_t file_size_limit_bytes = log.length() / 2;
ASSERT_TRUE(EnableLocalLogging(file_size_limit_bytes));
ASSERT_TRUE(PeerConnectionAdded(key.render_process_id, key.lid));
ASSERT_TRUE(file_path);
ASSERT_FALSE(file_path->empty());
// A failure is reported, because not everything could be written. The file
// will also be closed.
const auto pc = PeerConnectionKey(key.render_process_id, key.lid);
EXPECT_CALL(local_observer_, OnLocalLogStopped(pc)).Times(1);
ASSERT_EQ(OnWebRtcEventLogWrite(key.render_process_id, key.lid, log),
std::make_pair(false, false));
// Additional calls to Write() have no effect.
ASSERT_EQ(OnWebRtcEventLogWrite(key.render_process_id, key.lid, "ignored"),
std::make_pair(false, false));
ExpectFileContents(*file_path, log.substr(0, file_size_limit_bytes));
}
TEST_F(WebRtcEventLogManagerTest, LocalLogSanityOverUnlimitedFileSizes) {
const PeerConnectionKey key(rph_->GetID(), kPeerConnectionId);
base::Optional<base::FilePath> file_path;
ON_CALL(local_observer_, OnLocalLogStarted(key, _))
.WillByDefault(Invoke(SaveFilePathTo(&file_path)));
ASSERT_TRUE(EnableLocalLogging(kWebRtcEventLogManagerUnlimitedFileSize));
ASSERT_TRUE(PeerConnectionAdded(key.render_process_id, key.lid));
ASSERT_TRUE(file_path);
ASSERT_FALSE(file_path->empty());
const std::string log1 = "Who let the dogs out?";
const std::string log2 = "Woof, woof, woof, woof, woof!";
ASSERT_EQ(OnWebRtcEventLogWrite(key.render_process_id, key.lid, log1),
std::make_pair(true, false));
ASSERT_EQ(OnWebRtcEventLogWrite(key.render_process_id, key.lid, log2),
std::make_pair(true, false));
// Make sure the file would be closed, so that we could safely read it.
ASSERT_TRUE(PeerConnectionRemoved(key.render_process_id, key.lid));
ExpectFileContents(*file_path, log1 + log2);
}
TEST_F(WebRtcEventLogManagerTest, LocalLogNoWriteAfterLogStopped) {
const PeerConnectionKey key(rph_->GetID(), kPeerConnectionId);
base::Optional<base::FilePath> file_path;
ON_CALL(local_observer_, OnLocalLogStarted(key, _))
.WillByDefault(Invoke(SaveFilePathTo(&file_path)));
ASSERT_TRUE(EnableLocalLogging());
ASSERT_TRUE(PeerConnectionAdded(key.render_process_id, key.lid));
ASSERT_TRUE(file_path);
ASSERT_FALSE(file_path->empty());
const std::string log_before = "log_before_stop";
ASSERT_EQ(OnWebRtcEventLogWrite(key.render_process_id, key.lid, log_before),
std::make_pair(true, false));
EXPECT_CALL(local_observer_, OnLocalLogStopped(key)).Times(1);
ASSERT_TRUE(PeerConnectionRemoved(key.render_process_id, key.lid));
const std::string log_after = "log_after_stop";
ASSERT_EQ(OnWebRtcEventLogWrite(key.render_process_id, key.lid, log_after),
std::make_pair(false, false));
ExpectFileContents(*file_path, log_before);
}
TEST_F(WebRtcEventLogManagerTest, LocalLogOnlyWritesTheLogsAfterStarted) {
const PeerConnectionKey key(rph_->GetID(), kPeerConnectionId);
// Calls to Write() before the log was started are ignored.
EXPECT_CALL(local_observer_, OnLocalLogStarted(_, _)).Times(0);
const std::string log1 = "The lights begin to twinkle from the rocks:";
ASSERT_EQ(OnWebRtcEventLogWrite(key.render_process_id, key.lid, log1),
std::make_pair(false, false));
ASSERT_TRUE(base::IsDirectoryEmpty(local_logs_base_dir_));
base::Optional<base::FilePath> file_path;
EXPECT_CALL(local_observer_, OnLocalLogStarted(key, _))
.Times(1)
.WillOnce(Invoke(SaveFilePathTo(&file_path)));
ASSERT_TRUE(EnableLocalLogging());
ASSERT_TRUE(PeerConnectionAdded(key.render_process_id, key.lid));
ASSERT_TRUE(file_path);
ASSERT_FALSE(file_path->empty());
// Calls after the log started have an effect. The calls to Write() from
// before the log started are not remembered.
const std::string log2 = "The long day wanes: the slow moon climbs: the deep";
ASSERT_EQ(OnWebRtcEventLogWrite(key.render_process_id, key.lid, log2),
std::make_pair(true, false));
// Make sure the file would be closed, so that we could safely read it.
ASSERT_TRUE(PeerConnectionRemoved(rph_->GetID(), kPeerConnectionId));
ExpectFileContents(*file_path, log2);
}
// Note: This test also covers the scenario LocalLogExistingFilesNotOverwritten,
// which is therefore not explicitly tested.
TEST_F(WebRtcEventLogManagerTest, LocalLoggingRestartCreatesNewFile) {
const std::vector<std::string> logs = {"<setup>", "<punchline>", "<encore>"};
std::vector<base::Optional<PeerConnectionKey>> keys(logs.size());
std::vector<base::Optional<base::FilePath>> file_paths(logs.size());
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), kPeerConnectionId));
for (size_t i = 0; i < logs.size(); i++) {
ON_CALL(local_observer_, OnLocalLogStarted(_, _))
.WillByDefault(Invoke(SaveKeyAndFilePathTo(&keys[i], &file_paths[i])));
ASSERT_TRUE(EnableLocalLogging());
ASSERT_TRUE(keys[i]);
ASSERT_EQ(*keys[i], PeerConnectionKey(rph_->GetID(), kPeerConnectionId));
ASSERT_TRUE(file_paths[i]);
ASSERT_FALSE(file_paths[i]->empty());
ASSERT_EQ(OnWebRtcEventLogWrite(rph_->GetID(), kPeerConnectionId, logs[i]),
std::make_pair(true, false));
ASSERT_TRUE(DisableLocalLogging());
}
for (size_t i = 0; i < logs.size(); i++) {
ExpectFileContents(*file_paths[i], logs[i]);
}
}
TEST_F(WebRtcEventLogManagerTest, LocalLogMultipleActiveFiles) {
ASSERT_TRUE(EnableLocalLogging());
std::list<MockRenderProcessHost> rphs;
for (size_t i = 0; i < 3; i++) {
rphs.emplace_back(browser_context_.get());
};
std::vector<PeerConnectionKey> keys;
for (auto& rph : rphs) {
keys.emplace_back(rph.GetID(), kPeerConnectionId);
}
std::vector<base::Optional<base::FilePath>> file_paths(keys.size());
for (size_t i = 0; i < keys.size(); i++) {
ON_CALL(local_observer_, OnLocalLogStarted(keys[i], _))
.WillByDefault(Invoke(SaveFilePathTo(&file_paths[i])));
ASSERT_TRUE(PeerConnectionAdded(keys[i].render_process_id, keys[i].lid));
ASSERT_TRUE(file_paths[i]);
ASSERT_FALSE(file_paths[i]->empty());
}
std::vector<std::string> logs;
for (size_t i = 0; i < keys.size(); i++) {
logs.emplace_back(std::to_string(rph_->GetID()) +
std::to_string(kPeerConnectionId));
ASSERT_EQ(
OnWebRtcEventLogWrite(keys[i].render_process_id, keys[i].lid, logs[i]),
std::make_pair(true, false));
}
// Make sure the file woulds be closed, so that we could safely read them.
ASSERT_TRUE(DisableLocalLogging());
for (size_t i = 0; i < keys.size(); i++) {
ExpectFileContents(*file_paths[i], logs[i]);
}
}
TEST_F(WebRtcEventLogManagerTest, LocalLogLimitActiveLocalLogFiles) {
ASSERT_TRUE(EnableLocalLogging());
const int kMaxLocalLogFiles =
static_cast<int>(kMaxNumberLocalWebRtcEventLogFiles);
for (int i = 0; i < kMaxLocalLogFiles; i++) {
const PeerConnectionKey key(rph_->GetID(), i);
EXPECT_CALL(local_observer_, OnLocalLogStarted(key, _)).Times(1);
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), i));
}
EXPECT_CALL(local_observer_, OnLocalLogStarted(_, _)).Times(0);
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), kMaxLocalLogFiles));
}
// When a log reaches its maximum size limit, it is closed, and no longer
// counted towards the limit.
TEST_F(WebRtcEventLogManagerTest, LocalLogFilledLogNotCountedTowardsLogsLimit) {
const std::string log = "very_short_log";
ASSERT_TRUE(EnableLocalLogging(log.size()));
const int kMaxLocalLogFiles =
static_cast<int>(kMaxNumberLocalWebRtcEventLogFiles);
for (int i = 0; i < kMaxLocalLogFiles; i++) {
const PeerConnectionKey key(rph_->GetID(), i);
EXPECT_CALL(local_observer_, OnLocalLogStarted(key, _)).Times(1);
ASSERT_TRUE(PeerConnectionAdded(key.render_process_id, key.lid));
}
// By writing to one of the logs, we fill it and end up closing it, allowing
// an additional log to be written.
EXPECT_EQ(OnWebRtcEventLogWrite(rph_->GetID(), 0, log),
std::make_pair(true, false));
// We now have room for one additional log.
const PeerConnectionKey last_key(rph_->GetID(), kMaxLocalLogFiles);
EXPECT_CALL(local_observer_, OnLocalLogStarted(last_key, _)).Times(1);
ASSERT_TRUE(PeerConnectionAdded(last_key.render_process_id, last_key.lid));
}
TEST_F(WebRtcEventLogManagerTest,
LocalLogForRemovedPeerConnectionNotCountedTowardsLogsLimit) {
ASSERT_TRUE(EnableLocalLogging());
const int kMaxLocalLogFiles =
static_cast<int>(kMaxNumberLocalWebRtcEventLogFiles);
for (int i = 0; i < kMaxLocalLogFiles; i++) {
const PeerConnectionKey key(rph_->GetID(), i);
EXPECT_CALL(local_observer_, OnLocalLogStarted(key, _)).Times(1);
ASSERT_TRUE(PeerConnectionAdded(key.render_process_id, key.lid));
}
// When one peer connection is removed, one log is stopped, thereby allowing
// an additional log to be opened.
EXPECT_CALL(local_observer_,
OnLocalLogStopped(PeerConnectionKey(rph_->GetID(), 0)))
.Times(1);
ASSERT_TRUE(PeerConnectionRemoved(rph_->GetID(), 0));
// We now have room for one additional log.
const PeerConnectionKey last_key(rph_->GetID(), kMaxLocalLogFiles);
EXPECT_CALL(local_observer_, OnLocalLogStarted(last_key, _)).Times(1);
ASSERT_TRUE(PeerConnectionAdded(last_key.render_process_id, last_key.lid));
}
TEST_F(WebRtcEventLogManagerTest, LocalLogSanityDestructorWithActiveFile) {
// We don't actually test that the file was closed, because this behavior
// is not supported - WebRtcEventLogManager's destructor may never be called,
// except for in unit tests.
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), kPeerConnectionId));
ASSERT_TRUE(EnableLocalLogging());
DestroyUnitUnderTest(); // Explicitly show destruction does not crash.
}
TEST_F(WebRtcEventLogManagerTest, LocalLogIllegalPath) {
// Since the log file won't be properly opened, these will not be called.
EXPECT_CALL(local_observer_, OnLocalLogStarted(_, _)).Times(0);
EXPECT_CALL(local_observer_, OnLocalLogStopped(_)).Times(0);
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), kPeerConnectionId));
// See the documentation of the function for why |true| is expected despite
// the path being illegal.
const base::FilePath illegal_path(FILE_PATH_LITERAL(":!@#$%|`^&*\\/"));
EXPECT_TRUE(EnableLocalLogging(illegal_path));
EXPECT_TRUE(base::IsDirectoryEmpty(local_logs_base_dir_));
}
#if defined(OS_POSIX) && !defined(OS_FUCHSIA)
TEST_F(WebRtcEventLogManagerTest, LocalLogLegalPathWithoutPermissionsSanity) {
RemoveWritePermissionsFromDirectory(local_logs_base_dir_);
// Since the log file won't be properly opened, these will not be called.
EXPECT_CALL(local_observer_, OnLocalLogStarted(_, _)).Times(0);
EXPECT_CALL(local_observer_, OnLocalLogStopped(_)).Times(0);
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), kPeerConnectionId));
// See the documentation of the function for why |true| is expected despite
// the path being illegal.
EXPECT_TRUE(EnableLocalLogging(local_logs_base_path_));
EXPECT_TRUE(base::IsDirectoryEmpty(local_logs_base_dir_));
// Write() has no effect (but is handled gracefully).
EXPECT_EQ(OnWebRtcEventLogWrite(rph_->GetID(), kPeerConnectionId,
"Why did the chicken cross the road?"),
std::make_pair(false, false));
EXPECT_TRUE(base::IsDirectoryEmpty(local_logs_base_dir_));
// Logging was enabled, even if it had no effect because of the lacking
// permissions; therefore, the operation of disabling it makes sense.
EXPECT_TRUE(DisableLocalLogging());
EXPECT_TRUE(base::IsDirectoryEmpty(local_logs_base_dir_));
}
#endif // defined(OS_POSIX) && !defined(OS_FUCHSIA)
TEST_F(WebRtcEventLogManagerTest, LocalLogEmptyStringHandledGracefully) {
const PeerConnectionKey key(rph_->GetID(), kPeerConnectionId);
// By writing a log after the empty string, we show that no odd behavior is
// encountered, such as closing the file (an actual bug from WebRTC).
const std::vector<std::string> logs = {"<setup>", "", "<encore>"};
base::Optional<base::FilePath> file_path;
ON_CALL(local_observer_, OnLocalLogStarted(key, _))
.WillByDefault(Invoke(SaveFilePathTo(&file_path)));
ASSERT_TRUE(EnableLocalLogging());
ASSERT_TRUE(PeerConnectionAdded(key.render_process_id, key.lid));
ASSERT_TRUE(file_path);
ASSERT_FALSE(file_path->empty());
for (size_t i = 0; i < logs.size(); i++) {
ASSERT_EQ(OnWebRtcEventLogWrite(key.render_process_id, key.lid, logs[i]),
std::make_pair(true, false));
}
ASSERT_TRUE(DisableLocalLogging());
ExpectFileContents(
*file_path,
std::accumulate(std::begin(logs), std::end(logs), std::string()));
}
TEST_F(WebRtcEventLogManagerTest, LocalLogFilenameMatchesExpectedFormat) {
using StringType = base::FilePath::StringType;
const PeerConnectionKey key(rph_->GetID(), kPeerConnectionId);
base::Optional<base::FilePath> file_path;
ON_CALL(local_observer_, OnLocalLogStarted(key, _))
.WillByDefault(Invoke(SaveFilePathTo(&file_path)));
const base::Time::Exploded frozen_time_exploded{
2017, // Four digit year "2007"
9, // 1-based month (values 1 = January, etc.)
3, // 0-based day of week (0 = Sunday, etc.)
6, // 1-based day of month (1-31)
10, // Hour within the current day (0-23)
43, // Minute within the current hour (0-59)
29, // Second within the current minute.
0 // Milliseconds within the current second (0-999)
};
ASSERT_TRUE(frozen_time_exploded.HasValidValues());
FreezeClockAt(frozen_time_exploded);
const StringType user_defined = FILE_PATH_LITERAL("user_defined");
const base::FilePath local_logs_base_path =
local_logs_base_dir_.Append(user_defined);
ASSERT_TRUE(EnableLocalLogging(local_logs_base_path));
ASSERT_TRUE(PeerConnectionAdded(key.render_process_id, key.lid));
ASSERT_TRUE(file_path);
ASSERT_FALSE(file_path->empty());
// [user_defined]_[date]_[time]_[pid]_[lid].log
const StringType date = FILE_PATH_LITERAL("20170906");
const StringType time = FILE_PATH_LITERAL("1043");
base::FilePath expected_path = local_logs_base_path;
expected_path = local_logs_base_path.InsertBeforeExtension(
FILE_PATH_LITERAL("_") + date + FILE_PATH_LITERAL("_") + time +
FILE_PATH_LITERAL("_") + IntToStringType(rph_->GetID()) +
FILE_PATH_LITERAL("_") + IntToStringType(kPeerConnectionId));
expected_path = expected_path.AddExtension(FILE_PATH_LITERAL("log"));
EXPECT_EQ(file_path, expected_path);
}
TEST_F(WebRtcEventLogManagerTest,
LocalLogFilenameMatchesExpectedFormatRepeatedFilename) {
using StringType = base::FilePath::StringType;
const PeerConnectionKey key(rph_->GetID(), kPeerConnectionId);
base::Optional<base::FilePath> file_path_1;
base::Optional<base::FilePath> file_path_2;
EXPECT_CALL(local_observer_, OnLocalLogStarted(key, _))
.WillOnce(Invoke(SaveFilePathTo(&file_path_1)))
.WillOnce(Invoke(SaveFilePathTo(&file_path_2)));
const base::Time::Exploded frozen_time_exploded{
2017, // Four digit year "2007"
9, // 1-based month (values 1 = January, etc.)
3, // 0-based day of week (0 = Sunday, etc.)
6, // 1-based day of month (1-31)
10, // Hour within the current day (0-23)
43, // Minute within the current hour (0-59)
29, // Second within the current minute.
0 // Milliseconds within the current second (0-999)
};
ASSERT_TRUE(frozen_time_exploded.HasValidValues());
FreezeClockAt(frozen_time_exploded);
const StringType user_defined_portion = FILE_PATH_LITERAL("user_defined");
const base::FilePath local_logs_base_path =
local_logs_base_dir_.Append(user_defined_portion);
ASSERT_TRUE(EnableLocalLogging(local_logs_base_path));
ASSERT_TRUE(PeerConnectionAdded(key.render_process_id, key.lid));
ASSERT_TRUE(file_path_1);
ASSERT_FALSE(file_path_1->empty());
// [user_defined]_[date]_[time]_[pid]_[lid].log
const StringType date = FILE_PATH_LITERAL("20170906");
const StringType time = FILE_PATH_LITERAL("1043");
base::FilePath expected_path_1 = local_logs_base_path;
expected_path_1 = local_logs_base_path.InsertBeforeExtension(
FILE_PATH_LITERAL("_") + date + FILE_PATH_LITERAL("_") + time +
FILE_PATH_LITERAL("_") + IntToStringType(rph_->GetID()) +
FILE_PATH_LITERAL("_") + IntToStringType(kPeerConnectionId));
expected_path_1 = expected_path_1.AddExtension(FILE_PATH_LITERAL("log"));
ASSERT_EQ(file_path_1, expected_path_1);
ASSERT_TRUE(DisableLocalLogging());
ASSERT_TRUE(EnableLocalLogging(local_logs_base_path));
ASSERT_TRUE(file_path_2);
ASSERT_FALSE(file_path_2->empty());
const base::FilePath expected_path_2 =
expected_path_1.InsertBeforeExtension(FILE_PATH_LITERAL(" (1)"));
// Focus of the test - starting the same log again produces a new file,
// with an expected new filename.
ASSERT_EQ(file_path_2, expected_path_2);
}
TEST_F(WebRtcEventLogManagerTest,
OnRemoteLogStartedNotCalledIfRemoteLoggingNotEnabled) {
EXPECT_CALL(remote_observer_, OnRemoteLogStarted(_, _)).Times(0);
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), kPeerConnectionId));
}
TEST_F(WebRtcEventLogManagerTest,
OnRemoteLogStoppedNotCalledIfRemoteLoggingNotEnabled) {
EXPECT_CALL(remote_observer_, OnRemoteLogStopped(_)).Times(0);
const PeerConnectionKey key(rph_->GetID(), kPeerConnectionId);
ASSERT_TRUE(PeerConnectionAdded(key.render_process_id, key.lid));
ASSERT_TRUE(PeerConnectionRemoved(key.render_process_id, key.lid));
}
TEST_F(WebRtcEventLogManagerTest,
OnRemoteLogStartedCalledIfRemoteLoggingEnabled) {
const PeerConnectionKey key(rph_->GetID(), kPeerConnectionId);
EXPECT_CALL(remote_observer_, OnRemoteLogStarted(key, _)).Times(1);
ASSERT_TRUE(PeerConnectionAdded(key.render_process_id, key.lid));
ASSERT_TRUE(StartRemoteLogging(key.render_process_id, key.lid));
}
TEST_F(WebRtcEventLogManagerTest,
OnRemoteLogStoppedCalledIfRemoteLoggingEnabledThenPcRemoved) {
const PeerConnectionKey key(rph_->GetID(), kPeerConnectionId);
EXPECT_CALL(remote_observer_, OnRemoteLogStopped(key)).Times(1);
ASSERT_TRUE(PeerConnectionAdded(key.render_process_id, key.lid));
ASSERT_TRUE(StartRemoteLogging(key.render_process_id, key.lid));
ASSERT_TRUE(PeerConnectionRemoved(key.render_process_id, key.lid));
}
TEST_F(WebRtcEventLogManagerTest,
BrowserContextInitializationCreatesDirectoryForRemoteLogs) {
auto browser_context = CreateBrowserContext();
const base::FilePath remote_logs_path =
GetLogsDirectoryPath(browser_context->GetPath());
EXPECT_TRUE(base::DirectoryExists(remote_logs_path));
EXPECT_TRUE(base::IsDirectoryEmpty(remote_logs_path));
}
TEST_F(WebRtcEventLogManagerTest,
StartRemoteLoggingReturnsFalseIfUnknownPeerConnection) {
EXPECT_FALSE(StartRemoteLogging(rph_->GetID(), kPeerConnectionId));
}
TEST_F(WebRtcEventLogManagerTest,
StartRemoteLoggingReturnsTrueIfKnownPeerConnection) {
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), kPeerConnectionId));
EXPECT_TRUE(StartRemoteLogging(rph_->GetID(), kPeerConnectionId));
}
TEST_F(WebRtcEventLogManagerTest,
StartRemoteLoggingReturnsFalseIfRestartAttempt) {
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), kPeerConnectionId));
ASSERT_TRUE(StartRemoteLogging(rph_->GetID(), kPeerConnectionId));
EXPECT_FALSE(StartRemoteLogging(rph_->GetID(), kPeerConnectionId));
}
TEST_F(WebRtcEventLogManagerTest,
StartRemoteLoggingReturnsFalseIfUnlimitedFileSize) {
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), kPeerConnectionId));
EXPECT_FALSE(StartRemoteLogging(rph_->GetID(), kPeerConnectionId,
kWebRtcEventLogManagerUnlimitedFileSize));
}
TEST_F(WebRtcEventLogManagerTest,
StartRemoteLoggingReturnsFalseIfPeerConnectionAlreadyClosed) {
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), kPeerConnectionId));
ASSERT_TRUE(PeerConnectionRemoved(rph_->GetID(), kPeerConnectionId));
EXPECT_FALSE(StartRemoteLogging(rph_->GetID(), kPeerConnectionId));
}
TEST_F(WebRtcEventLogManagerTest, StartRemoteLoggingCreatesEmptyFile) {
base::Optional<base::FilePath> file_path;
const PeerConnectionKey key(rph_->GetID(), kPeerConnectionId);
EXPECT_CALL(remote_observer_, OnRemoteLogStarted(key, _))
.Times(1)
.WillOnce(Invoke(SaveFilePathTo(&file_path)));
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), kPeerConnectionId));
ASSERT_TRUE(StartRemoteLogging(rph_->GetID(), kPeerConnectionId));
ExpectFileContents(*file_path, "");
}
TEST_F(WebRtcEventLogManagerTest, RemoteLogFileCreatedInCorrectDirectory) {
// Set up separate browser contexts; each one will get one log.
constexpr size_t kLogsNum = 3;
std::unique_ptr<TestBrowserContext> browser_contexts[kLogsNum] = {
CreateBrowserContext(), CreateBrowserContext(), CreateBrowserContext()};
std::vector<std::unique_ptr<MockRenderProcessHost>> rphs;
for (auto& browser_context : browser_contexts) {
rphs.emplace_back(
std::make_unique<MockRenderProcessHost>(browser_context.get()));
}
// Prepare to store the logs' paths in distinct memory locations.
base::Optional<base::FilePath> file_paths[kLogsNum];
for (size_t i = 0; i < kLogsNum; i++) {
const PeerConnectionKey key(rphs[i]->GetID(), kPeerConnectionId);
EXPECT_CALL(remote_observer_, OnRemoteLogStarted(key, _))
.Times(1)
.WillOnce(Invoke(SaveFilePathTo(&file_paths[i])));
}
// Start one log for each browser context.
for (const auto& rph : rphs) {
ASSERT_TRUE(PeerConnectionAdded(rph->GetID(), kPeerConnectionId));
ASSERT_TRUE(StartRemoteLogging(rph->GetID(), kPeerConnectionId));
}
// All log files must be created in their own context's directory.
for (size_t i = 0; i < arraysize(browser_contexts); i++) {
ASSERT_TRUE(file_paths[i]);
EXPECT_TRUE(browser_contexts[i]->GetPath().IsParent(*file_paths[i]));
}
}
TEST_F(WebRtcEventLogManagerTest,
OnWebRtcEventLogWriteWritesToTheRemoteBoundFile) {
base::Optional<base::FilePath> file_path;
const PeerConnectionKey key(rph_->GetID(), kPeerConnectionId);
EXPECT_CALL(remote_observer_, OnRemoteLogStarted(key, _))
.Times(1)
.WillOnce(Invoke(SaveFilePathTo(&file_path)));
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), kPeerConnectionId));
ASSERT_TRUE(StartRemoteLogging(rph_->GetID(), kPeerConnectionId));
const char* const log = "1 + 1 = 3";
EXPECT_EQ(OnWebRtcEventLogWrite(rph_->GetID(), kPeerConnectionId, log),
std::make_pair(false, true));
ExpectFileContents(*file_path, log);
}
TEST_F(WebRtcEventLogManagerTest, WriteToBothLocalAndRemoteFiles) {
const PeerConnectionKey key(rph_->GetID(), kPeerConnectionId);
ASSERT_TRUE(PeerConnectionAdded(key.render_process_id, key.lid));
base::Optional<base::FilePath> local_path;
EXPECT_CALL(local_observer_, OnLocalLogStarted(key, _))
.Times(1)
.WillOnce(Invoke(SaveFilePathTo(&local_path)));
base::Optional<base::FilePath> remote_path;
EXPECT_CALL(remote_observer_, OnRemoteLogStarted(key, _))
.Times(1)
.WillOnce(Invoke(SaveFilePathTo(&remote_path)));
ASSERT_TRUE(EnableLocalLogging());
ASSERT_TRUE(StartRemoteLogging(key.render_process_id, key.lid));
ASSERT_TRUE(local_path);
ASSERT_FALSE(local_path->empty());
ASSERT_TRUE(remote_path);
ASSERT_FALSE(remote_path->empty());
const char* const log = "logloglog";
ASSERT_EQ(OnWebRtcEventLogWrite(key.render_process_id, key.lid, log),
std::make_pair(true, true));
// Ensure the flushing of the file to disk before attempting to read them.
ASSERT_TRUE(PeerConnectionRemoved(key.render_process_id, key.lid));
for (base::Optional<base::FilePath> file_path : {local_path, remote_path}) {
ExpectFileContents(*file_path, log);
}
}
TEST_F(WebRtcEventLogManagerTest, MultipleWritesToSameRemoteBoundLogfile) {
const PeerConnectionKey key(rph_->GetID(), kPeerConnectionId);
base::Optional<base::FilePath> file_path;
ON_CALL(remote_observer_, OnRemoteLogStarted(key, _))
.WillByDefault(Invoke(SaveFilePathTo(&file_path)));
ASSERT_TRUE(PeerConnectionAdded(key.render_process_id, key.lid));
ASSERT_TRUE(StartRemoteLogging(key.render_process_id, key.lid));
ASSERT_TRUE(file_path);
ASSERT_FALSE(file_path->empty());
const std::string logs[] = {"ABC", "DEF", "XYZ"};
for (const std::string& log : logs) {
ASSERT_EQ(OnWebRtcEventLogWrite(key.render_process_id, key.lid, log),
std::make_pair(false, true));
}
// Make sure the file would be closed, so that we could safely read it.
ASSERT_TRUE(PeerConnectionRemoved(key.render_process_id, key.lid));
ExpectFileContents(
*file_path,
std::accumulate(std::begin(logs), std::end(logs), std::string()));
}
TEST_F(WebRtcEventLogManagerTest, RemoteLogFileSizeLimitNotExceeded) {
const PeerConnectionKey key(rph_->GetID(), kPeerConnectionId);
base::Optional<base::FilePath> file_path;
ON_CALL(remote_observer_, OnRemoteLogStarted(key, _))
.WillByDefault(Invoke(SaveFilePathTo(&file_path)));
const std::string log = "tpyo";
const size_t file_size_limit_bytes = log.length() / 2;
ASSERT_TRUE(PeerConnectionAdded(key.render_process_id, key.lid));
ASSERT_TRUE(StartRemoteLogging(key.render_process_id, key.lid,
file_size_limit_bytes));
// A failure is reported, because not everything could be written. The file
// will also be closed.
EXPECT_CALL(remote_observer_, OnRemoteLogStopped(key)).Times(1);
ASSERT_EQ(OnWebRtcEventLogWrite(key.render_process_id, key.lid, log),
std::make_pair(false, false));
// Additional calls to Write() have no effect.
ASSERT_EQ(OnWebRtcEventLogWrite(key.render_process_id, key.lid, "ignored"),
std::make_pair(false, false));
ExpectFileContents(*file_path, log.substr(0, file_size_limit_bytes));
}
TEST_F(WebRtcEventLogManagerTest,
LogMultipleActiveRemoteLogsSameBrowserContext) {
const std::vector<PeerConnectionKey> keys = {
PeerConnectionKey(rph_->GetID(), 0), PeerConnectionKey(rph_->GetID(), 1),
PeerConnectionKey(rph_->GetID(), 2)};
std::vector<base::Optional<base::FilePath>> file_paths(keys.size());
for (size_t i = 0; i < keys.size(); i++) {
ON_CALL(remote_observer_, OnRemoteLogStarted(keys[i], _))
.WillByDefault(Invoke(SaveFilePathTo(&file_paths[i])));
ASSERT_TRUE(PeerConnectionAdded(keys[i].render_process_id, keys[i].lid));
ASSERT_TRUE(StartRemoteLogging(keys[i].render_process_id, keys[i].lid));
ASSERT_TRUE(file_paths[i]);
ASSERT_FALSE(file_paths[i]->empty());
}
std::vector<std::string> logs;
for (size_t i = 0; i < keys.size(); i++) {
logs.emplace_back(std::to_string(rph_->GetID()) + std::to_string(i));
ASSERT_EQ(
OnWebRtcEventLogWrite(keys[i].render_process_id, keys[i].lid, logs[i]),
std::make_pair(false, true));
}
// Make sure the file woulds be closed, so that we could safely read them.
for (auto& key : keys) {
ASSERT_TRUE(PeerConnectionRemoved(key.render_process_id, key.lid));
}
for (size_t i = 0; i < keys.size(); i++) {
ExpectFileContents(*file_paths[i], logs[i]);
}
}
TEST_F(WebRtcEventLogManagerTest,
LogMultipleActiveRemoteLogsDifferentBrowserContexts) {
constexpr size_t kLogsNum = 3;
std::unique_ptr<TestBrowserContext> browser_contexts[kLogsNum] = {
CreateBrowserContext(), CreateBrowserContext(), CreateBrowserContext()};
std::vector<std::unique_ptr<MockRenderProcessHost>> rphs;
for (auto& browser_context : browser_contexts) {
rphs.emplace_back(
std::make_unique<MockRenderProcessHost>(browser_context.get()));
}
std::vector<PeerConnectionKey> keys;
for (auto& rph : rphs) {
keys.emplace_back(rph->GetID(), kPeerConnectionId);
}
std::vector<base::Optional<base::FilePath>> file_paths(keys.size());
for (size_t i = 0; i < keys.size(); i++) {
ON_CALL(remote_observer_, OnRemoteLogStarted(keys[i], _))
.WillByDefault(Invoke(SaveFilePathTo(&file_paths[i])));
ASSERT_TRUE(PeerConnectionAdded(keys[i].render_process_id, keys[i].lid));
ASSERT_TRUE(StartRemoteLogging(keys[i].render_process_id, keys[i].lid));
ASSERT_TRUE(file_paths[i]);
ASSERT_FALSE(file_paths[i]->empty());
}
std::vector<std::string> logs;
for (size_t i = 0; i < keys.size(); i++) {
logs.emplace_back(std::to_string(rph_->GetID()) + std::to_string(i));
ASSERT_EQ(
OnWebRtcEventLogWrite(keys[i].render_process_id, keys[i].lid, logs[i]),
std::make_pair(false, true));
}
// Make sure the file woulds be closed, so that we could safely read them.
for (auto& key : keys) {
ASSERT_TRUE(PeerConnectionRemoved(key.render_process_id, key.lid));
}
for (size_t i = 0; i < keys.size(); i++) {
ExpectFileContents(*file_paths[i], logs[i]);
}
}
TEST_F(WebRtcEventLogManagerTest, DifferentRemoteLogsMayHaveDifferentMaximums) {
const std::vector<PeerConnectionKey> keys = {
PeerConnectionKey(rph_->GetID(), 0), PeerConnectionKey(rph_->GetID(), 1)};
std::vector<base::Optional<base::FilePath>> file_paths(keys.size());
for (size_t i = 0; i < keys.size(); i++) {
ON_CALL(remote_observer_, OnRemoteLogStarted(keys[i], _))
.WillByDefault(Invoke(SaveFilePathTo(&file_paths[i])));
}
const size_t file_size_limits_bytes[2] = {3, 5};
CHECK_EQ(arraysize(file_size_limits_bytes), keys.size());
const std::string log = "lorem ipsum";
for (size_t i = 0; i < keys.size(); i++) {
ASSERT_TRUE(PeerConnectionAdded(keys[i].render_process_id, keys[i].lid));
ASSERT_TRUE(StartRemoteLogging(keys[i].render_process_id, keys[i].lid,
file_size_limits_bytes[i]));
ASSERT_LT(file_size_limits_bytes[i], log.length());
// A failure is reported, because not everything could be written. The file
// will also be closed.
ASSERT_EQ(
OnWebRtcEventLogWrite(keys[i].render_process_id, keys[i].lid, log),
std::make_pair(false, false));
}
for (size_t i = 0; i < keys.size(); i++) {
ExpectFileContents(*file_paths[i],
log.substr(0, file_size_limits_bytes[i]));
}
}
TEST_F(WebRtcEventLogManagerTest, RemoteLogFileClosedWhenCapacityReached) {
const PeerConnectionKey key(rph_->GetID(), kPeerConnectionId);
base::Optional<base::FilePath> file_path;
ON_CALL(remote_observer_, OnRemoteLogStarted(key, _))
.WillByDefault(Invoke(SaveFilePathTo(&file_path)));
const std::string log = "Let X equal X.";
ASSERT_TRUE(PeerConnectionAdded(key.render_process_id, key.lid));
ASSERT_TRUE(StartRemoteLogging(key.render_process_id, key.lid, log.length()));
ASSERT_TRUE(file_path);
EXPECT_CALL(remote_observer_, OnRemoteLogStopped(key)).Times(1);
EXPECT_EQ(OnWebRtcEventLogWrite(key.render_process_id, key.lid, log),
std::make_pair(false, true));
}
#if defined(OS_POSIX) && !defined(OS_FUCHSIA)
// TODO(eladalon): Add unit tests for lacking read permissions when looking
// to upload the file. https://crbug.com/775415
TEST_F(WebRtcEventLogManagerTest,
FailureToCreateRemoteLogsDirHandledGracefully) {
base::ScopedTempDir browser_context_dir;
ASSERT_TRUE(browser_context_dir.CreateUniqueTempDir());
RemoveWritePermissionsFromDirectory(browser_context_dir.GetPath());
// Graceful handling by BrowserContext::EnableForBrowserContext.
auto browser_context = CreateBrowserContext(browser_context_dir.Take());
const base::FilePath remote_logs_path =
GetLogsDirectoryPath(browser_context->GetPath());
EXPECT_FALSE(base::DirectoryExists(remote_logs_path));
auto rph = std::make_unique<MockRenderProcessHost>(browser_context.get());
// Graceful handling of PeerConnectionAdded: True returned because the
// remote-logs' manager can still safely reason about the state of peer
// connections even if one of its browser contexts is defective.)
EXPECT_TRUE(PeerConnectionAdded(rph->GetID(), kPeerConnectionId));
// Graceful handling of StartRemoteLogging: False returned because it's
// impossible to write the log to a file.
EXPECT_FALSE(StartRemoteLogging(rph->GetID(), kPeerConnectionId));
// Graceful handling of OnWebRtcEventLogWrite: False returned because the log
// could not be written at all, let alone in its entirety.
const char* const log = "This is not a log.";
EXPECT_EQ(OnWebRtcEventLogWrite(rph->GetID(), kPeerConnectionId, log),
std::make_pair(false, false));
// Graceful handling of PeerConnectionRemoved: True returned because the
// remote-logs' manager can still safely reason about the state of peer
// connections even if one of its browser contexts is defective.
EXPECT_TRUE(PeerConnectionRemoved(rph->GetID(), kPeerConnectionId));
}
TEST_F(WebRtcEventLogManagerTest, GracefullyHandleFailureToStartRemoteLogFile) {
// WebRTC logging will not be turned on.
EXPECT_CALL(remote_observer_, OnRemoteLogStarted(_, _)).Times(0);
EXPECT_CALL(remote_observer_, OnRemoteLogStopped(_)).Times(0);
// Set up a BrowserContext whose directory we know, so that we would be
// able to manipulate it.
base::ScopedTempDir browser_context_dir;
ASSERT_TRUE(browser_context_dir.CreateUniqueTempDir());
auto browser_context = CreateBrowserContext(browser_context_dir.Take());
auto rph = std::make_unique<MockRenderProcessHost>(browser_context.get());
// Remove write permissions from the directory.
const base::FilePath remote_logs_path =
GetLogsDirectoryPath(browser_context->GetPath());
ASSERT_TRUE(base::DirectoryExists(remote_logs_path));
RemoveWritePermissionsFromDirectory(remote_logs_path);
// StartRemoteLogging() will now fail.
const PeerConnectionKey key(rph->GetID(), kPeerConnectionId);
ASSERT_TRUE(PeerConnectionAdded(key.render_process_id, key.lid));
EXPECT_FALSE(StartRemoteLogging(key.render_process_id, key.lid));
EXPECT_EQ(OnWebRtcEventLogWrite(key.render_process_id, key.lid, "abc"),
std::make_pair(false, false));
EXPECT_TRUE(base::IsDirectoryEmpty(remote_logs_path));
}
#endif // defined(OS_POSIX) && !defined(OS_FUCHSIA)
TEST_F(WebRtcEventLogManagerTest, RemoteLogLimitActiveLogFiles) {
for (int i = 0; i < kMaxActiveRemoteLogFiles + 1; i++) {
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), i));
}
int i;
for (i = 0; i < kMaxActiveRemoteLogFiles; i++) {
EXPECT_CALL(remote_observer_,
OnRemoteLogStarted(PeerConnectionKey(rph_->GetID(), i), _))
.Times(1);
ASSERT_TRUE(StartRemoteLogging(rph_->GetID(), i));
}
EXPECT_CALL(remote_observer_, OnRemoteLogStarted(_, _)).Times(0);
EXPECT_FALSE(StartRemoteLogging(rph_->GetID(), i));
}
TEST_F(WebRtcEventLogManagerTest,
RemoteLogFilledLogNotCountedTowardsLogsLimit) {
const std::string log = "very_short_log";
int i;
for (i = 0; i < kMaxActiveRemoteLogFiles; i++) {
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), i));
EXPECT_CALL(remote_observer_,
OnRemoteLogStarted(PeerConnectionKey(rph_->GetID(), i), _))
.Times(1);
ASSERT_TRUE(StartRemoteLogging(rph_->GetID(), i, log.length()));
}
// By writing to one of the logs until it reaches capacity, we fill it,
// causing it to close, therefore allowing an additional log.
EXPECT_EQ(OnWebRtcEventLogWrite(rph_->GetID(), 0, log),
std::make_pair(false, true));
// We now have room for one additional log.
++i;
EXPECT_CALL(remote_observer_,
OnRemoteLogStarted(PeerConnectionKey(rph_->GetID(), i), _))
.Times(1);
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), i));
ASSERT_TRUE(StartRemoteLogging(rph_->GetID(), i));
}
TEST_F(WebRtcEventLogManagerTest,
RemoteLogForRemovedPeerConnectionNotCountedTowardsLogsLimit) {
for (int i = 0; i < kMaxActiveRemoteLogFiles; i++) {
const PeerConnectionKey key(rph_->GetID(), i);
ASSERT_TRUE(PeerConnectionAdded(key.render_process_id, key.lid));
EXPECT_CALL(remote_observer_, OnRemoteLogStarted(key, _)).Times(1);
ASSERT_TRUE(StartRemoteLogging(key.render_process_id, key.lid));
}
// By removing a peer connection associated with one of the logs, we allow
// an additional log.
ASSERT_TRUE(PeerConnectionRemoved(rph_->GetID(), 0));
// We now have room for one additional log.
const PeerConnectionKey last_key(rph_->GetID(), kMaxActiveRemoteLogFiles);
EXPECT_CALL(remote_observer_, OnRemoteLogStarted(last_key, _)).Times(1);
ASSERT_TRUE(PeerConnectionAdded(last_key.render_process_id, last_key.lid));
ASSERT_TRUE(StartRemoteLogging(last_key.render_process_id, last_key.lid));
}
TEST_F(WebRtcEventLogManagerTest,
ActiveLogsForBrowserContextCountedTowardsItsPendingsLogsLimit) {
SuppressUploading();
// Produce kMaxPendingRemoteLogFiles pending logs.
for (int i = 0; i < kMaxPendingRemoteLogFiles; i++) {
const PeerConnectionKey key(rph_->GetID(), i);
ASSERT_TRUE(PeerConnectionAdded(key.render_process_id, key.lid));
ASSERT_TRUE(StartRemoteLogging(key.render_process_id, key.lid));
ASSERT_TRUE(PeerConnectionRemoved(key.render_process_id, key.lid));
}
// It is now impossible to start another *active* log for that BrowserContext,
// because we have too many pending logs (and active logs become pending
// once completed).
const PeerConnectionKey forbidden(rph_->GetID(), kMaxPendingRemoteLogFiles);
ASSERT_TRUE(PeerConnectionAdded(forbidden.render_process_id, forbidden.lid));
EXPECT_FALSE(StartRemoteLogging(forbidden.render_process_id, forbidden.lid));
}
TEST_F(WebRtcEventLogManagerTest,
ObserveLimitOnMaximumPendingLogsPerBrowserContext) {
// Keep one active peer connection on an independent BrowserContext in order
// to suppress uploading of pending files during this test.
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), 1));
// Create additional BrowserContexts for the test.
std::unique_ptr<TestBrowserContext> browser_contexts[2] = {
std::make_unique<TestBrowserContext>(),
std::make_unique<TestBrowserContext>()};
std::unique_ptr<MockRenderProcessHost> rphs[2] = {
std::make_unique<MockRenderProcessHost>(browser_contexts[0].get()),
std::make_unique<MockRenderProcessHost>(browser_contexts[1].get())};
// Allowed to start kMaxPendingRemoteLogFiles for each BrowserContext.
for (int i = 0; i < kMaxPendingRemoteLogFiles; i++) {
const PeerConnectionKey key(rphs[0]->GetID(), i);
// The log could be opened:
ASSERT_TRUE(PeerConnectionAdded(key.render_process_id, key.lid));
ASSERT_TRUE(StartRemoteLogging(key.render_process_id, key.lid));
// The log changes state from ACTIVE to PENDING:
EXPECT_TRUE(PeerConnectionRemoved(key.render_process_id, key.lid));
}
// Not allowed to start any more remote-bound logs for that BrowserContext.
ASSERT_TRUE(PeerConnectionAdded(rphs[0]->GetID(), kMaxPendingRemoteLogFiles));
EXPECT_FALSE(StartRemoteLogging(rphs[0]->GetID(), kMaxPendingRemoteLogFiles));
// Other BrowserContexts aren't limit by the previous one's limit.
ASSERT_TRUE(PeerConnectionAdded(rphs[1]->GetID(), 0));
EXPECT_TRUE(StartRemoteLogging(rphs[1]->GetID(), 0));
}
TEST_F(WebRtcEventLogManagerTest,
LogsFromPreviousSessionBecomePendingLogsWhenBrowserContextInitialized) {
// Create a directory and seed it with log files, simulating the creation
// of logs in a previous session.
std::list<base::FilePath> expected_files;
base::ScopedTempDir browser_context_dir;
ASSERT_TRUE(browser_context_dir.CreateUniqueTempDir());
const base::FilePath remote_logs_dir =
GetLogsDirectoryPath(browser_context_dir.GetPath());
ASSERT_TRUE(CreateDirectory(remote_logs_dir));
for (size_t i = 0; i < kMaxPendingRemoteBoundWebRtcEventLogs; i++) {
const base::FilePath file_path =
remote_logs_dir.Append(IntToStringType(i))
.AddExtension(kRemoteBoundLogExtension);
constexpr int file_flags = base::File::FLAG_CREATE |
base::File::FLAG_WRITE |
base::File::FLAG_EXCLUSIVE_WRITE;
base::File file(file_path, file_flags);
ASSERT_TRUE(file.IsValid() && file.created());
expected_files.push_back(file_path);
}
// This factory enforces the expectation that the files will be uploaded,
// all of them, only them, and in the order expected.
base::RunLoop run_loop;
SetWebRtcEventLogUploaderFactoryForTesting(
std::make_unique<FileListExpectingWebRtcEventLogUploader::Factory>(
&expected_files, true, &run_loop));
auto browser_context = CreateBrowserContext(browser_context_dir.Take());
auto rph = std::make_unique<MockRenderProcessHost>(browser_context.get());
WaitForPendingTasks(&run_loop);
}
TEST_P(WebRtcEventLogManagerTest, FinishedRemoteLogsUploadedAndFileDeleted) {
// |upload_result| show that the files are deleted independent of the
// upload's success / failure.
const bool upload_result = GetParam();
const PeerConnectionKey key(rph_->GetID(), 1);
base::Optional<base::FilePath> log_file;
ON_CALL(remote_observer_, OnRemoteLogStarted(key, _))
.WillByDefault(Invoke(SaveFilePathTo(&log_file)));
ASSERT_TRUE(PeerConnectionAdded(key.render_process_id, key.lid));
ASSERT_TRUE(StartRemoteLogging(key.render_process_id, key.lid));
ASSERT_TRUE(log_file);
base::RunLoop run_loop;
std::list<base::FilePath> expected_files = {*log_file};
SetWebRtcEventLogUploaderFactoryForTesting(
std::make_unique<FileListExpectingWebRtcEventLogUploader::Factory>(
&expected_files, upload_result, &run_loop));
ASSERT_TRUE(PeerConnectionRemoved(key.render_process_id, key.lid));
const base::FilePath remote_logs_path =
GetLogsDirectoryPath(browser_context_->GetPath());
EXPECT_TRUE(base::IsDirectoryEmpty(remote_logs_path));
}
// Note that SuppressUploading() and UnSuppressUploading() use the behavior
// guaranteed by this test.
TEST_F(WebRtcEventLogManagerTest, UploadOnlyWhenNoActivePeerConnections) {
const PeerConnectionKey untracked(rph_->GetID(), 0);
const PeerConnectionKey tracked(rph_->GetID(), 1);
// Suppresses the uploading of the "tracked" peer connection's log.
ASSERT_TRUE(PeerConnectionAdded(untracked.render_process_id, untracked.lid));
// The tracked peer connection's log is not uploaded when finished, because
// another peer connection is still active.
base::Optional<base::FilePath> log_file;
ON_CALL(remote_observer_, OnRemoteLogStarted(tracked, _))
.WillByDefault(Invoke(SaveFilePathTo(&log_file)));
ASSERT_TRUE(PeerConnectionAdded(tracked.render_process_id, tracked.lid));
ASSERT_TRUE(StartRemoteLogging(tracked.render_process_id, tracked.lid));
ASSERT_TRUE(log_file);
ASSERT_TRUE(PeerConnectionRemoved(tracked.render_process_id, tracked.lid));
// Perform another action synchronously, so that we may be assured that the
// observer's lack of callbacks was not a timing fluke.
OnWebRtcEventLogWrite(untracked.render_process_id, untracked.lid, "Ook!");
// Having been convinced that |tracked|'s log was not uploded while
// |untracked| was active, close |untracked| and see that |tracked|'s log
// is now uploaded.
base::RunLoop run_loop;
std::list<base::FilePath> expected_uploads = {*log_file};
SetWebRtcEventLogUploaderFactoryForTesting(
std::make_unique<FileListExpectingWebRtcEventLogUploader::Factory>(
&expected_uploads, true, &run_loop));
ASSERT_TRUE(
PeerConnectionRemoved(untracked.render_process_id, untracked.lid));
WaitForPendingTasks(&run_loop);
}
TEST_F(WebRtcEventLogManagerTest, UploadOrderDependsOnLastModificationTime) {
SuppressUploading();
// Create three directories and seed them with log files.
base::FilePath browser_context_paths[3];
base::FilePath file_paths[3];
for (size_t i = 0; i < 3; i++) {
base::ScopedTempDir browser_context_dir;
ASSERT_TRUE(browser_context_dir.CreateUniqueTempDir());
browser_context_paths[i] = browser_context_dir.Take();
const base::FilePath remote_logs_dir =
GetLogsDirectoryPath(browser_context_paths[i]);
ASSERT_TRUE(CreateDirectory(remote_logs_dir));
file_paths[i] = remote_logs_dir.AppendASCII("file").AddExtension(
kRemoteBoundLogExtension);
constexpr int file_flags = base::File::FLAG_CREATE |
base::File::FLAG_WRITE |
base::File::FLAG_EXCLUSIVE_WRITE;
base::File file(file_paths[i], file_flags);
ASSERT_TRUE(file.IsValid() && file.created());
}
// Touch() requires setting the last access time as well. Keep it the same
// for all files, showing that the difference between them lies elsewhere.
base::File::Info file_info;
ASSERT_TRUE(base::GetFileInfo(file_paths[0], &file_info));
const base::Time shared_last_accessed = file_info.last_accessed;
// Set the files' last modification time according to a non-trivial
// permutation (not the order of creation or its reverse).
const size_t permutation[3] = {2, 0, 1};
std::list<base::FilePath> expected_files;
base::Time mod_time = base::Time::Now() - base::TimeDelta::FromSeconds(3);
for (size_t i = 0; i < 3; i++) {
mod_time += base::TimeDelta::FromSeconds(1); // Back to the future.
const base::FilePath& path = file_paths[permutation[i]];
ASSERT_TRUE(base::TouchFile(path, shared_last_accessed, mod_time));
expected_files.emplace_back(path);
}
// Recognize the files as pending files by initializing their BrowserContexts.
std::unique_ptr<TestBrowserContext> browser_contexts[3];
std::unique_ptr<MockRenderProcessHost> rphs[3];
for (size_t i = 0; i < 3; i++) {
browser_contexts[i] = CreateBrowserContext(browser_context_paths[i]);
rphs[i] =
std::make_unique<MockRenderProcessHost>(browser_contexts[i].get());
}
// Show that the files are uploaded by order of modification.
base::RunLoop run_loop;
SetWebRtcEventLogUploaderFactoryForTesting(
std::make_unique<FileListExpectingWebRtcEventLogUploader::Factory>(
&expected_files, true, &run_loop));
UnsuppressUploading();
WaitForPendingTasks(&run_loop);
}
TEST_F(WebRtcEventLogManagerTest, ExpiredFilesArePrunedRatherThanUploaded) {
SuppressUploading();
constexpr size_t kExpired = 0;
constexpr size_t kFresh = 1;
DCHECK_GE(kMaxPendingRemoteBoundWebRtcEventLogs, 2u)
<< "Please restructure the test to use separate browser contexts.";
base::FilePath browser_context_path;
base::FilePath file_paths[2];
base::ScopedTempDir browser_context_dir;
ASSERT_TRUE(browser_context_dir.CreateUniqueTempDir());
browser_context_path = browser_context_dir.Take();
const base::FilePath remote_logs_dir =
GetLogsDirectoryPath(browser_context_path);
ASSERT_TRUE(CreateDirectory(remote_logs_dir));
for (size_t i = 0; i < 2; i++) {
file_paths[i] = remote_logs_dir.Append(IntToStringType(i))
.AddExtension(kRemoteBoundLogExtension);
constexpr int file_flags = base::File::FLAG_CREATE |
base::File::FLAG_WRITE |
base::File::FLAG_EXCLUSIVE_WRITE;
base::File file(file_paths[i], file_flags);
ASSERT_TRUE(file.IsValid() && file.created());
}
// Touch() requires setting the last access time as well. Keep it current,
// showing that only the last modification time matters.
base::File::Info file_info;
ASSERT_TRUE(base::GetFileInfo(file_paths[0], &file_info));
// Set the expired file's last modification time to past max retention.
const base::Time expired_mod_time = base::Time::Now() -
kRemoteBoundWebRtcEventLogsMaxRetention -
base::TimeDelta::FromSeconds(1);
ASSERT_TRUE(base::TouchFile(file_paths[kExpired], file_info.last_accessed,
expired_mod_time));
// Recognize the file as pending by initializing its BrowserContext.
std::unique_ptr<TestBrowserContext> browser_context;
browser_context = CreateBrowserContext(browser_context_path);
// Show that the expired file is not uploaded.
base::RunLoop run_loop;
std::list<base::FilePath> expected_files = {file_paths[kFresh]};
SetWebRtcEventLogUploaderFactoryForTesting(
std::make_unique<FileListExpectingWebRtcEventLogUploader::Factory>(
&expected_files, true, &run_loop));
UnsuppressUploading();
WaitForPendingTasks(&run_loop);
// Both the uploaded file as well as the expired file have no been removed
// from local disk.
EXPECT_TRUE(
base::IsDirectoryEmpty(GetLogsDirectoryPath(browser_context->GetPath())));
}
// TODO(eladalon): Add a test showing that a file expiring while another
// is being uploaded, is not uploaded after the current upload is completed.
// This is significant because Chrome might stay up for a long time.
// https://crbug.com/775415
TEST_F(WebRtcEventLogManagerTest, RemoteLogSanityDestructorWithActiveFile) {
// We don't actually test that the file was closed, because this behavior
// is not supported - WebRtcEventLogManager's destructor may never be called,
// except for in unit tests.
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), kPeerConnectionId));
ASSERT_TRUE(PeerConnectionRemoved(rph_->GetID(), kPeerConnectionId));
DestroyUnitUnderTest(); // Explicitly show destruction does not crash.
}
TEST_F(WebRtcEventLogManagerTest, RemoteLogEmptyStringHandledGracefully) {
const PeerConnectionKey key(rph_->GetID(), kPeerConnectionId);
// By writing a log after the empty string, we show that no odd behavior is
// encountered, such as closing the file (an actual bug from WebRTC).
const std::vector<std::string> logs = {"<setup>", "", "<encore>"};
base::Optional<base::FilePath> file_path;
ON_CALL(remote_observer_, OnRemoteLogStarted(key, _))
.WillByDefault(Invoke(SaveFilePathTo(&file_path)));
ASSERT_TRUE(PeerConnectionAdded(key.render_process_id, key.lid));
ASSERT_TRUE(StartRemoteLogging(key.render_process_id, key.lid));
ASSERT_TRUE(file_path);
ASSERT_FALSE(file_path->empty());
for (size_t i = 0; i < logs.size(); i++) {
ASSERT_EQ(OnWebRtcEventLogWrite(key.render_process_id, key.lid, logs[i]),
std::make_pair(false, true));
}
ASSERT_TRUE(PeerConnectionRemoved(key.render_process_id, key.lid));
ExpectFileContents(
*file_path,
std::accumulate(std::begin(logs), std::end(logs), std::string()));
}
#if defined(OS_POSIX) && !defined(OS_FUCHSIA)
TEST_F(WebRtcEventLogManagerTest,
UnopenedRemoteLogFilesNotCountedTowardsActiveLogsLimit) {
// Set up BrowserContexts whose directories we know, so that we would be
// able to manipulate them.
std::unique_ptr<TestBrowserContext> browser_contexts[2];
std::unique_ptr<MockRenderProcessHost> rphs[2];
for (size_t i = 0; i < 2; i++) {
base::ScopedTempDir browser_context_dir;
ASSERT_TRUE(browser_context_dir.CreateUniqueTempDir());
browser_contexts[i] = CreateBrowserContext(browser_context_dir.Take());
rphs[i] =
std::make_unique<MockRenderProcessHost>(browser_contexts[i].get());
}
constexpr size_t without_permissions = 0;
constexpr size_t with_permissions = 1;
// Remove write permissions from one directory.
const base::FilePath permissions_lacking_remote_logs_path =
GetLogsDirectoryPath(browser_contexts[without_permissions]->GetPath());
ASSERT_TRUE(base::DirectoryExists(permissions_lacking_remote_logs_path));
RemoveWritePermissionsFromDirectory(permissions_lacking_remote_logs_path);
// Fail to start a log associated with the permission-lacking directory.
ASSERT_TRUE(PeerConnectionAdded(rphs[without_permissions]->GetID(), 0));
ASSERT_FALSE(StartRemoteLogging(rphs[without_permissions]->GetID(), 0));
// Show that this was not counted towards the limit of active files.
for (int i = 0; i < kMaxActiveRemoteLogFiles; i++) {
ASSERT_TRUE(PeerConnectionAdded(rphs[with_permissions]->GetID(), i));
EXPECT_TRUE(StartRemoteLogging(rphs[with_permissions]->GetID(), i));
}
}
#endif // defined(OS_POSIX) && !defined(OS_FUCHSIA)
TEST_F(WebRtcEventLogManagerTest,
NoStartWebRtcSendingEventLogsWhenLocalEnabledWithoutPeerConnection) {
SetPeerConnectionTrackerProxyForTesting(
std::make_unique<PeerConnectionTrackerProxyForTesting>(this));
ASSERT_TRUE(EnableLocalLogging());
EXPECT_TRUE(webrtc_state_change_instructions_.empty());
}
TEST_F(WebRtcEventLogManagerTest,
NoStartWebRtcSendingEventLogsWhenPeerConnectionButNoLoggingEnabled) {
SetPeerConnectionTrackerProxyForTesting(
std::make_unique<PeerConnectionTrackerProxyForTesting>(this));
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), kPeerConnectionId));
EXPECT_TRUE(webrtc_state_change_instructions_.empty());
}
TEST_F(WebRtcEventLogManagerTest,
StartWebRtcSendingEventLogsWhenLocalEnabledThenPeerConnectionAdded) {
SetPeerConnectionTrackerProxyForTesting(
std::make_unique<PeerConnectionTrackerProxyForTesting>(this));
ASSERT_TRUE(EnableLocalLogging());
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), kPeerConnectionId));
ExpectWebRtcStateChangeInstruction(rph_->GetID(), kPeerConnectionId, true);
}
TEST_F(WebRtcEventLogManagerTest,
StartWebRtcSendingEventLogsWhenPeerConnectionAddedThenLocalEnabled) {
SetPeerConnectionTrackerProxyForTesting(
std::make_unique<PeerConnectionTrackerProxyForTesting>(this));
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), kPeerConnectionId));
ASSERT_TRUE(EnableLocalLogging());
ExpectWebRtcStateChangeInstruction(rph_->GetID(), kPeerConnectionId, true);
}
TEST_F(WebRtcEventLogManagerTest,
StartWebRtcSendingEventLogsWhenRemoteLoggingEnabled) {
SetPeerConnectionTrackerProxyForTesting(
std::make_unique<PeerConnectionTrackerProxyForTesting>(this));
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), kPeerConnectionId));
ASSERT_TRUE(StartRemoteLogging(rph_->GetID(), kPeerConnectionId));
ExpectWebRtcStateChangeInstruction(rph_->GetID(), kPeerConnectionId, true);
}
TEST_F(WebRtcEventLogManagerTest,
InstructWebRtcToStopSendingEventLogsWhenLocalLoggingStopped) {
// Setup
SetPeerConnectionTrackerProxyForTesting(
std::make_unique<PeerConnectionTrackerProxyForTesting>(this));
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), kPeerConnectionId));
ASSERT_TRUE(EnableLocalLogging());
ExpectWebRtcStateChangeInstruction(rph_->GetID(), kPeerConnectionId, true);
// Test
ASSERT_TRUE(DisableLocalLogging());
ExpectWebRtcStateChangeInstruction(rph_->GetID(), kPeerConnectionId, false);
}
// #1 - Local logging was the cause of the logs.
TEST_F(WebRtcEventLogManagerTest,
InstructWebRtcToStopSendingEventLogsWhenPeerConnectionRemoved1) {
// Setup
SetPeerConnectionTrackerProxyForTesting(
std::make_unique<PeerConnectionTrackerProxyForTesting>(this));
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), kPeerConnectionId));
ASSERT_TRUE(EnableLocalLogging());
ExpectWebRtcStateChangeInstruction(rph_->GetID(), kPeerConnectionId, true);
// Test
ASSERT_TRUE(PeerConnectionRemoved(rph_->GetID(), kPeerConnectionId));
ExpectWebRtcStateChangeInstruction(rph_->GetID(), kPeerConnectionId, false);
}
// #2 - Remote logging was the cause of the logs.
TEST_F(WebRtcEventLogManagerTest,
InstructWebRtcToStopSendingEventLogsWhenPeerConnectionRemoved2) {
// Setup
SetPeerConnectionTrackerProxyForTesting(
std::make_unique<PeerConnectionTrackerProxyForTesting>(this));
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), kPeerConnectionId));
ASSERT_TRUE(StartRemoteLogging(rph_->GetID(), kPeerConnectionId));
ExpectWebRtcStateChangeInstruction(rph_->GetID(), kPeerConnectionId, true);
// Test
ASSERT_TRUE(PeerConnectionRemoved(rph_->GetID(), kPeerConnectionId));
ExpectWebRtcStateChangeInstruction(rph_->GetID(), kPeerConnectionId, false);
}
// #1 - Local logging added first.
TEST_F(WebRtcEventLogManagerTest,
SecondLoggingTargetDoesNotInitiateWebRtcLogging1) {
// Setup
SetPeerConnectionTrackerProxyForTesting(
std::make_unique<PeerConnectionTrackerProxyForTesting>(this));
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), kPeerConnectionId));
ASSERT_TRUE(EnableLocalLogging());
ExpectWebRtcStateChangeInstruction(rph_->GetID(), kPeerConnectionId, true);
// Test
ASSERT_TRUE(StartRemoteLogging(rph_->GetID(), kPeerConnectionId));
EXPECT_TRUE(webrtc_state_change_instructions_.empty());
}
// #2 - Remote logging added first.
TEST_F(WebRtcEventLogManagerTest,
SecondLoggingTargetDoesNotInitiateWebRtcLogging2) {
// Setup
SetPeerConnectionTrackerProxyForTesting(
std::make_unique<PeerConnectionTrackerProxyForTesting>(this));
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), kPeerConnectionId));
ASSERT_TRUE(StartRemoteLogging(rph_->GetID(), kPeerConnectionId));
ExpectWebRtcStateChangeInstruction(rph_->GetID(), kPeerConnectionId, true);
// Test
ASSERT_TRUE(EnableLocalLogging());
EXPECT_TRUE(webrtc_state_change_instructions_.empty());
}
TEST_F(WebRtcEventLogManagerTest,
DisablingLocalLoggingWhenRemoteLoggingEnabledDoesNotStopWebRtcLogging) {
// Setup
SetPeerConnectionTrackerProxyForTesting(
std::make_unique<PeerConnectionTrackerProxyForTesting>(this));
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), kPeerConnectionId));
ASSERT_TRUE(EnableLocalLogging());
ASSERT_TRUE(StartRemoteLogging(rph_->GetID(), kPeerConnectionId));
ExpectWebRtcStateChangeInstruction(rph_->GetID(), kPeerConnectionId, true);
// Test
ASSERT_TRUE(DisableLocalLogging());
EXPECT_TRUE(webrtc_state_change_instructions_.empty());
// Cleanup
ASSERT_TRUE(PeerConnectionRemoved(rph_->GetID(), kPeerConnectionId));
ExpectWebRtcStateChangeInstruction(rph_->GetID(), kPeerConnectionId, false);
}
TEST_F(WebRtcEventLogManagerTest,
DisablingLocalLoggingAfterPcRemovalHasNoEffectOnWebRtcLogging) {
// Setup
SetPeerConnectionTrackerProxyForTesting(
std::make_unique<PeerConnectionTrackerProxyForTesting>(this));
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), kPeerConnectionId));
ASSERT_TRUE(EnableLocalLogging());
ASSERT_TRUE(StartRemoteLogging(rph_->GetID(), kPeerConnectionId));
ExpectWebRtcStateChangeInstruction(rph_->GetID(), kPeerConnectionId, true);
// Test
ASSERT_TRUE(PeerConnectionRemoved(rph_->GetID(), kPeerConnectionId));
ExpectWebRtcStateChangeInstruction(rph_->GetID(), kPeerConnectionId, false);
ASSERT_TRUE(DisableLocalLogging());
EXPECT_TRUE(webrtc_state_change_instructions_.empty());
}
// Once a peer connection with a given key was removed, it may not again be
// added. But, if this impossible case occurs, WebRtcEventLogManager will
// not crash.
TEST_F(WebRtcEventLogManagerTest, SanityOverRecreatingTheSamePeerConnection) {
ASSERT_TRUE(EnableLocalLogging());
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), kPeerConnectionId));
ASSERT_TRUE(StartRemoteLogging(rph_->GetID(), kPeerConnectionId));
OnWebRtcEventLogWrite(rph_->GetID(), kPeerConnectionId, "log1");
ASSERT_TRUE(PeerConnectionRemoved(rph_->GetID(), kPeerConnectionId));
ASSERT_TRUE(PeerConnectionAdded(rph_->GetID(), kPeerConnectionId));
OnWebRtcEventLogWrite(rph_->GetID(), kPeerConnectionId, "log2");
}
// The logs would typically be binary. However, the other tests only cover ASCII
// characters, for readability. This test shows that this is not a problem.
TEST_F(WebRtcEventLogManagerTest, LogAllPossibleCharacters) {
const PeerConnectionKey key(rph_->GetID(), kPeerConnectionId);
base::Optional<base::FilePath> local_log_file_path;
ON_CALL(local_observer_, OnLocalLogStarted(key, _))
.WillByDefault(Invoke(SaveFilePathTo(&local_log_file_path)));
base::Optional<base::FilePath> remote_log_file_path;
ON_CALL(remote_observer_, OnRemoteLogStarted(key, _))
.WillByDefault(Invoke(SaveFilePathTo(&remote_log_file_path)));
ASSERT_TRUE(EnableLocalLogging());
ASSERT_TRUE(PeerConnectionAdded(key.render_process_id, key.lid));
ASSERT_TRUE(StartRemoteLogging(key.render_process_id, key.lid));
ASSERT_TRUE(local_log_file_path);
ASSERT_FALSE(local_log_file_path->empty());
ASSERT_TRUE(remote_log_file_path);
ASSERT_FALSE(remote_log_file_path->empty());
std::string all_chars;
for (size_t i = 0; i < 256; i++) {
all_chars += static_cast<uint8_t>(i);
}
ASSERT_EQ(OnWebRtcEventLogWrite(key.render_process_id, key.lid, all_chars),
std::make_pair(true, true));
// Make sure the file would be closed, so that we could safely read it.
ASSERT_TRUE(PeerConnectionRemoved(key.render_process_id, key.lid));
ExpectFileContents(*local_log_file_path, all_chars);
ExpectFileContents(*remote_log_file_path, all_chars);
}
TEST_F(WebRtcEventLogManagerTest, LocalLogsClosedWhenRenderProcessHostExits) {
const PeerConnectionKey key(rph_->GetID(), kPeerConnectionId);
ASSERT_TRUE(PeerConnectionAdded(key.render_process_id, key.lid));
ASSERT_TRUE(EnableLocalLogging());
// The expectation for OnLocalLogStopped() will be saturated by this
// destruction of the RenderProcessHost, which triggers an implicit
// removal of all PeerConnections associated with it.
EXPECT_CALL(local_observer_, OnLocalLogStopped(key)).Times(1);
rph_.reset();
}
TEST_F(WebRtcEventLogManagerTest, RemoteLogsClosedWhenRenderProcessHostExits) {
const PeerConnectionKey key(rph_->GetID(), kPeerConnectionId);
ASSERT_TRUE(PeerConnectionAdded(key.render_process_id, key.lid));
ASSERT_TRUE(StartRemoteLogging(key.render_process_id, key.lid));
// The expectation for OnRemoteLogStopped() will be saturated by this
// destruction of the RenderProcessHost, which triggers an implicit
// removal of all PeerConnections associated with it.
EXPECT_CALL(remote_observer_, OnRemoteLogStopped(key)).Times(1);
rph_.reset();
}
// Once a RenderProcessHost exits/crashes, its PeerConnections are removed,
// which means that they can no longer suppress an upload.
TEST_F(WebRtcEventLogManagerTest,
RenderProcessHostExitCanRemoveUploadSuppression) {
SuppressUploading();
const PeerConnectionKey key(rph_->GetID(), kPeerConnectionId);
base::Optional<base::FilePath> file_path;
ON_CALL(remote_observer_, OnRemoteLogStarted(key, _))
.WillByDefault(Invoke(SaveFilePathTo(&file_path)));
ASSERT_TRUE(PeerConnectionAdded(key.render_process_id, key.lid));
ASSERT_TRUE(StartRemoteLogging(key.render_process_id, key.lid));
ASSERT_TRUE(PeerConnectionRemoved(key.render_process_id, key.lid));
ASSERT_TRUE(file_path);
ASSERT_FALSE(file_path->empty());
// The above removal is not sufficient to trigger an upload (so the test will
// not be flaky). It's only once we destroy the RPH with which the suppressing
// PeerConnection is associated, that upload will take place.
base::RunLoop run_loop;
std::list<base::FilePath> expected_files = {*file_path};
SetWebRtcEventLogUploaderFactoryForTesting(
std::make_unique<FileListExpectingWebRtcEventLogUploader::Factory>(
&expected_files, true, &run_loop));
// We destroy the RPH without explicitly removing its PeerConnection (unlike
// a call to UnsuppressUploading()).
upload_suppressing_rph_.reset();
WaitForPendingTasks(&run_loop);
}
INSTANTIATE_TEST_CASE_P(UploadCompleteResult,
WebRtcEventLogManagerTest,
::testing::Values(false, true));
} // namespace content