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