1 // Copyright 2013 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_log_uploader.h"
6 
7 #include <stddef.h>
8 
9 #include <string>
10 #include <utility>
11 
12 #include "base/bind.h"
13 #include "base/files/file.h"
14 #include "base/files/file_path.h"
15 #include "base/files/file_util.h"
16 #include "base/files/scoped_temp_dir.h"
17 #include "base/run_loop.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/string_split.h"
20 #include "base/task/post_task.h"
21 #include "base/test/task_environment.h"
22 #include "base/threading/sequenced_task_runner_handle.h"
23 #include "base/time/time.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25 
26 const char kTestTime[] = "time";
27 const char kTestReportId[] = "report-id";
28 const char kTestLocalId[] = "local-id";
29 
30 class WebRtcLogUploaderTest : public testing::Test {
31  public:
WebRtcLogUploaderTest()32   WebRtcLogUploaderTest() {}
33 
VerifyNumberOfLines(int expected_lines)34   bool VerifyNumberOfLines(int expected_lines) {
35     std::vector<std::string> lines = GetLinesFromListFile();
36     EXPECT_EQ(expected_lines, static_cast<int>(lines.size()));
37     return expected_lines == static_cast<int>(lines.size());
38   }
39 
VerifyLastLineHasAllInfo()40   bool VerifyLastLineHasAllInfo() {
41     std::string last_line = GetLastLineFromListFile();
42     if (last_line.empty())
43       return false;
44     std::vector<std::string> line_parts = base::SplitString(
45         last_line, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
46     EXPECT_EQ(4u, line_parts.size());
47     if (4u != line_parts.size())
48       return false;
49     // The times (indices 0 and 3) is the time when the info was written to the
50     // file which we don't know, so just verify that it's not empty.
51     EXPECT_FALSE(line_parts[0].empty());
52     EXPECT_STREQ(kTestReportId, line_parts[1].c_str());
53     EXPECT_STREQ(kTestLocalId, line_parts[2].c_str());
54     EXPECT_FALSE(line_parts[3].empty());
55     return true;
56   }
57 
58   // Verify that the last line contains the correct info for a local storage.
VerifyLastLineHasLocalStorageInfoOnly()59   bool VerifyLastLineHasLocalStorageInfoOnly() {
60     std::string last_line = GetLastLineFromListFile();
61     if (last_line.empty())
62       return false;
63     std::vector<std::string> line_parts = base::SplitString(
64         last_line, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
65     EXPECT_EQ(4u, line_parts.size());
66     if (4u != line_parts.size())
67       return false;
68     EXPECT_TRUE(line_parts[0].empty());
69     EXPECT_TRUE(line_parts[1].empty());
70     EXPECT_STREQ(kTestLocalId, line_parts[2].c_str());
71     EXPECT_FALSE(line_parts[3].empty());
72     return true;
73   }
74 
75   // Verify that the last line contains the correct info for an upload.
VerifyLastLineHasUploadInfoOnly()76   bool VerifyLastLineHasUploadInfoOnly() {
77     std::string last_line = GetLastLineFromListFile();
78     if (last_line.empty())
79       return false;
80     std::vector<std::string> line_parts = base::SplitString(
81         last_line, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
82     EXPECT_EQ(4u, line_parts.size());
83     if (4u != line_parts.size())
84       return false;
85     EXPECT_FALSE(line_parts[0].empty());
86     EXPECT_STREQ(kTestReportId, line_parts[1].c_str());
87     EXPECT_TRUE(line_parts[2].empty());
88     EXPECT_FALSE(line_parts[3].empty());
89     return true;
90   }
91 
AddLinesToTestFile(int number_of_lines)92   bool AddLinesToTestFile(int number_of_lines) {
93     base::File test_list_file(test_list_path_,
94                               base::File::FLAG_OPEN | base::File::FLAG_APPEND);
95     EXPECT_TRUE(test_list_file.IsValid());
96     if (!test_list_file.IsValid())
97       return false;
98 
99     for (int i = 0; i < number_of_lines; ++i) {
100       EXPECT_EQ(static_cast<int>(sizeof(kTestTime)) - 1,
101                 test_list_file.WriteAtCurrentPos(kTestTime,
102                                                  sizeof(kTestTime) - 1));
103       EXPECT_EQ(1, test_list_file.WriteAtCurrentPos(",", 1));
104       EXPECT_EQ(static_cast<int>(sizeof(kTestReportId)) - 1,
105                 test_list_file.WriteAtCurrentPos(kTestReportId,
106                                                  sizeof(kTestReportId) - 1));
107       EXPECT_EQ(1, test_list_file.WriteAtCurrentPos(",", 1));
108       EXPECT_EQ(static_cast<int>(sizeof(kTestLocalId)) - 1,
109                 test_list_file.WriteAtCurrentPos(kTestLocalId,
110                                                  sizeof(kTestLocalId) - 1));
111       EXPECT_EQ(1, test_list_file.WriteAtCurrentPos(",", 1));
112       EXPECT_EQ(static_cast<int>(sizeof(kTestTime)) - 1,
113                 test_list_file.WriteAtCurrentPos(kTestTime,
114                                                  sizeof(kTestTime) - 1));
115       EXPECT_EQ(1, test_list_file.WriteAtCurrentPos("\n", 1));
116     }
117     return true;
118   }
119 
GetLinesFromListFile()120   std::vector<std::string> GetLinesFromListFile() {
121     std::string contents;
122     int read = base::ReadFileToString(test_list_path_, &contents);
123     EXPECT_GT(read, 0);
124     if (read == 0)
125       return std::vector<std::string>();
126     // Since every line should end with '\n', the last line should be empty. So
127     // we expect at least two lines including the final empty. Remove the empty
128     // line before returning.
129     std::vector<std::string> lines = base::SplitString(
130         contents, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
131     EXPECT_GT(lines.size(), 1u);
132     if (lines.size() < 2)
133       return std::vector<std::string>();
134     EXPECT_TRUE(lines.back().empty());
135     if (!lines.back().empty())
136       return std::vector<std::string>();
137     lines.pop_back();
138     return lines;
139   }
140 
GetLastLineFromListFile()141   std::string GetLastLineFromListFile() {
142     std::vector<std::string> lines = GetLinesFromListFile();
143     EXPECT_GT(lines.size(), 0u);
144     if (lines.empty())
145       return std::string();
146     return lines[lines.size() - 1];
147   }
148 
VerifyRtpDumpInMultipart(const std::string & post_data,const std::string & dump_name,const std::string & dump_content)149   void VerifyRtpDumpInMultipart(const std::string& post_data,
150                                 const std::string& dump_name,
151                                 const std::string& dump_content) {
152     std::vector<std::string> lines = base::SplitStringUsingSubstr(
153         post_data, "\r\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
154 
155     std::string name_line = "Content-Disposition: form-data; name=\"";
156     name_line.append(dump_name);
157     name_line.append("\"");
158     name_line.append("; filename=\"");
159     name_line.append(dump_name);
160     name_line.append(".gz\"");
161 
162     size_t i = 0;
163     for (; i < lines.size(); ++i) {
164       if (lines[i] == name_line)
165         break;
166     }
167 
168     // The RTP dump takes 4 lines: content-disposition, content-type, empty
169     // line, dump content.
170     EXPECT_LT(i, lines.size() - 3);
171 
172     EXPECT_EQ("Content-Type: application/gzip", lines[i + 1]);
173     EXPECT_EQ("", lines[i + 2]);
174     EXPECT_EQ(dump_content, lines[i + 3]);
175   }
176 
AddLocallyStoredLogInfoToUploadListFile(WebRtcLogUploader * log_uploader,const base::FilePath & upload_list_path,const std::string & local_log_id)177   static void AddLocallyStoredLogInfoToUploadListFile(
178       WebRtcLogUploader* log_uploader,
179       const base::FilePath& upload_list_path,
180       const std::string& local_log_id) {
181     base::RunLoop run_loop;
182     log_uploader->background_task_runner()->PostTaskAndReply(
183         FROM_HERE,
184         base::BindOnce(
185             &WebRtcLogUploader::AddLocallyStoredLogInfoToUploadListFile,
186             base::Unretained(log_uploader), upload_list_path, local_log_id),
187         run_loop.QuitClosure());
188     run_loop.Run();
189   }
190 
FlushRunLoop()191   void FlushRunLoop() {
192     base::RunLoop run_loop;
193     base::SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE,
194                                                      run_loop.QuitClosure());
195     run_loop.Run();
196   }
197 
198   base::test::TaskEnvironment task_environment_;
199   base::FilePath test_list_path_;
200 };
201 
TEST_F(WebRtcLogUploaderTest,AddLocallyStoredLogInfoToUploadListFile)202 TEST_F(WebRtcLogUploaderTest, AddLocallyStoredLogInfoToUploadListFile) {
203   // Get a temporary filename. We don't want the file to exist to begin with
204   // since that's the normal use case, hence the delete.
205   ASSERT_TRUE(base::CreateTemporaryFile(&test_list_path_));
206   EXPECT_TRUE(base::DeleteFile(test_list_path_));
207   std::unique_ptr<WebRtcLogUploader> webrtc_log_uploader(
208       new WebRtcLogUploader());
209 
210   AddLocallyStoredLogInfoToUploadListFile(webrtc_log_uploader.get(),
211                                           test_list_path_, kTestLocalId);
212   AddLocallyStoredLogInfoToUploadListFile(webrtc_log_uploader.get(),
213                                           test_list_path_, kTestLocalId);
214   ASSERT_TRUE(VerifyNumberOfLines(2));
215   ASSERT_TRUE(VerifyLastLineHasLocalStorageInfoOnly());
216 
217   const int expected_line_limit = 50;
218   ASSERT_TRUE(AddLinesToTestFile(expected_line_limit - 2));
219   ASSERT_TRUE(VerifyNumberOfLines(expected_line_limit));
220   ASSERT_TRUE(VerifyLastLineHasAllInfo());
221 
222   AddLocallyStoredLogInfoToUploadListFile(webrtc_log_uploader.get(),
223                                           test_list_path_, kTestLocalId);
224   ASSERT_TRUE(VerifyNumberOfLines(expected_line_limit));
225   ASSERT_TRUE(VerifyLastLineHasLocalStorageInfoOnly());
226 
227   ASSERT_TRUE(AddLinesToTestFile(10));
228   ASSERT_TRUE(VerifyNumberOfLines(60));
229   ASSERT_TRUE(VerifyLastLineHasAllInfo());
230 
231   AddLocallyStoredLogInfoToUploadListFile(webrtc_log_uploader.get(),
232                                           test_list_path_, kTestLocalId);
233   ASSERT_TRUE(VerifyNumberOfLines(expected_line_limit));
234   ASSERT_TRUE(VerifyLastLineHasLocalStorageInfoOnly());
235 
236   webrtc_log_uploader->Shutdown();
237   FlushRunLoop();
238 }
239 
TEST_F(WebRtcLogUploaderTest,AddUploadedLogInfoToUploadListFile)240 TEST_F(WebRtcLogUploaderTest, AddUploadedLogInfoToUploadListFile) {
241   // Get a temporary filename. We don't want the file to exist to begin with
242   // since that's the normal use case, hence the delete.
243   ASSERT_TRUE(base::CreateTemporaryFile(&test_list_path_));
244   EXPECT_TRUE(base::DeleteFile(test_list_path_));
245   std::unique_ptr<WebRtcLogUploader> webrtc_log_uploader(
246       new WebRtcLogUploader());
247 
248   AddLocallyStoredLogInfoToUploadListFile(webrtc_log_uploader.get(),
249                                           test_list_path_, kTestLocalId);
250   ASSERT_TRUE(VerifyNumberOfLines(1));
251   ASSERT_TRUE(VerifyLastLineHasLocalStorageInfoOnly());
252 
253   webrtc_log_uploader->AddUploadedLogInfoToUploadListFile(
254       test_list_path_, kTestLocalId, kTestReportId);
255   ASSERT_TRUE(VerifyNumberOfLines(1));
256   ASSERT_TRUE(VerifyLastLineHasAllInfo());
257 
258   // Use a local ID that should not be found in the list.
259   webrtc_log_uploader->AddUploadedLogInfoToUploadListFile(
260       test_list_path_, "dummy id", kTestReportId);
261   ASSERT_TRUE(VerifyNumberOfLines(2));
262   ASSERT_TRUE(VerifyLastLineHasUploadInfoOnly());
263 
264   webrtc_log_uploader->Shutdown();
265   FlushRunLoop();
266 }
267 
TEST_F(WebRtcLogUploaderTest,AddRtpDumpsToPostedData)268 TEST_F(WebRtcLogUploaderTest, AddRtpDumpsToPostedData) {
269   base::ScopedTempDir temp_dir;
270   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
271 
272   std::unique_ptr<WebRtcLogUploader> webrtc_log_uploader(
273       new WebRtcLogUploader());
274 
275   std::string post_data;
276   webrtc_log_uploader->OverrideUploadWithBufferForTesting(&post_data);
277 
278   // Create the fake dump files.
279   const base::FilePath incoming_dump = temp_dir.GetPath().AppendASCII("recv");
280   const base::FilePath outgoing_dump = temp_dir.GetPath().AppendASCII("send");
281   const std::string incoming_dump_content = "dummy incoming";
282   const std::string outgoing_dump_content = "dummy outgoing";
283 
284   base::WriteFile(incoming_dump,
285                   &incoming_dump_content[0],
286                   incoming_dump_content.size());
287   base::WriteFile(outgoing_dump,
288                   &outgoing_dump_content[0],
289                   outgoing_dump_content.size());
290 
291   WebRtcLogUploader::UploadDoneData upload_done_data;
292   upload_done_data.paths.directory = temp_dir.GetPath().AppendASCII("log");
293 
294   upload_done_data.paths.incoming_rtp_dump = incoming_dump;
295   upload_done_data.paths.outgoing_rtp_dump = outgoing_dump;
296 
297   std::unique_ptr<WebRtcLogBuffer> log(new WebRtcLogBuffer());
298   log->SetComplete();
299 
300   base::RunLoop run_loop;
301   webrtc_log_uploader->background_task_runner()->PostTaskAndReply(
302       FROM_HERE,
303       base::BindOnce(&WebRtcLogUploader::LoggingStoppedDoUpload,
304                      base::Unretained(webrtc_log_uploader.get()),
305                      std::move(log), std::make_unique<WebRtcLogMetaDataMap>(),
306                      std::move(upload_done_data)),
307       run_loop.QuitClosure());
308   run_loop.Run();
309 
310   VerifyRtpDumpInMultipart(post_data, "rtpdump_recv", incoming_dump_content);
311   VerifyRtpDumpInMultipart(post_data, "rtpdump_send", outgoing_dump_content);
312 
313   webrtc_log_uploader->Shutdown();
314   FlushRunLoop();
315 }
316