1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/media/webrtc/webrtc_event_log_uploader.h"
6
7 #include <memory>
8 #include <string>
9
10 #include "base/bind.h"
11 #include "base/callback_forward.h"
12 #include "base/files/file_path.h"
13 #include "base/files/file_util.h"
14 #include "base/files/scoped_temp_dir.h"
15 #include "base/memory/scoped_refptr.h"
16 #include "base/run_loop.h"
17 #include "build/build_config.h"
18 #include "chrome/browser/media/webrtc/webrtc_event_log_manager_common.h"
19 #include "chrome/test/base/testing_browser_process.h"
20 #include "chrome/test/base/testing_profile_manager.h"
21 #include "content/public/test/browser_task_environment.h"
22 #include "net/http/http_status_code.h"
23 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
24 #include "services/network/test/test_url_loader_factory.h"
25 #include "services/network/test/test_utils.h"
26 #include "testing/gmock/include/gmock/gmock.h"
27 #include "testing/gtest/include/gtest/gtest.h"
28
29 namespace webrtc_event_logging {
30
31 using ::testing::_;
32 using ::testing::StrictMock;
33 using BrowserContextId = WebRtcEventLogPeerConnectionKey::BrowserContextId;
34
35 namespace {
36 class UploadObserver {
37 public:
UploadObserver(base::OnceClosure on_complete_callback)38 explicit UploadObserver(base::OnceClosure on_complete_callback)
39 : on_complete_callback_(std::move(on_complete_callback)) {}
40
41 // Combines the mock functionality via a helper (CompletionCallback),
42 // as well as unblocks its owner through |on_complete_callback_|.
OnWebRtcEventLogUploadComplete(const base::FilePath & log_file,bool upload_successful)43 void OnWebRtcEventLogUploadComplete(const base::FilePath& log_file,
44 bool upload_successful) {
45 CompletionCallback(log_file, upload_successful);
46 std::move(on_complete_callback_).Run();
47 }
48
49 MOCK_METHOD2(CompletionCallback, void(const base::FilePath&, bool));
50
51 private:
52 base::OnceClosure on_complete_callback_;
53 };
54
55 #if defined(OS_POSIX)
RemovePermissions(const base::FilePath & path,int removed_permissions)56 void RemovePermissions(const base::FilePath& path, int removed_permissions) {
57 int permissions;
58 ASSERT_TRUE(base::GetPosixFilePermissions(path, &permissions));
59 permissions &= ~removed_permissions;
60 ASSERT_TRUE(base::SetPosixFilePermissions(path, permissions));
61 }
62
RemoveReadPermissions(const base::FilePath & path)63 void RemoveReadPermissions(const base::FilePath& path) {
64 constexpr int read_permissions = base::FILE_PERMISSION_READ_BY_USER |
65 base::FILE_PERMISSION_READ_BY_GROUP |
66 base::FILE_PERMISSION_READ_BY_OTHERS;
67 RemovePermissions(path, read_permissions);
68 }
69 #endif // defined(OS_POSIX)
70 } // namespace
71
72 class WebRtcEventLogUploaderImplTest : public ::testing::Test {
73 public:
WebRtcEventLogUploaderImplTest()74 WebRtcEventLogUploaderImplTest()
75 : test_shared_url_loader_factory_(
76 base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
77 &test_url_loader_factory_)),
78 observer_run_loop_(),
79 observer_(observer_run_loop_.QuitWhenIdleClosure()) {
80 TestingBrowserProcess::GetGlobal()->SetSharedURLLoaderFactory(
81 test_shared_url_loader_factory_);
82
83 EXPECT_TRUE(base::Time::FromString("30 Dec 1983", &kReasonableTime));
84
85 uploader_factory_ = std::make_unique<WebRtcEventLogUploaderImpl::Factory>(
86 base::SequencedTaskRunnerHandle::Get());
87 }
88
~WebRtcEventLogUploaderImplTest()89 ~WebRtcEventLogUploaderImplTest() override {
90 task_environment_.RunUntilIdle();
91 }
92
SetUp()93 void SetUp() override {
94 testing_profile_manager_ = std::make_unique<TestingProfileManager>(
95 TestingBrowserProcess::GetGlobal());
96 EXPECT_TRUE(profiles_dir_.CreateUniqueTempDir());
97 EXPECT_TRUE(testing_profile_manager_->SetUp(profiles_dir_.GetPath()));
98
99 testing_profile_ =
100 testing_profile_manager_->CreateTestingProfile("arbitrary_name");
101
102 browser_context_id_ = GetBrowserContextId(testing_profile_);
103
104 // Create the sub-dir for the remote-bound logs that would have been set
105 // up by WebRtcEventLogManager, if WebRtcEventLogManager were instantiated.
106 // Note that the testing profile's overall directory is a temporary one.
107 const base::FilePath logs_dir =
108 GetRemoteBoundWebRtcEventLogsDir(testing_profile_->GetPath());
109 ASSERT_TRUE(base::CreateDirectory(logs_dir));
110
111 // Create a log file and put some arbitrary data in it.
112 // Note that the testing profile's overall directory is a temporary one.
113 ASSERT_TRUE(base::CreateTemporaryFileInDir(logs_dir, &log_file_));
114 constexpr size_t kLogFileSizeBytes = 100u;
115 const std::string file_contents(kLogFileSizeBytes, 'A');
116 ASSERT_EQ(
117 base::WriteFile(log_file_, file_contents.c_str(), file_contents.size()),
118 static_cast<int>(file_contents.size()));
119 }
120
121 // For tests which imitate a response (or several).
SetURLLoaderResponse(net::HttpStatusCode http_code,int net_error)122 void SetURLLoaderResponse(net::HttpStatusCode http_code, int net_error) {
123 DCHECK(test_shared_url_loader_factory_);
124 const std::string kResponseId = "ec1ed029734b8f7e"; // Arbitrary.
125 test_url_loader_factory_.AddResponse(
126 GURL(WebRtcEventLogUploaderImpl::kUploadURL),
127 network::CreateURLResponseHead(http_code), kResponseId,
128 network::URLLoaderCompletionStatus(net_error));
129 }
130
StartAndWaitForUpload(BrowserContextId browser_context_id=BrowserContextId (),base::Time last_modified_time=base::Time ())131 void StartAndWaitForUpload(
132 BrowserContextId browser_context_id = BrowserContextId(),
133 base::Time last_modified_time = base::Time()) {
134 DCHECK(test_shared_url_loader_factory_);
135
136 if (last_modified_time.is_null()) {
137 last_modified_time = kReasonableTime;
138 }
139
140 const WebRtcLogFileInfo log_file_info(browser_context_id, log_file_,
141 last_modified_time);
142
143 uploader_ = uploader_factory_->Create(log_file_info, ResultCallback());
144
145 observer_run_loop_.Run(); // Observer was given quit-closure by ctor.
146 }
147
StartAndWaitForUploadWithCustomMaxSize(size_t max_log_size_bytes,BrowserContextId browser_context_id=BrowserContextId (),base::Time last_modified_time=base::Time ())148 void StartAndWaitForUploadWithCustomMaxSize(
149 size_t max_log_size_bytes,
150 BrowserContextId browser_context_id = BrowserContextId(),
151 base::Time last_modified_time = base::Time()) {
152 DCHECK(test_shared_url_loader_factory_);
153
154 if (last_modified_time.is_null()) {
155 last_modified_time = kReasonableTime;
156 }
157
158 const WebRtcLogFileInfo log_file_info(browser_context_id, log_file_,
159 last_modified_time);
160
161 uploader_ = uploader_factory_->CreateWithCustomMaxSizeForTesting(
162 log_file_info, ResultCallback(), max_log_size_bytes);
163
164 observer_run_loop_.Run(); // Observer was given quit-closure by ctor.
165 }
166
StartUploadThatWillNotTerminate(BrowserContextId browser_context_id=BrowserContextId (),base::Time last_modified_time=base::Time ())167 void StartUploadThatWillNotTerminate(
168 BrowserContextId browser_context_id = BrowserContextId(),
169 base::Time last_modified_time = base::Time()) {
170 DCHECK(test_shared_url_loader_factory_);
171
172 if (last_modified_time.is_null()) {
173 last_modified_time = kReasonableTime;
174 }
175
176 const WebRtcLogFileInfo log_file_info(browser_context_id, log_file_,
177 last_modified_time);
178
179 uploader_ = uploader_factory_->Create(log_file_info, ResultCallback());
180 }
181
ResultCallback()182 WebRtcEventLogUploader::UploadResultCallback ResultCallback() {
183 return base::BindOnce(&UploadObserver::OnWebRtcEventLogUploadComplete,
184 base::Unretained(&observer_));
185 }
186
187 content::BrowserTaskEnvironment task_environment_;
188
189 base::Time kReasonableTime;
190
191 network::TestURLLoaderFactory test_url_loader_factory_;
192 scoped_refptr<network::SharedURLLoaderFactory>
193 test_shared_url_loader_factory_;
194
195 base::RunLoop observer_run_loop_;
196
197 base::ScopedTempDir profiles_dir_;
198 std::unique_ptr<TestingProfileManager> testing_profile_manager_;
199 TestingProfile* testing_profile_; // |testing_profile_manager_| owns.
200 BrowserContextId browser_context_id_;
201
202 base::FilePath log_file_;
203
204 StrictMock<UploadObserver> observer_;
205
206 // These (uploader-factory and uploader) are the units under test.
207 std::unique_ptr<WebRtcEventLogUploaderImpl::Factory> uploader_factory_;
208 std::unique_ptr<WebRtcEventLogUploader> uploader_;
209 };
210
TEST_F(WebRtcEventLogUploaderImplTest,SuccessfulUploadReportedToObserver)211 TEST_F(WebRtcEventLogUploaderImplTest, SuccessfulUploadReportedToObserver) {
212 SetURLLoaderResponse(net::HTTP_OK, net::OK);
213 EXPECT_CALL(observer_, CompletionCallback(log_file_, true)).Times(1);
214 StartAndWaitForUpload();
215 EXPECT_FALSE(base::PathExists(log_file_));
216 }
217
218 // Version #1 - request reported as successful, but got an error (404) as the
219 // HTTP return code.
220 // Due to the simplicitly of both tests, this also tests the scenario
221 // FileDeletedAfterUnsuccessfulUpload, rather than giving each its own test.
TEST_F(WebRtcEventLogUploaderImplTest,UnsuccessfulUploadReportedToObserver1)222 TEST_F(WebRtcEventLogUploaderImplTest, UnsuccessfulUploadReportedToObserver1) {
223 SetURLLoaderResponse(net::HTTP_NOT_FOUND, net::OK);
224 EXPECT_CALL(observer_, CompletionCallback(log_file_, false)).Times(1);
225 StartAndWaitForUpload();
226 EXPECT_FALSE(base::PathExists(log_file_));
227 }
228
229 // Version #2 - request reported as failed; HTTP return code ignored, even
230 // if it's a purported success.
TEST_F(WebRtcEventLogUploaderImplTest,UnsuccessfulUploadReportedToObserver2)231 TEST_F(WebRtcEventLogUploaderImplTest, UnsuccessfulUploadReportedToObserver2) {
232 SetURLLoaderResponse(net::HTTP_NOT_FOUND, net::ERR_FAILED);
233 EXPECT_CALL(observer_, CompletionCallback(log_file_, false)).Times(1);
234 StartAndWaitForUpload();
235 EXPECT_FALSE(base::PathExists(log_file_));
236 }
237
238 #if defined(OS_POSIX)
TEST_F(WebRtcEventLogUploaderImplTest,FailureToReadFileReportedToObserver)239 TEST_F(WebRtcEventLogUploaderImplTest, FailureToReadFileReportedToObserver) {
240 // Show the failure was independent of the URLLoaderFactory's primed return
241 // value.
242 SetURLLoaderResponse(net::HTTP_OK, net::OK);
243
244 RemoveReadPermissions(log_file_);
245 EXPECT_CALL(observer_, CompletionCallback(log_file_, false)).Times(1);
246 StartAndWaitForUpload();
247 }
248
TEST_F(WebRtcEventLogUploaderImplTest,NonExistentFileReportedToObserver)249 TEST_F(WebRtcEventLogUploaderImplTest, NonExistentFileReportedToObserver) {
250 // Show the failure was independent of the URLLoaderFactory's primed return
251 // value.
252 SetURLLoaderResponse(net::HTTP_OK, net::OK);
253
254 log_file_ = log_file_.Append(FILE_PATH_LITERAL("garbage"));
255 EXPECT_CALL(observer_, CompletionCallback(log_file_, false)).Times(1);
256 StartAndWaitForUpload();
257 }
258 #endif // defined(OS_POSIX)
259
TEST_F(WebRtcEventLogUploaderImplTest,FilesUpToMaxSizeUploaded)260 TEST_F(WebRtcEventLogUploaderImplTest, FilesUpToMaxSizeUploaded) {
261 int64_t log_file_size_bytes;
262 ASSERT_TRUE(base::GetFileSize(log_file_, &log_file_size_bytes));
263
264 SetURLLoaderResponse(net::HTTP_OK, net::OK);
265 EXPECT_CALL(observer_, CompletionCallback(log_file_, true)).Times(1);
266 StartAndWaitForUploadWithCustomMaxSize(log_file_size_bytes);
267 EXPECT_FALSE(base::PathExists(log_file_));
268 }
269
TEST_F(WebRtcEventLogUploaderImplTest,ExcessivelyLargeFilesNotUploaded)270 TEST_F(WebRtcEventLogUploaderImplTest, ExcessivelyLargeFilesNotUploaded) {
271 int64_t log_file_size_bytes;
272 ASSERT_TRUE(base::GetFileSize(log_file_, &log_file_size_bytes));
273
274 SetURLLoaderResponse(net::HTTP_OK, net::OK);
275 EXPECT_CALL(observer_, CompletionCallback(log_file_, false)).Times(1);
276 StartAndWaitForUploadWithCustomMaxSize(log_file_size_bytes - 1);
277 EXPECT_FALSE(base::PathExists(log_file_));
278 }
279
TEST_F(WebRtcEventLogUploaderImplTest,CancelBeforeUploadCompletionCallsCallbackWithFalse)280 TEST_F(WebRtcEventLogUploaderImplTest,
281 CancelBeforeUploadCompletionCallsCallbackWithFalse) {
282 const base::Time last_modified = base::Time::Now();
283 StartUploadThatWillNotTerminate(browser_context_id_, last_modified);
284 EXPECT_CALL(observer_, CompletionCallback(log_file_, false)).Times(1);
285 uploader_->Cancel();
286 }
287
TEST_F(WebRtcEventLogUploaderImplTest,SecondCallToCancelHasNoEffect)288 TEST_F(WebRtcEventLogUploaderImplTest, SecondCallToCancelHasNoEffect) {
289 const base::Time last_modified = base::Time::Now();
290 StartUploadThatWillNotTerminate(browser_context_id_, last_modified);
291
292 EXPECT_CALL(observer_, CompletionCallback(log_file_, _)).Times(1);
293
294 uploader_->Cancel();
295 uploader_->Cancel();
296 }
297
TEST_F(WebRtcEventLogUploaderImplTest,CancelAfterUploadCompletionCallbackWasCalledHasNoEffect)298 TEST_F(WebRtcEventLogUploaderImplTest,
299 CancelAfterUploadCompletionCallbackWasCalledHasNoEffect) {
300 SetURLLoaderResponse(net::HTTP_OK, net::OK);
301 EXPECT_CALL(observer_, CompletionCallback(log_file_, true)).Times(1);
302 StartAndWaitForUpload();
303
304 EXPECT_CALL(observer_, CompletionCallback(_, _)).Times(0);
305 uploader_->Cancel();
306 }
307
TEST_F(WebRtcEventLogUploaderImplTest,CancelOnAbortedUploadHasNoEffect)308 TEST_F(WebRtcEventLogUploaderImplTest, CancelOnAbortedUploadHasNoEffect) {
309 // Show the failure was independent of the URLLoaderFactory's primed return
310 // value.
311 SetURLLoaderResponse(net::HTTP_OK, net::OK);
312
313 log_file_ = log_file_.Append(FILE_PATH_LITERAL("garbage"));
314 EXPECT_CALL(observer_, CompletionCallback(log_file_, false)).Times(1);
315 StartAndWaitForUpload();
316
317 EXPECT_CALL(observer_, CompletionCallback(_, _)).Times(0);
318 uploader_->Cancel();
319 }
320
TEST_F(WebRtcEventLogUploaderImplTest,CancelOnOngoingUploadDeletesFile)321 TEST_F(WebRtcEventLogUploaderImplTest, CancelOnOngoingUploadDeletesFile) {
322 const base::Time last_modified = base::Time::Now();
323 StartUploadThatWillNotTerminate(browser_context_id_, last_modified);
324
325 EXPECT_CALL(observer_, CompletionCallback(log_file_, false)).Times(1);
326 uploader_->Cancel();
327 observer_run_loop_.Run();
328
329 EXPECT_FALSE(base::PathExists(log_file_));
330 }
331
TEST_F(WebRtcEventLogUploaderImplTest,GetWebRtcLogFileInfoReturnsCorrectInfoBeforeUploadDone)332 TEST_F(WebRtcEventLogUploaderImplTest,
333 GetWebRtcLogFileInfoReturnsCorrectInfoBeforeUploadDone) {
334 const base::Time last_modified = base::Time::Now();
335 StartUploadThatWillNotTerminate(browser_context_id_, last_modified);
336
337 const WebRtcLogFileInfo info = uploader_->GetWebRtcLogFileInfo();
338 EXPECT_EQ(info.browser_context_id, browser_context_id_);
339 EXPECT_EQ(info.path, log_file_);
340 EXPECT_EQ(info.last_modified, last_modified);
341
342 // Test tear-down.
343 EXPECT_CALL(observer_, CompletionCallback(log_file_, false)).Times(1);
344 uploader_->Cancel();
345 }
346
TEST_F(WebRtcEventLogUploaderImplTest,GetWebRtcLogFileInfoReturnsCorrectInfoAfterUploadSucceeded)347 TEST_F(WebRtcEventLogUploaderImplTest,
348 GetWebRtcLogFileInfoReturnsCorrectInfoAfterUploadSucceeded) {
349 SetURLLoaderResponse(net::HTTP_OK, net::OK);
350 EXPECT_CALL(observer_, CompletionCallback(log_file_, true)).Times(1);
351
352 const base::Time last_modified = base::Time::Now();
353 StartAndWaitForUpload(browser_context_id_, last_modified);
354
355 const WebRtcLogFileInfo info = uploader_->GetWebRtcLogFileInfo();
356 EXPECT_EQ(info.browser_context_id, browser_context_id_);
357 EXPECT_EQ(info.path, log_file_);
358 EXPECT_EQ(info.last_modified, last_modified);
359 }
360
TEST_F(WebRtcEventLogUploaderImplTest,GetWebRtcLogFileInfoReturnsCorrectInfoWhenCalledOnCancelledUpload)361 TEST_F(WebRtcEventLogUploaderImplTest,
362 GetWebRtcLogFileInfoReturnsCorrectInfoWhenCalledOnCancelledUpload) {
363 const base::Time last_modified = base::Time::Now();
364 StartUploadThatWillNotTerminate(browser_context_id_, last_modified);
365 EXPECT_CALL(observer_, CompletionCallback(log_file_, false)).Times(1);
366 uploader_->Cancel();
367
368 const WebRtcLogFileInfo info = uploader_->GetWebRtcLogFileInfo();
369 EXPECT_EQ(info.browser_context_id, browser_context_id_);
370 EXPECT_EQ(info.path, log_file_);
371 EXPECT_EQ(info.last_modified, last_modified);
372 }
373
374 // TODO(crbug.com/775415): Add a unit test that shows that files with
375 // non-ASCII filenames are discard. (Or, alternatively, add support for them.)
376
377 } // namespace webrtc_event_logging
378