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