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