blob: 40a9e83c2c78b06d862f6725e0a8cadf6b384f91 [file] [log] [blame]
// Copyright 2018 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 "base/memory/raw_ptr.h"
#include "chrome/browser/media/webrtc/webrtc_event_log_uploader.h"
#include <memory>
#include <string>
#include "base/bind.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/memory/scoped_refptr.h"
#include "base/run_loop.h"
#include "build/build_config.h"
#include "chrome/browser/media/webrtc/webrtc_event_log_manager_common.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "content/public/test/browser_task_environment.h"
#include "net/http/http_status_code.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "services/network/test/test_url_loader_factory.h"
#include "services/network/test/test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace webrtc_event_logging {
using ::testing::_;
using ::testing::StrictMock;
using BrowserContextId = WebRtcEventLogPeerConnectionKey::BrowserContextId;
namespace {
class UploadObserver {
public:
explicit UploadObserver(base::OnceClosure on_complete_callback)
: on_complete_callback_(std::move(on_complete_callback)) {}
// Combines the mock functionality via a helper (CompletionCallback),
// as well as unblocks its owner through |on_complete_callback_|.
void OnWebRtcEventLogUploadComplete(const base::FilePath& log_file,
bool upload_successful) {
CompletionCallback(log_file, upload_successful);
std::move(on_complete_callback_).Run();
}
MOCK_METHOD2(CompletionCallback, void(const base::FilePath&, bool));
private:
base::OnceClosure on_complete_callback_;
};
#if defined(OS_POSIX)
void RemovePermissions(const base::FilePath& path, int removed_permissions) {
int permissions;
ASSERT_TRUE(base::GetPosixFilePermissions(path, &permissions));
permissions &= ~removed_permissions;
ASSERT_TRUE(base::SetPosixFilePermissions(path, permissions));
}
void RemoveReadPermissions(const base::FilePath& path) {
constexpr int read_permissions = base::FILE_PERMISSION_READ_BY_USER |
base::FILE_PERMISSION_READ_BY_GROUP |
base::FILE_PERMISSION_READ_BY_OTHERS;
RemovePermissions(path, read_permissions);
}
#endif // defined(OS_POSIX)
} // namespace
class WebRtcEventLogUploaderImplTest : public ::testing::Test {
public:
WebRtcEventLogUploaderImplTest()
: test_shared_url_loader_factory_(
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory_)),
observer_run_loop_(),
observer_(observer_run_loop_.QuitWhenIdleClosure()) {
TestingBrowserProcess::GetGlobal()->SetSharedURLLoaderFactory(
test_shared_url_loader_factory_);
EXPECT_TRUE(base::Time::FromString("30 Dec 1983", &kReasonableTime));
uploader_factory_ = std::make_unique<WebRtcEventLogUploaderImpl::Factory>(
base::SequencedTaskRunnerHandle::Get());
}
~WebRtcEventLogUploaderImplTest() override {
task_environment_.RunUntilIdle();
}
void SetUp() override {
testing_profile_manager_ = std::make_unique<TestingProfileManager>(
TestingBrowserProcess::GetGlobal());
EXPECT_TRUE(profiles_dir_.CreateUniqueTempDir());
EXPECT_TRUE(testing_profile_manager_->SetUp(profiles_dir_.GetPath()));
testing_profile_ =
testing_profile_manager_->CreateTestingProfile("arbitrary_name");
browser_context_id_ = GetBrowserContextId(testing_profile_);
// Create the sub-dir for the remote-bound logs that would have been set
// up by WebRtcEventLogManager, if WebRtcEventLogManager were instantiated.
// Note that the testing profile's overall directory is a temporary one.
const base::FilePath logs_dir =
GetRemoteBoundWebRtcEventLogsDir(testing_profile_->GetPath());
ASSERT_TRUE(base::CreateDirectory(logs_dir));
// Create a log file and put some arbitrary data in it.
// Note that the testing profile's overall directory is a temporary one.
ASSERT_TRUE(base::CreateTemporaryFileInDir(logs_dir, &log_file_));
constexpr size_t kLogFileSizeBytes = 100u;
const std::string file_contents(kLogFileSizeBytes, 'A');
ASSERT_EQ(
base::WriteFile(log_file_, file_contents.c_str(), file_contents.size()),
static_cast<int>(file_contents.size()));
}
// For tests which imitate a response (or several).
void SetURLLoaderResponse(net::HttpStatusCode http_code, int net_error) {
DCHECK(test_shared_url_loader_factory_);
const std::string kResponseId = "ec1ed029734b8f7e"; // Arbitrary.
test_url_loader_factory_.AddResponse(
GURL(WebRtcEventLogUploaderImpl::kUploadURL),
network::CreateURLResponseHead(http_code), kResponseId,
network::URLLoaderCompletionStatus(net_error));
}
void StartAndWaitForUpload(
BrowserContextId browser_context_id = BrowserContextId(),
base::Time last_modified_time = base::Time()) {
DCHECK(test_shared_url_loader_factory_);
if (last_modified_time.is_null()) {
last_modified_time = kReasonableTime;
}
const WebRtcLogFileInfo log_file_info(browser_context_id, log_file_,
last_modified_time);
uploader_ = uploader_factory_->Create(log_file_info, ResultCallback());
observer_run_loop_.Run(); // Observer was given quit-closure by ctor.
}
void StartAndWaitForUploadWithCustomMaxSize(
size_t max_log_size_bytes,
BrowserContextId browser_context_id = BrowserContextId(),
base::Time last_modified_time = base::Time()) {
DCHECK(test_shared_url_loader_factory_);
if (last_modified_time.is_null()) {
last_modified_time = kReasonableTime;
}
const WebRtcLogFileInfo log_file_info(browser_context_id, log_file_,
last_modified_time);
uploader_ = uploader_factory_->CreateWithCustomMaxSizeForTesting(
log_file_info, ResultCallback(), max_log_size_bytes);
observer_run_loop_.Run(); // Observer was given quit-closure by ctor.
}
void StartUploadThatWillNotTerminate(
BrowserContextId browser_context_id = BrowserContextId(),
base::Time last_modified_time = base::Time()) {
DCHECK(test_shared_url_loader_factory_);
if (last_modified_time.is_null()) {
last_modified_time = kReasonableTime;
}
const WebRtcLogFileInfo log_file_info(browser_context_id, log_file_,
last_modified_time);
uploader_ = uploader_factory_->Create(log_file_info, ResultCallback());
}
WebRtcEventLogUploader::UploadResultCallback ResultCallback() {
return base::BindOnce(&UploadObserver::OnWebRtcEventLogUploadComplete,
base::Unretained(&observer_));
}
content::BrowserTaskEnvironment task_environment_;
base::Time kReasonableTime;
network::TestURLLoaderFactory test_url_loader_factory_;
scoped_refptr<network::SharedURLLoaderFactory>
test_shared_url_loader_factory_;
base::RunLoop observer_run_loop_;
base::ScopedTempDir profiles_dir_;
std::unique_ptr<TestingProfileManager> testing_profile_manager_;
raw_ptr<TestingProfile> testing_profile_; // |testing_profile_manager_| owns.
BrowserContextId browser_context_id_;
base::FilePath log_file_;
StrictMock<UploadObserver> observer_;
// These (uploader-factory and uploader) are the units under test.
std::unique_ptr<WebRtcEventLogUploaderImpl::Factory> uploader_factory_;
std::unique_ptr<WebRtcEventLogUploader> uploader_;
};
TEST_F(WebRtcEventLogUploaderImplTest, SuccessfulUploadReportedToObserver) {
SetURLLoaderResponse(net::HTTP_OK, net::OK);
EXPECT_CALL(observer_, CompletionCallback(log_file_, true)).Times(1);
StartAndWaitForUpload();
EXPECT_FALSE(base::PathExists(log_file_));
}
// Version #1 - request reported as successful, but got an error (404) as the
// HTTP return code.
// Due to the simplicitly of both tests, this also tests the scenario
// FileDeletedAfterUnsuccessfulUpload, rather than giving each its own test.
TEST_F(WebRtcEventLogUploaderImplTest, UnsuccessfulUploadReportedToObserver1) {
SetURLLoaderResponse(net::HTTP_NOT_FOUND, net::OK);
EXPECT_CALL(observer_, CompletionCallback(log_file_, false)).Times(1);
StartAndWaitForUpload();
EXPECT_FALSE(base::PathExists(log_file_));
}
// Version #2 - request reported as failed; HTTP return code ignored, even
// if it's a purported success.
TEST_F(WebRtcEventLogUploaderImplTest, UnsuccessfulUploadReportedToObserver2) {
SetURLLoaderResponse(net::HTTP_NOT_FOUND, net::ERR_FAILED);
EXPECT_CALL(observer_, CompletionCallback(log_file_, false)).Times(1);
StartAndWaitForUpload();
EXPECT_FALSE(base::PathExists(log_file_));
}
#if defined(OS_POSIX)
TEST_F(WebRtcEventLogUploaderImplTest, FailureToReadFileReportedToObserver) {
// Show the failure was independent of the URLLoaderFactory's primed return
// value.
SetURLLoaderResponse(net::HTTP_OK, net::OK);
RemoveReadPermissions(log_file_);
EXPECT_CALL(observer_, CompletionCallback(log_file_, false)).Times(1);
StartAndWaitForUpload();
}
TEST_F(WebRtcEventLogUploaderImplTest, NonExistentFileReportedToObserver) {
// Show the failure was independent of the URLLoaderFactory's primed return
// value.
SetURLLoaderResponse(net::HTTP_OK, net::OK);
log_file_ = log_file_.Append(FILE_PATH_LITERAL("garbage"));
EXPECT_CALL(observer_, CompletionCallback(log_file_, false)).Times(1);
StartAndWaitForUpload();
}
#endif // defined(OS_POSIX)
TEST_F(WebRtcEventLogUploaderImplTest, FilesUpToMaxSizeUploaded) {
int64_t log_file_size_bytes;
ASSERT_TRUE(base::GetFileSize(log_file_, &log_file_size_bytes));
SetURLLoaderResponse(net::HTTP_OK, net::OK);
EXPECT_CALL(observer_, CompletionCallback(log_file_, true)).Times(1);
StartAndWaitForUploadWithCustomMaxSize(log_file_size_bytes);
EXPECT_FALSE(base::PathExists(log_file_));
}
TEST_F(WebRtcEventLogUploaderImplTest, ExcessivelyLargeFilesNotUploaded) {
int64_t log_file_size_bytes;
ASSERT_TRUE(base::GetFileSize(log_file_, &log_file_size_bytes));
SetURLLoaderResponse(net::HTTP_OK, net::OK);
EXPECT_CALL(observer_, CompletionCallback(log_file_, false)).Times(1);
StartAndWaitForUploadWithCustomMaxSize(log_file_size_bytes - 1);
EXPECT_FALSE(base::PathExists(log_file_));
}
TEST_F(WebRtcEventLogUploaderImplTest,
CancelBeforeUploadCompletionCallsCallbackWithFalse) {
const base::Time last_modified = base::Time::Now();
StartUploadThatWillNotTerminate(browser_context_id_, last_modified);
EXPECT_CALL(observer_, CompletionCallback(log_file_, false)).Times(1);
uploader_->Cancel();
}
TEST_F(WebRtcEventLogUploaderImplTest, SecondCallToCancelHasNoEffect) {
const base::Time last_modified = base::Time::Now();
StartUploadThatWillNotTerminate(browser_context_id_, last_modified);
EXPECT_CALL(observer_, CompletionCallback(log_file_, _)).Times(1);
uploader_->Cancel();
uploader_->Cancel();
}
TEST_F(WebRtcEventLogUploaderImplTest,
CancelAfterUploadCompletionCallbackWasCalledHasNoEffect) {
SetURLLoaderResponse(net::HTTP_OK, net::OK);
EXPECT_CALL(observer_, CompletionCallback(log_file_, true)).Times(1);
StartAndWaitForUpload();
EXPECT_CALL(observer_, CompletionCallback(_, _)).Times(0);
uploader_->Cancel();
}
TEST_F(WebRtcEventLogUploaderImplTest, CancelOnAbortedUploadHasNoEffect) {
// Show the failure was independent of the URLLoaderFactory's primed return
// value.
SetURLLoaderResponse(net::HTTP_OK, net::OK);
log_file_ = log_file_.Append(FILE_PATH_LITERAL("garbage"));
EXPECT_CALL(observer_, CompletionCallback(log_file_, false)).Times(1);
StartAndWaitForUpload();
EXPECT_CALL(observer_, CompletionCallback(_, _)).Times(0);
uploader_->Cancel();
}
TEST_F(WebRtcEventLogUploaderImplTest, CancelOnOngoingUploadDeletesFile) {
const base::Time last_modified = base::Time::Now();
StartUploadThatWillNotTerminate(browser_context_id_, last_modified);
EXPECT_CALL(observer_, CompletionCallback(log_file_, false)).Times(1);
uploader_->Cancel();
observer_run_loop_.Run();
EXPECT_FALSE(base::PathExists(log_file_));
}
TEST_F(WebRtcEventLogUploaderImplTest,
GetWebRtcLogFileInfoReturnsCorrectInfoBeforeUploadDone) {
const base::Time last_modified = base::Time::Now();
StartUploadThatWillNotTerminate(browser_context_id_, last_modified);
const WebRtcLogFileInfo info = uploader_->GetWebRtcLogFileInfo();
EXPECT_EQ(info.browser_context_id, browser_context_id_);
EXPECT_EQ(info.path, log_file_);
EXPECT_EQ(info.last_modified, last_modified);
// Test tear-down.
EXPECT_CALL(observer_, CompletionCallback(log_file_, false)).Times(1);
uploader_->Cancel();
}
TEST_F(WebRtcEventLogUploaderImplTest,
GetWebRtcLogFileInfoReturnsCorrectInfoAfterUploadSucceeded) {
SetURLLoaderResponse(net::HTTP_OK, net::OK);
EXPECT_CALL(observer_, CompletionCallback(log_file_, true)).Times(1);
const base::Time last_modified = base::Time::Now();
StartAndWaitForUpload(browser_context_id_, last_modified);
const WebRtcLogFileInfo info = uploader_->GetWebRtcLogFileInfo();
EXPECT_EQ(info.browser_context_id, browser_context_id_);
EXPECT_EQ(info.path, log_file_);
EXPECT_EQ(info.last_modified, last_modified);
}
TEST_F(WebRtcEventLogUploaderImplTest,
GetWebRtcLogFileInfoReturnsCorrectInfoWhenCalledOnCancelledUpload) {
const base::Time last_modified = base::Time::Now();
StartUploadThatWillNotTerminate(browser_context_id_, last_modified);
EXPECT_CALL(observer_, CompletionCallback(log_file_, false)).Times(1);
uploader_->Cancel();
const WebRtcLogFileInfo info = uploader_->GetWebRtcLogFileInfo();
EXPECT_EQ(info.browser_context_id, browser_context_id_);
EXPECT_EQ(info.path, log_file_);
EXPECT_EQ(info.last_modified, last_modified);
}
// TODO(crbug.com/775415): Add a unit test that shows that files with
// non-ASCII filenames are discard. (Or, alternatively, add support for them.)
} // namespace webrtc_event_logging