| 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 |