Home | History | Annotate | Download | only in browser
      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