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