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 "components/nacl/browser/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(), true); 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(), true); 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(const base::File& file, bool is_hit) { 71 EXPECT_FALSE(is_hit); 72 ASSERT_TRUE(file.IsValid()); 73 base::File::Info info; 74 base::File* mutable_file = const_cast<base::File*>(&file); 75 EXPECT_TRUE(mutable_file->GetInfo(&info)); 76 EXPECT_FALSE(info.is_directory); 77 EXPECT_EQ(0LL, info.size); 78 char str[16]; 79 memset(str, 0x0, 16); 80 snprintf(str, 16, "testdata%d", ++write_callback_count_); 81 EXPECT_EQ(16, mutable_file->Write(0, str, 16)); 82 temp_callback_count_++; 83 } 84 void CallbackExpectHit(const base::File& file, bool is_hit) { 85 EXPECT_TRUE(is_hit); 86 ASSERT_TRUE(file.IsValid()); 87 base::File::Info info; 88 base::File* mutable_file = const_cast<base::File*>(&file); 89 EXPECT_TRUE(mutable_file->GetInfo(&info)); 90 EXPECT_FALSE(info.is_directory); 91 EXPECT_EQ(16LL, info.size); 92 char data[16]; 93 memset(data, 0x0, 16); 94 char str[16]; 95 memset(str, 0x0, 16); 96 snprintf(str, 16, "testdata%d", write_callback_count_); 97 EXPECT_EQ(16, mutable_file->Read(0, data, 16)); 98 EXPECT_STREQ(str, data); 99 temp_callback_count_++; 100 } 101 102 protected: 103 PnaclHost* host_; 104 int temp_callback_count_; 105 int write_callback_count_; 106 content::TestBrowserThreadBundle thread_bundle_; 107 base::ScopedTempDir temp_dir_; 108 }; 109 110 static nacl::PnaclCacheInfo GetTestCacheInfo() { 111 nacl::PnaclCacheInfo info; 112 info.pexe_url = GURL("http://www.google.com"); 113 info.abi_version = 0; 114 info.opt_level = 0; 115 info.has_no_store_header = false; 116 return info; 117 } 118 119 #define GET_NEXE_FD(renderer, instance, incognito, info, expect_hit) \ 120 do { \ 121 SCOPED_TRACE(""); \ 122 host_->GetNexeFd( \ 123 renderer, \ 124 0, /* ignore render_view_id for now */ \ 125 instance, \ 126 incognito, \ 127 info, \ 128 base::Bind(expect_hit ? &PnaclHostTest::CallbackExpectHit \ 129 : &PnaclHostTest::CallbackExpectMiss, \ 130 base::Unretained(this))); \ 131 } while (0) 132 133 TEST_F(PnaclHostTest, BasicMiss) { 134 nacl::PnaclCacheInfo info = GetTestCacheInfo(); 135 // Test cold miss. 136 GET_NEXE_FD(0, 0, false, info, false); 137 EXPECT_EQ(1U, host_->pending_translations()); 138 FlushQueues(); 139 EXPECT_EQ(1U, host_->pending_translations()); 140 EXPECT_EQ(1, temp_callback_count_); 141 host_->TranslationFinished(0, 0, true); 142 FlushQueues(); 143 EXPECT_EQ(0U, host_->pending_translations()); 144 // Test that a different cache info field also misses. 145 info.etag = std::string("something else"); 146 GET_NEXE_FD(0, 0, false, info, false); 147 FlushQueues(); 148 EXPECT_EQ(2, temp_callback_count_); 149 EXPECT_EQ(1U, host_->pending_translations()); 150 host_->RendererClosing(0); 151 FlushQueues(); 152 // Check that the cache has de-initialized after the last renderer goes away. 153 EXPECT_FALSE(CacheIsInitialized()); 154 } 155 156 TEST_F(PnaclHostTest, BadArguments) { 157 nacl::PnaclCacheInfo info = GetTestCacheInfo(); 158 GET_NEXE_FD(0, 0, false, info, false); 159 EXPECT_EQ(1U, host_->pending_translations()); 160 host_->TranslationFinished(0, 1, true); // nonexistent translation 161 EXPECT_EQ(1U, host_->pending_translations()); 162 host_->RendererClosing(1); // nonexistent renderer 163 EXPECT_EQ(1U, host_->pending_translations()); 164 FlushQueues(); 165 EXPECT_EQ(1, temp_callback_count_); 166 host_->RendererClosing(0); // close without finishing 167 } 168 169 TEST_F(PnaclHostTest, BasicHit) { 170 nacl::PnaclCacheInfo info = GetTestCacheInfo(); 171 GET_NEXE_FD(0, 0, false, info, false); 172 FlushQueues(); 173 EXPECT_EQ(1, temp_callback_count_); 174 host_->TranslationFinished(0, 0, true); 175 FlushQueues(); 176 GET_NEXE_FD(0, 1, false, info, true); 177 FlushQueues(); 178 EXPECT_EQ(2, temp_callback_count_); 179 EXPECT_EQ(0U, host_->pending_translations()); 180 } 181 182 TEST_F(PnaclHostTest, TranslationErrors) { 183 nacl::PnaclCacheInfo info = GetTestCacheInfo(); 184 GET_NEXE_FD(0, 0, false, info, false); 185 // Early abort, before temp file request returns 186 host_->TranslationFinished(0, 0, false); 187 FlushQueues(); 188 EXPECT_EQ(0U, host_->pending_translations()); 189 EXPECT_EQ(0, temp_callback_count_); 190 // The backend will have been freed when the query comes back and there 191 // are no pending translations. 192 EXPECT_FALSE(CacheIsInitialized()); 193 ReInitBackend(); 194 // Check that another request for the same info misses successfully. 195 GET_NEXE_FD(0, 0, false, info, false); 196 FlushQueues(); 197 host_->TranslationFinished(0, 0, true); 198 FlushQueues(); 199 EXPECT_EQ(1, temp_callback_count_); 200 EXPECT_EQ(0U, host_->pending_translations()); 201 202 // Now try sending the error after the temp file request returns 203 info.abi_version = 222; 204 GET_NEXE_FD(0, 0, false, info, false); 205 FlushQueues(); 206 EXPECT_EQ(2, temp_callback_count_); 207 host_->TranslationFinished(0, 0, false); 208 FlushQueues(); 209 EXPECT_EQ(0U, host_->pending_translations()); 210 // Check another successful miss 211 GET_NEXE_FD(0, 0, false, info, false); 212 FlushQueues(); 213 EXPECT_EQ(3, temp_callback_count_); 214 host_->TranslationFinished(0, 0, false); 215 EXPECT_EQ(0U, host_->pending_translations()); 216 } 217 218 TEST_F(PnaclHostTest, OverlappedMissesAfterTempReturn) { 219 nacl::PnaclCacheInfo info = GetTestCacheInfo(); 220 GET_NEXE_FD(0, 0, false, info, false); 221 FlushQueues(); 222 EXPECT_EQ(1, temp_callback_count_); 223 EXPECT_EQ(1U, host_->pending_translations()); 224 // Test that a second request for the same nexe while the first one is still 225 // outstanding eventually hits. 226 GET_NEXE_FD(0, 1, false, info, true); 227 FlushQueues(); 228 EXPECT_EQ(2U, host_->pending_translations()); 229 // The temp file should not be returned to the second request until after the 230 // first is finished translating. 231 EXPECT_EQ(1, temp_callback_count_); 232 host_->TranslationFinished(0, 0, true); 233 FlushQueues(); 234 EXPECT_EQ(2, temp_callback_count_); 235 EXPECT_EQ(0U, host_->pending_translations()); 236 } 237 238 TEST_F(PnaclHostTest, OverlappedMissesBeforeTempReturn) { 239 nacl::PnaclCacheInfo info = GetTestCacheInfo(); 240 GET_NEXE_FD(0, 0, false, info, false); 241 // Send the 2nd fd request before the first one returns a temp file. 242 GET_NEXE_FD(0, 1, false, info, true); 243 FlushQueues(); 244 EXPECT_EQ(1, temp_callback_count_); 245 EXPECT_EQ(2U, host_->pending_translations()); 246 FlushQueues(); 247 EXPECT_EQ(2U, host_->pending_translations()); 248 EXPECT_EQ(1, temp_callback_count_); 249 host_->TranslationFinished(0, 0, true); 250 FlushQueues(); 251 EXPECT_EQ(2, temp_callback_count_); 252 EXPECT_EQ(0U, host_->pending_translations()); 253 } 254 255 TEST_F(PnaclHostTest, OverlappedHitsBeforeTempReturn) { 256 nacl::PnaclCacheInfo info = GetTestCacheInfo(); 257 // Store one in the cache and complete it. 258 GET_NEXE_FD(0, 0, false, info, false); 259 FlushQueues(); 260 EXPECT_EQ(1, temp_callback_count_); 261 host_->TranslationFinished(0, 0, true); 262 FlushQueues(); 263 EXPECT_EQ(0U, host_->pending_translations()); 264 GET_NEXE_FD(0, 0, false, info, true); 265 // Request the second before the first temp file returns. 266 GET_NEXE_FD(0, 1, false, info, true); 267 FlushQueues(); 268 EXPECT_EQ(3, temp_callback_count_); 269 EXPECT_EQ(0U, host_->pending_translations()); 270 } 271 272 TEST_F(PnaclHostTest, OverlappedHitsAfterTempReturn) { 273 nacl::PnaclCacheInfo info = GetTestCacheInfo(); 274 // Store one in the cache and complete it. 275 GET_NEXE_FD(0, 0, false, info, false); 276 FlushQueues(); 277 EXPECT_EQ(1, temp_callback_count_); 278 host_->TranslationFinished(0, 0, true); 279 FlushQueues(); 280 EXPECT_EQ(0U, host_->pending_translations()); 281 GET_NEXE_FD(0, 0, false, info, true); 282 FlushQueues(); 283 GET_NEXE_FD(0, 1, false, info, true); 284 FlushQueues(); 285 EXPECT_EQ(3, temp_callback_count_); 286 EXPECT_EQ(0U, host_->pending_translations()); 287 } 288 289 TEST_F(PnaclHostTest, OverlappedMissesRendererClosing) { 290 nacl::PnaclCacheInfo info = GetTestCacheInfo(); 291 GET_NEXE_FD(0, 0, false, info, false); 292 // Send the 2nd fd request from a different renderer. 293 // Test that it eventually gets an fd after the first renderer closes. 294 GET_NEXE_FD(1, 1, false, info, false); 295 FlushQueues(); 296 EXPECT_EQ(1, temp_callback_count_); 297 EXPECT_EQ(2U, host_->pending_translations()); 298 FlushQueues(); 299 EXPECT_EQ(2U, host_->pending_translations()); 300 EXPECT_EQ(1, temp_callback_count_); 301 host_->RendererClosing(0); 302 FlushQueues(); 303 EXPECT_EQ(2, temp_callback_count_); 304 EXPECT_EQ(1U, host_->pending_translations()); 305 host_->RendererClosing(1); 306 } 307 308 TEST_F(PnaclHostTest, Incognito) { 309 nacl::PnaclCacheInfo info = GetTestCacheInfo(); 310 GET_NEXE_FD(0, 0, true, info, false); 311 FlushQueues(); 312 EXPECT_EQ(1, temp_callback_count_); 313 host_->TranslationFinished(0, 0, true); 314 FlushQueues(); 315 // Check that an incognito translation is not stored in the cache 316 GET_NEXE_FD(0, 0, false, info, false); 317 FlushQueues(); 318 EXPECT_EQ(2, temp_callback_count_); 319 host_->TranslationFinished(0, 0, true); 320 FlushQueues(); 321 // Check that an incognito translation can hit from a normal one. 322 GET_NEXE_FD(0, 0, true, info, true); 323 FlushQueues(); 324 EXPECT_EQ(3, temp_callback_count_); 325 } 326 327 TEST_F(PnaclHostTest, IncognitoOverlappedMiss) { 328 nacl::PnaclCacheInfo info = GetTestCacheInfo(); 329 GET_NEXE_FD(0, 0, true, info, false); 330 GET_NEXE_FD(0, 1, false, info, false); 331 FlushQueues(); 332 // Check that both translations have returned misses, (i.e. that the 333 // second one has not blocked on the incognito one) 334 EXPECT_EQ(2, temp_callback_count_); 335 host_->TranslationFinished(0, 0, true); 336 host_->TranslationFinished(0, 1, true); 337 FlushQueues(); 338 EXPECT_EQ(0U, host_->pending_translations()); 339 340 // Same test, but issue the 2nd request after the first has returned a miss. 341 info.abi_version = 222; 342 GET_NEXE_FD(0, 0, true, info, false); 343 FlushQueues(); 344 EXPECT_EQ(3, temp_callback_count_); 345 GET_NEXE_FD(0, 1, false, info, false); 346 FlushQueues(); 347 EXPECT_EQ(4, temp_callback_count_); 348 host_->RendererClosing(0); 349 } 350 351 TEST_F(PnaclHostTest, IncognitoSecondOverlappedMiss) { 352 // If the non-incognito request comes first, it should 353 // behave exactly like OverlappedMissBeforeTempReturn 354 nacl::PnaclCacheInfo info = GetTestCacheInfo(); 355 GET_NEXE_FD(0, 0, false, info, false); 356 // Send the 2nd fd request before the first one returns a temp file. 357 GET_NEXE_FD(0, 1, true, info, true); 358 FlushQueues(); 359 EXPECT_EQ(1, temp_callback_count_); 360 EXPECT_EQ(2U, host_->pending_translations()); 361 FlushQueues(); 362 EXPECT_EQ(2U, host_->pending_translations()); 363 EXPECT_EQ(1, temp_callback_count_); 364 host_->TranslationFinished(0, 0, true); 365 FlushQueues(); 366 EXPECT_EQ(2, temp_callback_count_); 367 EXPECT_EQ(0U, host_->pending_translations()); 368 } 369 370 // Test that pexes with the no-store header do not get cached. 371 TEST_F(PnaclHostTest, CacheControlNoStore) { 372 nacl::PnaclCacheInfo info = GetTestCacheInfo(); 373 info.has_no_store_header = true; 374 GET_NEXE_FD(0, 0, false, info, false); 375 FlushQueues(); 376 EXPECT_EQ(1, temp_callback_count_); 377 host_->TranslationFinished(0, 0, true); 378 FlushQueues(); 379 EXPECT_EQ(0U, host_->pending_translations()); 380 EXPECT_EQ(0, GetCacheSize()); 381 } 382 383 // Test that no-store pexes do not wait, but do duplicate translations 384 TEST_F(PnaclHostTest, NoStoreOverlappedMiss) { 385 nacl::PnaclCacheInfo info = GetTestCacheInfo(); 386 info.has_no_store_header = true; 387 GET_NEXE_FD(0, 0, false, info, false); 388 GET_NEXE_FD(0, 1, false, info, false); 389 FlushQueues(); 390 // Check that both translations have returned misses, (i.e. that the 391 // second one has not blocked on the first one) 392 EXPECT_EQ(2, temp_callback_count_); 393 host_->TranslationFinished(0, 0, true); 394 host_->TranslationFinished(0, 1, true); 395 FlushQueues(); 396 EXPECT_EQ(0U, host_->pending_translations()); 397 398 // Same test, but issue the 2nd request after the first has returned a miss. 399 info.abi_version = 222; 400 GET_NEXE_FD(0, 0, false, info, false); 401 FlushQueues(); 402 EXPECT_EQ(3, temp_callback_count_); 403 GET_NEXE_FD(0, 1, false, info, false); 404 FlushQueues(); 405 EXPECT_EQ(4, temp_callback_count_); 406 host_->RendererClosing(0); 407 } 408 409 TEST_F(PnaclHostTest, ClearTranslationCache) { 410 nacl::PnaclCacheInfo info = GetTestCacheInfo(); 411 // Add 2 entries in the cache 412 GET_NEXE_FD(0, 0, false, info, false); 413 info.abi_version = 222; 414 GET_NEXE_FD(0, 1, false, info, false); 415 FlushQueues(); 416 EXPECT_EQ(2, temp_callback_count_); 417 host_->TranslationFinished(0, 0, true); 418 host_->TranslationFinished(0, 1, true); 419 FlushQueues(); 420 EXPECT_EQ(0U, host_->pending_translations()); 421 EXPECT_EQ(2, GetCacheSize()); 422 net::TestCompletionCallback cb; 423 // Since we are using a memory backend, the clear should happen immediately. 424 host_->ClearTranslationCacheEntriesBetween( 425 base::Time(), base::Time(), base::Bind(cb.callback(), 0)); 426 // Check that the translation cache has been cleared before flushing the 427 // queues, because the backend will be freed once it is. 428 EXPECT_EQ(0, GetCacheSize()); 429 EXPECT_EQ(0, cb.GetResult(net::ERR_IO_PENDING)); 430 // Now check that the backend has been freed. 431 EXPECT_FALSE(CacheIsInitialized()); 432 } 433 434 // A version of PnaclHostTest that initializes cache on disk. 435 class PnaclHostTestDisk : public PnaclHostTest { 436 protected: 437 virtual void SetUp() { 438 host_ = new PnaclHost(); 439 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 440 host_->InitForTest(temp_dir_.path(), false); 441 EXPECT_EQ(PnaclHost::CacheInitializing, host_->cache_state_); 442 } 443 void DeInit() { 444 host_->DeInitIfSafe(); 445 } 446 }; 447 TEST_F(PnaclHostTestDisk, DeInitWhileInitializing) { 448 // Since there's no easy way to pump message queues one message at a time, we 449 // have to simulate what would happen if 1 DeInitIfsafe task gets queued, then 450 // a GetNexeFd gets queued, and then another DeInitIfSafe gets queued before 451 // the first one runs. We can just shortcut and call DeInitIfSafe while the 452 // cache is still initializing. 453 DeInit(); 454 base::RunLoop().RunUntilIdle(); 455 } 456 457 } // namespace pnacl 458