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()); 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 433