OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 // | 4 // |
5 // Unit tests for the SafeBrowsing storage system. | 5 // Unit tests for the SafeBrowsing storage system. |
6 | 6 |
7 #include "app/sql/connection.h" | 7 #include "app/sql/connection.h" |
8 #include "app/sql/statement.h" | 8 #include "app/sql/statement.h" |
9 #include "base/file_util.h" | 9 #include "base/file_util.h" |
10 #include "base/format_macros.h" | |
11 #include "base/logging.h" | 10 #include "base/logging.h" |
12 #include "base/message_loop.h" | 11 #include "base/message_loop.h" |
13 #include "base/metrics/stats_counters.h" | |
14 #include "base/path_service.h" | |
15 #include "base/process_util.h" | |
16 #include "base/scoped_temp_dir.h" | 12 #include "base/scoped_temp_dir.h" |
17 #include "base/sha2.h" | 13 #include "base/sha2.h" |
18 #include "base/string_util.h" | |
19 #include "base/time.h" | 14 #include "base/time.h" |
20 #include "chrome/browser/safe_browsing/protocol_parser.h" | |
21 #include "chrome/browser/safe_browsing/safe_browsing_database.h" | 15 #include "chrome/browser/safe_browsing/safe_browsing_database.h" |
22 #include "chrome/browser/safe_browsing/safe_browsing_store_file.h" | 16 #include "chrome/browser/safe_browsing/safe_browsing_store_file.h" |
23 #include "chrome/browser/safe_browsing/safe_browsing_store_sqlite.h" | 17 #include "chrome/browser/safe_browsing/safe_browsing_store_sqlite.h" |
24 #include "chrome/browser/safe_browsing/safe_browsing_store_unittest_helper.h" | 18 #include "chrome/browser/safe_browsing/safe_browsing_store_unittest_helper.h" |
25 #include "googleurl/src/gurl.h" | 19 #include "googleurl/src/gurl.h" |
26 #include "testing/gtest/include/gtest/gtest.h" | 20 #include "testing/gtest/include/gtest/gtest.h" |
27 #include "testing/platform_test.h" | 21 #include "testing/platform_test.h" |
28 | 22 |
29 using base::Time; | 23 using base::Time; |
30 | 24 |
(...skipping 1126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1157 EXPECT_FALSE(file_util::PathExists(database_filename_)); | 1151 EXPECT_FALSE(file_util::PathExists(database_filename_)); |
1158 | 1152 |
1159 // Run the update again successfully. | 1153 // Run the update again successfully. |
1160 EXPECT_TRUE(database_->UpdateStarted(&lists)); | 1154 EXPECT_TRUE(database_->UpdateStarted(&lists)); |
1161 database_->InsertChunks(safe_browsing_util::kMalwareList, chunks); | 1155 database_->InsertChunks(safe_browsing_util::kMalwareList, chunks); |
1162 database_->UpdateFinished(true); | 1156 database_->UpdateFinished(true); |
1163 EXPECT_TRUE(file_util::PathExists(database_filename_)); | 1157 EXPECT_TRUE(file_util::PathExists(database_filename_)); |
1164 | 1158 |
1165 database_.reset(); | 1159 database_.reset(); |
1166 } | 1160 } |
1167 | |
1168 namespace { | |
1169 | |
1170 void PrintStat(const char* name) { | |
1171 int value = base::StatsTable::current()->GetCounterValue(name); | |
1172 SB_DLOG(INFO) << StringPrintf("%s %d", name, value); | |
1173 } | |
1174 | |
1175 FilePath GetFullSBDataPath(const FilePath& path) { | |
1176 FilePath full_path; | |
1177 if (!PathService::Get(base::DIR_SOURCE_ROOT, &full_path)) { | |
1178 ADD_FAILURE() << "Unable to find test DIR_SOURCE_ROOT for test data."; | |
1179 return FilePath(); | |
1180 } | |
1181 full_path = full_path.AppendASCII("chrome"); | |
1182 full_path = full_path.AppendASCII("test"); | |
1183 full_path = full_path.AppendASCII("data"); | |
1184 full_path = full_path.AppendASCII("safe_browsing"); | |
1185 full_path = full_path.Append(path); | |
1186 return full_path; | |
1187 } | |
1188 | |
1189 // TODO(shess): The clients of this structure manually manage | |
1190 // |chunks|. Improve this code to apply the RAII idiom to manage | |
1191 // |chunks|. | |
1192 struct ChunksInfo { | |
1193 SBChunkList* chunks; // weak | |
1194 std::string listname; | |
1195 }; | |
1196 | |
1197 // TODO(shess): Move this into SafeBrowsingDatabaseTest. | |
1198 void PerformUpdate(SafeBrowsingDatabaseNew* database, | |
1199 const FilePath& database_filename, | |
1200 const FilePath& initial_db, | |
1201 const std::vector<ChunksInfo>& chunks, | |
1202 const std::vector<SBChunkDelete>& deletes) { | |
1203 base::IoCounters before, after; | |
1204 | |
1205 if (!initial_db.empty()) { | |
1206 FilePath full_initial_db = GetFullSBDataPath(initial_db); | |
1207 ASSERT_FALSE(full_initial_db.empty()); | |
1208 ASSERT_TRUE(file_util::PathExists(full_initial_db)); | |
1209 ASSERT_TRUE(file_util::CopyFile(full_initial_db, database_filename)); | |
1210 } | |
1211 | |
1212 Time before_time = Time::Now(); | |
1213 base::ProcessHandle handle = base::Process::Current().handle(); | |
1214 scoped_ptr<base::ProcessMetrics> metric( | |
1215 #if !defined(OS_MACOSX) | |
1216 base::ProcessMetrics::CreateProcessMetrics(handle)); | |
1217 #else | |
1218 // Getting stats only for the current process is enough, so NULL is fine. | |
1219 base::ProcessMetrics::CreateProcessMetrics(handle, NULL)); | |
1220 #endif | |
1221 // Get IO stats. These are currently not supported on Mac, and may not be | |
1222 // available for Linux, so we check the result and only show IO stats if | |
1223 // they are available. | |
1224 bool gotIOCounters = metric->GetIOCounters(&before); | |
1225 | |
1226 std::vector<SBListChunkRanges> lists; | |
1227 EXPECT_TRUE(database->UpdateStarted(&lists)); | |
1228 database->DeleteChunks(deletes); | |
1229 for (size_t i = 0; i < chunks.size(); ++i) | |
1230 database->InsertChunks(chunks[i].listname, *chunks[i].chunks); | |
1231 | |
1232 database->UpdateFinished(true); | |
1233 | |
1234 gotIOCounters = gotIOCounters && metric->GetIOCounters(&after); | |
1235 | |
1236 if (gotIOCounters) { | |
1237 SB_DLOG(INFO) << StringPrintf("I/O Read Bytes: %" PRIu64, | |
1238 after.ReadTransferCount - before.ReadTransferCount); | |
1239 SB_DLOG(INFO) << StringPrintf("I/O Write Bytes: %" PRIu64, | |
1240 after.WriteTransferCount - before.WriteTransferCount); | |
1241 SB_DLOG(INFO) << StringPrintf("I/O Reads: %" PRIu64, | |
1242 after.ReadOperationCount - before.ReadOperationCount); | |
1243 SB_DLOG(INFO) << StringPrintf("I/O Writes: %" PRIu64, | |
1244 after.WriteOperationCount - before.WriteOperationCount); | |
1245 } | |
1246 SB_DLOG(INFO) << StringPrintf("Finished in %" PRId64 " ms", | |
1247 (Time::Now() - before_time).InMilliseconds()); | |
1248 | |
1249 PrintStat("c:SB.HostSelect"); | |
1250 PrintStat("c:SB.HostSelectForBloomFilter"); | |
1251 PrintStat("c:SB.HostReplace"); | |
1252 PrintStat("c:SB.HostInsert"); | |
1253 PrintStat("c:SB.HostDelete"); | |
1254 PrintStat("c:SB.ChunkSelect"); | |
1255 PrintStat("c:SB.ChunkInsert"); | |
1256 PrintStat("c:SB.ChunkDelete"); | |
1257 PrintStat("c:SB.TransactionCommit"); | |
1258 } | |
1259 | |
1260 void UpdateDatabase(SafeBrowsingDatabaseNew* database, | |
1261 const FilePath& database_filename, | |
1262 const FilePath& initial_db, | |
1263 const FilePath& response_path, | |
1264 const FilePath& updates_path) { | |
1265 // First we read the chunks from disk, so that this isn't counted in IO bytes. | |
1266 std::vector<ChunksInfo> chunks; | |
1267 | |
1268 SafeBrowsingProtocolParser parser; | |
1269 if (!updates_path.empty()) { | |
1270 FilePath data_dir = GetFullSBDataPath(updates_path); | |
1271 ASSERT_FALSE(data_dir.empty()); | |
1272 ASSERT_TRUE(file_util::PathExists(data_dir)); | |
1273 file_util::FileEnumerator file_enum(data_dir, false, | |
1274 file_util::FileEnumerator::FILES); | |
1275 while (true) { | |
1276 FilePath file = file_enum.Next(); | |
1277 if (file.empty()) | |
1278 break; | |
1279 | |
1280 int64 size64; | |
1281 bool result = file_util::GetFileSize(file, &size64); | |
1282 ASSERT_TRUE(result); | |
1283 | |
1284 int size = static_cast<int>(size64); | |
1285 scoped_array<char> data(new char[size]); | |
1286 file_util::ReadFile(file, data.get(), size); | |
1287 | |
1288 ChunksInfo info; | |
1289 info.chunks = new SBChunkList; | |
1290 | |
1291 bool re_key; | |
1292 result = parser.ParseChunk(data.get(), size, "", "", | |
1293 &re_key, info.chunks); | |
1294 ASSERT_TRUE(result); | |
1295 | |
1296 info.listname = WideToASCII(file.BaseName().ToWStringHack()); | |
1297 size_t index = info.listname.find('_'); // Get rid fo the _s or _a. | |
1298 info.listname.resize(index); | |
1299 info.listname.erase(0, 3); // Get rid of the 000 etc. | |
1300 | |
1301 chunks.push_back(info); | |
1302 } | |
1303 } | |
1304 | |
1305 std::vector<SBChunkDelete> deletes; | |
1306 if (!response_path.empty()) { | |
1307 std::string update; | |
1308 FilePath full_response_path = GetFullSBDataPath(response_path); | |
1309 ASSERT_FALSE(full_response_path.empty()); | |
1310 ASSERT_TRUE(file_util::PathExists(full_response_path)); | |
1311 if (file_util::ReadFileToString(full_response_path, &update)) { | |
1312 int next_update; | |
1313 bool result, rekey, reset; | |
1314 std::vector<ChunkUrl> urls; | |
1315 result = parser.ParseUpdate(update.c_str(), | |
1316 static_cast<int>(update.length()), | |
1317 "", | |
1318 &next_update, | |
1319 &rekey, | |
1320 &reset, | |
1321 &deletes, | |
1322 &urls); | |
1323 ASSERT_TRUE(result); | |
1324 if (!updates_path.empty()) | |
1325 ASSERT_EQ(urls.size(), chunks.size()); | |
1326 } | |
1327 } | |
1328 | |
1329 PerformUpdate(database, database_filename, initial_db, chunks, deletes); | |
1330 | |
1331 // TODO(shess): Make ChunksInfo handle this via scoping. | |
1332 for (std::vector<ChunksInfo>::iterator iter = chunks.begin(); | |
1333 iter != chunks.end(); ++iter) { | |
1334 delete iter->chunks; | |
1335 iter->chunks = NULL; | |
1336 } | |
1337 } | |
1338 | |
1339 // Construct the shared base path used by the GetOld* functions. | |
1340 FilePath BasePath() { | |
1341 return FilePath(FILE_PATH_LITERAL("old")); | |
1342 } | |
1343 | |
1344 FilePath GetOldSafeBrowsingPath() { | |
1345 return BasePath().AppendASCII("SafeBrowsing"); | |
1346 } | |
1347 | |
1348 FilePath GetOldResponsePath() { | |
1349 return BasePath().AppendASCII("response"); | |
1350 } | |
1351 | |
1352 FilePath GetOldUpdatesPath() { | |
1353 return BasePath().AppendASCII("updates"); | |
1354 } | |
1355 | |
1356 } // namespace | |
1357 | |
1358 // Counts the IO needed for the initial update of a database. | |
1359 // test\data\safe_browsing\download_update.py was used to fetch the add/sub | |
1360 // chunks that are read, in order to get repeatable runs. | |
1361 TEST_F(SafeBrowsingDatabaseTest, DatabaseInitialIO) { | |
1362 UpdateDatabase(database_.get(), database_filename_, | |
1363 FilePath(), FilePath(), FilePath().AppendASCII("initial")); | |
1364 } | |
1365 | |
1366 // Counts the IO needed to update a month old database. | |
1367 // The data files were generated by running "..\download_update.py postdata" | |
1368 // in the "safe_browsing\old" directory. | |
1369 TEST_F(SafeBrowsingDatabaseTest, DatabaseOldIO) { | |
1370 UpdateDatabase(database_.get(), database_filename_, GetOldSafeBrowsingPath(), | |
1371 GetOldResponsePath(), GetOldUpdatesPath()); | |
1372 } | |
1373 | |
1374 // Like DatabaseOldIO but only the deletes. | |
1375 TEST_F(SafeBrowsingDatabaseTest, DatabaseOldDeletesIO) { | |
1376 UpdateDatabase(database_.get(), database_filename_, | |
1377 GetOldSafeBrowsingPath(), GetOldResponsePath(), FilePath()); | |
1378 } | |
1379 | |
1380 // Like DatabaseOldIO but only the updates. | |
1381 TEST_F(SafeBrowsingDatabaseTest, DatabaseOldUpdatesIO) { | |
1382 UpdateDatabase(database_.get(), database_filename_, | |
1383 GetOldSafeBrowsingPath(), FilePath(), GetOldUpdatesPath()); | |
1384 } | |
1385 | |
1386 // Does a a lot of addel's on very large chunks. | |
1387 TEST_F(SafeBrowsingDatabaseTest, DatabaseOldLotsofDeletesIO) { | |
1388 std::vector<ChunksInfo> chunks; | |
1389 std::vector<SBChunkDelete> deletes; | |
1390 SBChunkDelete del; | |
1391 del.is_sub_del = false; | |
1392 del.list_name = safe_browsing_util::kMalwareList; | |
1393 del.chunk_del.push_back(ChunkRange(3539, 3579)); | |
1394 deletes.push_back(del); | |
1395 PerformUpdate(database_.get(), database_filename_, | |
1396 GetOldSafeBrowsingPath(), chunks, deletes); | |
1397 } | |
OLD | NEW |