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