| OLD | NEW |
| (Empty) |
| 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/nacl_host/pnacl_host.h" | |
| 6 | |
| 7 #include <stdio.h> | |
| 8 #include "base/bind.h" | |
| 9 #include "base/files/scoped_temp_dir.h" | |
| 10 #include "base/run_loop.h" | |
| 11 #include "base/threading/sequenced_worker_pool.h" | |
| 12 #include "components/nacl/browser/pnacl_translation_cache.h" | |
| 13 #include "content/public/browser/browser_thread.h" | |
| 14 #include "content/public/test/test_browser_thread_bundle.h" | |
| 15 #include "net/base/test_completion_callback.h" | |
| 16 #include "testing/gtest/include/gtest/gtest.h" | |
| 17 | |
| 18 #if defined(OS_WIN) | |
| 19 #define snprintf _snprintf | |
| 20 #endif | |
| 21 | |
| 22 namespace pnacl { | |
| 23 | |
| 24 class PnaclHostTest : public testing::Test { | |
| 25 protected: | |
| 26 PnaclHostTest() | |
| 27 : host_(NULL), | |
| 28 temp_callback_count_(0), | |
| 29 write_callback_count_(0), | |
| 30 thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {} | |
| 31 virtual void SetUp() { | |
| 32 host_ = new PnaclHost(); | |
| 33 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); | |
| 34 host_->InitForTest(temp_dir_.path()); | |
| 35 base::RunLoop().RunUntilIdle(); | |
| 36 EXPECT_EQ(PnaclHost::CacheReady, host_->cache_state_); | |
| 37 } | |
| 38 virtual void TearDown() { | |
| 39 EXPECT_EQ(0U, host_->pending_translations()); | |
| 40 // Give the host a chance to de-init the backend, and then delete it. | |
| 41 host_->RendererClosing(0); | |
| 42 FlushQueues(); | |
| 43 EXPECT_EQ(PnaclHost::CacheUninitialized, host_->cache_state_); | |
| 44 delete host_; | |
| 45 } | |
| 46 // Flush the blocking pool first, then any tasks it posted to the IO thread. | |
| 47 // Do 2 rounds of flushing, because some operations require 2 trips back and | |
| 48 // forth between the threads. | |
| 49 void FlushQueues() { | |
| 50 content::BrowserThread::GetBlockingPool()->FlushForTesting(); | |
| 51 base::RunLoop().RunUntilIdle(); | |
| 52 content::BrowserThread::GetBlockingPool()->FlushForTesting(); | |
| 53 base::RunLoop().RunUntilIdle(); | |
| 54 } | |
| 55 int GetCacheSize() { return host_->disk_cache_->Size(); } | |
| 56 int CacheIsInitialized() { | |
| 57 return host_->cache_state_ == PnaclHost::CacheReady; | |
| 58 } | |
| 59 void ReInitBackend() { | |
| 60 host_->InitForTest(temp_dir_.path()); | |
| 61 base::RunLoop().RunUntilIdle(); | |
| 62 EXPECT_EQ(PnaclHost::CacheReady, host_->cache_state_); | |
| 63 } | |
| 64 | |
| 65 public: // Required for derived classes to bind this method | |
| 66 // Callbacks used by tests which call GetNexeFd. | |
| 67 // CallbackExpectMiss checks that the fd is valid and a miss is reported, | |
| 68 // and also writes some data into the file, which is read back by | |
| 69 // CallbackExpectHit | |
| 70 void CallbackExpectMiss(base::PlatformFile fd, bool is_hit) { | |
| 71 EXPECT_FALSE(is_hit); | |
| 72 ASSERT_FALSE(fd == base::kInvalidPlatformFileValue); | |
| 73 base::PlatformFileInfo info; | |
| 74 EXPECT_TRUE(base::GetPlatformFileInfo(fd, &info)); | |
| 75 EXPECT_FALSE(info.is_directory); | |
| 76 EXPECT_EQ(0LL, info.size); | |
| 77 char str[16]; | |
| 78 memset(str, 0x0, 16); | |
| 79 snprintf(str, 16, "testdata%d", ++write_callback_count_); | |
| 80 EXPECT_EQ(16, base::WritePlatformFile(fd, 0, str, 16)); | |
| 81 temp_callback_count_++; | |
| 82 } | |
| 83 void CallbackExpectHit(base::PlatformFile fd, bool is_hit) { | |
| 84 EXPECT_TRUE(is_hit); | |
| 85 ASSERT_FALSE(fd == base::kInvalidPlatformFileValue); | |
| 86 base::PlatformFileInfo info; | |
| 87 EXPECT_TRUE(base::GetPlatformFileInfo(fd, &info)); | |
| 88 EXPECT_FALSE(info.is_directory); | |
| 89 EXPECT_EQ(16LL, info.size); | |
| 90 char data[16]; | |
| 91 memset(data, 0x0, 16); | |
| 92 char str[16]; | |
| 93 memset(str, 0x0, 16); | |
| 94 snprintf(str, 16, "testdata%d", write_callback_count_); | |
| 95 EXPECT_EQ(16, base::ReadPlatformFile(fd, 0, data, 16)); | |
| 96 EXPECT_STREQ(str, data); | |
| 97 temp_callback_count_++; | |
| 98 } | |
| 99 | |
| 100 protected: | |
| 101 PnaclHost* host_; | |
| 102 int temp_callback_count_; | |
| 103 int write_callback_count_; | |
| 104 content::TestBrowserThreadBundle thread_bundle_; | |
| 105 base::ScopedTempDir temp_dir_; | |
| 106 }; | |
| 107 | |
| 108 static nacl::PnaclCacheInfo GetTestCacheInfo() { | |
| 109 nacl::PnaclCacheInfo info; | |
| 110 info.pexe_url = GURL("http://www.google.com"); | |
| 111 info.abi_version = 0; | |
| 112 info.opt_level = 0; | |
| 113 info.has_no_store_header = false; | |
| 114 return info; | |
| 115 } | |
| 116 | |
| 117 #define GET_NEXE_FD(renderer, instance, incognito, info, expect_hit) \ | |
| 118 do { \ | |
| 119 SCOPED_TRACE(""); \ | |
| 120 host_->GetNexeFd( \ | |
| 121 renderer, \ | |
| 122 0, /* ignore render_view_id for now */ \ | |
| 123 instance, \ | |
| 124 incognito, \ | |
| 125 info, \ | |
| 126 base::Bind(expect_hit ? &PnaclHostTest::CallbackExpectHit \ | |
| 127 : &PnaclHostTest::CallbackExpectMiss, \ | |
| 128 base::Unretained(this))); \ | |
| 129 } while (0) | |
| 130 | |
| 131 TEST_F(PnaclHostTest, BasicMiss) { | |
| 132 nacl::PnaclCacheInfo info = GetTestCacheInfo(); | |
| 133 // Test cold miss. | |
| 134 GET_NEXE_FD(0, 0, false, info, false); | |
| 135 EXPECT_EQ(1U, host_->pending_translations()); | |
| 136 FlushQueues(); | |
| 137 EXPECT_EQ(1U, host_->pending_translations()); | |
| 138 EXPECT_EQ(1, temp_callback_count_); | |
| 139 host_->TranslationFinished(0, 0, true); | |
| 140 FlushQueues(); | |
| 141 EXPECT_EQ(0U, host_->pending_translations()); | |
| 142 // Test that a different cache info field also misses. | |
| 143 info.etag = std::string("something else"); | |
| 144 GET_NEXE_FD(0, 0, false, info, false); | |
| 145 FlushQueues(); | |
| 146 EXPECT_EQ(2, temp_callback_count_); | |
| 147 EXPECT_EQ(1U, host_->pending_translations()); | |
| 148 host_->RendererClosing(0); | |
| 149 FlushQueues(); | |
| 150 // Check that the cache has de-initialized after the last renderer goes away. | |
| 151 EXPECT_FALSE(CacheIsInitialized()); | |
| 152 } | |
| 153 | |
| 154 TEST_F(PnaclHostTest, BadArguments) { | |
| 155 nacl::PnaclCacheInfo info = GetTestCacheInfo(); | |
| 156 GET_NEXE_FD(0, 0, false, info, false); | |
| 157 EXPECT_EQ(1U, host_->pending_translations()); | |
| 158 host_->TranslationFinished(0, 1, true); // nonexistent translation | |
| 159 EXPECT_EQ(1U, host_->pending_translations()); | |
| 160 host_->RendererClosing(1); // nonexistent renderer | |
| 161 EXPECT_EQ(1U, host_->pending_translations()); | |
| 162 FlushQueues(); | |
| 163 EXPECT_EQ(1, temp_callback_count_); | |
| 164 host_->RendererClosing(0); // close without finishing | |
| 165 } | |
| 166 | |
| 167 TEST_F(PnaclHostTest, BasicHit) { | |
| 168 nacl::PnaclCacheInfo info = GetTestCacheInfo(); | |
| 169 GET_NEXE_FD(0, 0, false, info, false); | |
| 170 FlushQueues(); | |
| 171 EXPECT_EQ(1, temp_callback_count_); | |
| 172 host_->TranslationFinished(0, 0, true); | |
| 173 FlushQueues(); | |
| 174 GET_NEXE_FD(0, 1, false, info, true); | |
| 175 FlushQueues(); | |
| 176 EXPECT_EQ(2, temp_callback_count_); | |
| 177 EXPECT_EQ(0U, host_->pending_translations()); | |
| 178 } | |
| 179 | |
| 180 TEST_F(PnaclHostTest, TranslationErrors) { | |
| 181 nacl::PnaclCacheInfo info = GetTestCacheInfo(); | |
| 182 GET_NEXE_FD(0, 0, false, info, false); | |
| 183 // Early abort, before temp file request returns | |
| 184 host_->TranslationFinished(0, 0, false); | |
| 185 FlushQueues(); | |
| 186 EXPECT_EQ(0U, host_->pending_translations()); | |
| 187 EXPECT_EQ(0, temp_callback_count_); | |
| 188 // The backend will have been freed when the query comes back and there | |
| 189 // are no pending translations. | |
| 190 EXPECT_FALSE(CacheIsInitialized()); | |
| 191 ReInitBackend(); | |
| 192 // Check that another request for the same info misses successfully. | |
| 193 GET_NEXE_FD(0, 0, false, info, false); | |
| 194 FlushQueues(); | |
| 195 host_->TranslationFinished(0, 0, true); | |
| 196 FlushQueues(); | |
| 197 EXPECT_EQ(1, temp_callback_count_); | |
| 198 EXPECT_EQ(0U, host_->pending_translations()); | |
| 199 | |
| 200 // Now try sending the error after the temp file request returns | |
| 201 info.abi_version = 222; | |
| 202 GET_NEXE_FD(0, 0, false, info, false); | |
| 203 FlushQueues(); | |
| 204 EXPECT_EQ(2, temp_callback_count_); | |
| 205 host_->TranslationFinished(0, 0, false); | |
| 206 FlushQueues(); | |
| 207 EXPECT_EQ(0U, host_->pending_translations()); | |
| 208 // Check another successful miss | |
| 209 GET_NEXE_FD(0, 0, false, info, false); | |
| 210 FlushQueues(); | |
| 211 EXPECT_EQ(3, temp_callback_count_); | |
| 212 host_->TranslationFinished(0, 0, false); | |
| 213 EXPECT_EQ(0U, host_->pending_translations()); | |
| 214 } | |
| 215 | |
| 216 TEST_F(PnaclHostTest, OverlappedMissesAfterTempReturn) { | |
| 217 nacl::PnaclCacheInfo info = GetTestCacheInfo(); | |
| 218 GET_NEXE_FD(0, 0, false, info, false); | |
| 219 FlushQueues(); | |
| 220 EXPECT_EQ(1, temp_callback_count_); | |
| 221 EXPECT_EQ(1U, host_->pending_translations()); | |
| 222 // Test that a second request for the same nexe while the first one is still | |
| 223 // outstanding eventually hits. | |
| 224 GET_NEXE_FD(0, 1, false, info, true); | |
| 225 FlushQueues(); | |
| 226 EXPECT_EQ(2U, host_->pending_translations()); | |
| 227 // The temp file should not be returned to the second request until after the | |
| 228 // first is finished translating. | |
| 229 EXPECT_EQ(1, temp_callback_count_); | |
| 230 host_->TranslationFinished(0, 0, true); | |
| 231 FlushQueues(); | |
| 232 EXPECT_EQ(2, temp_callback_count_); | |
| 233 EXPECT_EQ(0U, host_->pending_translations()); | |
| 234 } | |
| 235 | |
| 236 TEST_F(PnaclHostTest, OverlappedMissesBeforeTempReturn) { | |
| 237 nacl::PnaclCacheInfo info = GetTestCacheInfo(); | |
| 238 GET_NEXE_FD(0, 0, false, info, false); | |
| 239 // Send the 2nd fd request before the first one returns a temp file. | |
| 240 GET_NEXE_FD(0, 1, false, info, true); | |
| 241 FlushQueues(); | |
| 242 EXPECT_EQ(1, temp_callback_count_); | |
| 243 EXPECT_EQ(2U, host_->pending_translations()); | |
| 244 FlushQueues(); | |
| 245 EXPECT_EQ(2U, host_->pending_translations()); | |
| 246 EXPECT_EQ(1, temp_callback_count_); | |
| 247 host_->TranslationFinished(0, 0, true); | |
| 248 FlushQueues(); | |
| 249 EXPECT_EQ(2, temp_callback_count_); | |
| 250 EXPECT_EQ(0U, host_->pending_translations()); | |
| 251 } | |
| 252 | |
| 253 TEST_F(PnaclHostTest, OverlappedHitsBeforeTempReturn) { | |
| 254 nacl::PnaclCacheInfo info = GetTestCacheInfo(); | |
| 255 // Store one in the cache and complete it. | |
| 256 GET_NEXE_FD(0, 0, false, info, false); | |
| 257 FlushQueues(); | |
| 258 EXPECT_EQ(1, temp_callback_count_); | |
| 259 host_->TranslationFinished(0, 0, true); | |
| 260 FlushQueues(); | |
| 261 EXPECT_EQ(0U, host_->pending_translations()); | |
| 262 GET_NEXE_FD(0, 0, false, info, true); | |
| 263 // Request the second before the first temp file returns. | |
| 264 GET_NEXE_FD(0, 1, false, info, true); | |
| 265 FlushQueues(); | |
| 266 EXPECT_EQ(3, temp_callback_count_); | |
| 267 EXPECT_EQ(0U, host_->pending_translations()); | |
| 268 } | |
| 269 | |
| 270 TEST_F(PnaclHostTest, OverlappedHitsAfterTempReturn) { | |
| 271 nacl::PnaclCacheInfo info = GetTestCacheInfo(); | |
| 272 // Store one in the cache and complete it. | |
| 273 GET_NEXE_FD(0, 0, false, info, false); | |
| 274 FlushQueues(); | |
| 275 EXPECT_EQ(1, temp_callback_count_); | |
| 276 host_->TranslationFinished(0, 0, true); | |
| 277 FlushQueues(); | |
| 278 EXPECT_EQ(0U, host_->pending_translations()); | |
| 279 GET_NEXE_FD(0, 0, false, info, true); | |
| 280 FlushQueues(); | |
| 281 GET_NEXE_FD(0, 1, false, info, true); | |
| 282 FlushQueues(); | |
| 283 EXPECT_EQ(3, temp_callback_count_); | |
| 284 EXPECT_EQ(0U, host_->pending_translations()); | |
| 285 } | |
| 286 | |
| 287 TEST_F(PnaclHostTest, OverlappedMissesRendererClosing) { | |
| 288 nacl::PnaclCacheInfo info = GetTestCacheInfo(); | |
| 289 GET_NEXE_FD(0, 0, false, info, false); | |
| 290 // Send the 2nd fd request from a different renderer. | |
| 291 // Test that it eventually gets an fd after the first renderer closes. | |
| 292 GET_NEXE_FD(1, 1, false, info, false); | |
| 293 FlushQueues(); | |
| 294 EXPECT_EQ(1, temp_callback_count_); | |
| 295 EXPECT_EQ(2U, host_->pending_translations()); | |
| 296 FlushQueues(); | |
| 297 EXPECT_EQ(2U, host_->pending_translations()); | |
| 298 EXPECT_EQ(1, temp_callback_count_); | |
| 299 host_->RendererClosing(0); | |
| 300 FlushQueues(); | |
| 301 EXPECT_EQ(2, temp_callback_count_); | |
| 302 EXPECT_EQ(1U, host_->pending_translations()); | |
| 303 host_->RendererClosing(1); | |
| 304 } | |
| 305 | |
| 306 TEST_F(PnaclHostTest, Incognito) { | |
| 307 nacl::PnaclCacheInfo info = GetTestCacheInfo(); | |
| 308 GET_NEXE_FD(0, 0, true, info, false); | |
| 309 FlushQueues(); | |
| 310 EXPECT_EQ(1, temp_callback_count_); | |
| 311 host_->TranslationFinished(0, 0, true); | |
| 312 FlushQueues(); | |
| 313 // Check that an incognito translation is not stored in the cache | |
| 314 GET_NEXE_FD(0, 0, false, info, false); | |
| 315 FlushQueues(); | |
| 316 EXPECT_EQ(2, temp_callback_count_); | |
| 317 host_->TranslationFinished(0, 0, true); | |
| 318 FlushQueues(); | |
| 319 // Check that an incognito translation can hit from a normal one. | |
| 320 GET_NEXE_FD(0, 0, true, info, true); | |
| 321 FlushQueues(); | |
| 322 EXPECT_EQ(3, temp_callback_count_); | |
| 323 } | |
| 324 | |
| 325 TEST_F(PnaclHostTest, IncognitoOverlappedMiss) { | |
| 326 nacl::PnaclCacheInfo info = GetTestCacheInfo(); | |
| 327 GET_NEXE_FD(0, 0, true, info, false); | |
| 328 GET_NEXE_FD(0, 1, false, info, false); | |
| 329 FlushQueues(); | |
| 330 // Check that both translations have returned misses, (i.e. that the | |
| 331 // second one has not blocked on the incognito one) | |
| 332 EXPECT_EQ(2, temp_callback_count_); | |
| 333 host_->TranslationFinished(0, 0, true); | |
| 334 host_->TranslationFinished(0, 1, true); | |
| 335 FlushQueues(); | |
| 336 EXPECT_EQ(0U, host_->pending_translations()); | |
| 337 | |
| 338 // Same test, but issue the 2nd request after the first has returned a miss. | |
| 339 info.abi_version = 222; | |
| 340 GET_NEXE_FD(0, 0, true, info, false); | |
| 341 FlushQueues(); | |
| 342 EXPECT_EQ(3, temp_callback_count_); | |
| 343 GET_NEXE_FD(0, 1, false, info, false); | |
| 344 FlushQueues(); | |
| 345 EXPECT_EQ(4, temp_callback_count_); | |
| 346 host_->RendererClosing(0); | |
| 347 } | |
| 348 | |
| 349 TEST_F(PnaclHostTest, IncognitoSecondOverlappedMiss) { | |
| 350 // If the non-incognito request comes first, it should | |
| 351 // behave exactly like OverlappedMissBeforeTempReturn | |
| 352 nacl::PnaclCacheInfo info = GetTestCacheInfo(); | |
| 353 GET_NEXE_FD(0, 0, false, info, false); | |
| 354 // Send the 2nd fd request before the first one returns a temp file. | |
| 355 GET_NEXE_FD(0, 1, true, info, true); | |
| 356 FlushQueues(); | |
| 357 EXPECT_EQ(1, temp_callback_count_); | |
| 358 EXPECT_EQ(2U, host_->pending_translations()); | |
| 359 FlushQueues(); | |
| 360 EXPECT_EQ(2U, host_->pending_translations()); | |
| 361 EXPECT_EQ(1, temp_callback_count_); | |
| 362 host_->TranslationFinished(0, 0, true); | |
| 363 FlushQueues(); | |
| 364 EXPECT_EQ(2, temp_callback_count_); | |
| 365 EXPECT_EQ(0U, host_->pending_translations()); | |
| 366 } | |
| 367 | |
| 368 // Test that pexes with the no-store header do not get cached. | |
| 369 TEST_F(PnaclHostTest, CacheControlNoStore) { | |
| 370 nacl::PnaclCacheInfo info = GetTestCacheInfo(); | |
| 371 info.has_no_store_header = true; | |
| 372 GET_NEXE_FD(0, 0, false, info, false); | |
| 373 FlushQueues(); | |
| 374 EXPECT_EQ(1, temp_callback_count_); | |
| 375 host_->TranslationFinished(0, 0, true); | |
| 376 FlushQueues(); | |
| 377 EXPECT_EQ(0U, host_->pending_translations()); | |
| 378 EXPECT_EQ(0, GetCacheSize()); | |
| 379 } | |
| 380 | |
| 381 // Test that no-store pexes do not wait, but do duplicate translations | |
| 382 TEST_F(PnaclHostTest, NoStoreOverlappedMiss) { | |
| 383 nacl::PnaclCacheInfo info = GetTestCacheInfo(); | |
| 384 info.has_no_store_header = true; | |
| 385 GET_NEXE_FD(0, 0, false, info, false); | |
| 386 GET_NEXE_FD(0, 1, false, info, false); | |
| 387 FlushQueues(); | |
| 388 // Check that both translations have returned misses, (i.e. that the | |
| 389 // second one has not blocked on the first one) | |
| 390 EXPECT_EQ(2, temp_callback_count_); | |
| 391 host_->TranslationFinished(0, 0, true); | |
| 392 host_->TranslationFinished(0, 1, true); | |
| 393 FlushQueues(); | |
| 394 EXPECT_EQ(0U, host_->pending_translations()); | |
| 395 | |
| 396 // Same test, but issue the 2nd request after the first has returned a miss. | |
| 397 info.abi_version = 222; | |
| 398 GET_NEXE_FD(0, 0, false, info, false); | |
| 399 FlushQueues(); | |
| 400 EXPECT_EQ(3, temp_callback_count_); | |
| 401 GET_NEXE_FD(0, 1, false, info, false); | |
| 402 FlushQueues(); | |
| 403 EXPECT_EQ(4, temp_callback_count_); | |
| 404 host_->RendererClosing(0); | |
| 405 } | |
| 406 | |
| 407 TEST_F(PnaclHostTest, ClearTranslationCache) { | |
| 408 nacl::PnaclCacheInfo info = GetTestCacheInfo(); | |
| 409 // Add 2 entries in the cache | |
| 410 GET_NEXE_FD(0, 0, false, info, false); | |
| 411 info.abi_version = 222; | |
| 412 GET_NEXE_FD(0, 1, false, info, false); | |
| 413 FlushQueues(); | |
| 414 EXPECT_EQ(2, temp_callback_count_); | |
| 415 host_->TranslationFinished(0, 0, true); | |
| 416 host_->TranslationFinished(0, 1, true); | |
| 417 FlushQueues(); | |
| 418 EXPECT_EQ(0U, host_->pending_translations()); | |
| 419 EXPECT_EQ(2, GetCacheSize()); | |
| 420 net::TestCompletionCallback cb; | |
| 421 // Since we are using a memory backend, the clear should happen immediately. | |
| 422 host_->ClearTranslationCacheEntriesBetween( | |
| 423 base::Time(), base::Time(), base::Bind(cb.callback(), 0)); | |
| 424 // Check that the translation cache has been cleared before flushing the | |
| 425 // queues, because the backend will be freed once it is. | |
| 426 EXPECT_EQ(0, GetCacheSize()); | |
| 427 EXPECT_EQ(0, cb.GetResult(net::ERR_IO_PENDING)); | |
| 428 // Now check that the backend has been freed. | |
| 429 EXPECT_FALSE(CacheIsInitialized()); | |
| 430 } | |
| 431 | |
| 432 } // namespace pnacl | |
| OLD | NEW |