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_translation_cache.h"
      6 
      7 #include "base/files/file_path.h"
      8 #include "base/files/scoped_temp_dir.h"
      9 #include "base/message_loop/message_loop.h"
     10 #include "base/run_loop.h"
     11 #include "components/nacl/common/pnacl_types.h"
     12 #include "content/public/browser/browser_thread.h"
     13 #include "content/public/test/test_browser_thread_bundle.h"
     14 #include "net/base/io_buffer.h"
     15 #include "net/base/test_completion_callback.h"
     16 #include "testing/gtest/include/gtest/gtest.h"
     17 
     18 using content::BrowserThread;
     19 using base::FilePath;
     20 
     21 namespace pnacl {
     22 
     23 const int kTestDiskCacheSize = 16 * 1024 * 1024;
     24 
     25 class PnaclTranslationCacheTest : public testing::Test {
     26  protected:
     27   PnaclTranslationCacheTest()
     28       : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {}
     29   virtual ~PnaclTranslationCacheTest() {}
     30   virtual void SetUp() { cache_.reset(new PnaclTranslationCache()); }
     31   virtual void TearDown() {
     32     // The destructor of PnaclTranslationCacheWriteEntry posts a task to the IO
     33     // thread to close the backend cache entry. We want to make sure the entries
     34     // are closed before we delete the backend (and in particular the destructor
     35     // for the memory backend has a DCHECK to verify this), so we run the loop
     36     // here to ensure the task gets processed.
     37     base::RunLoop().RunUntilIdle();
     38     cache_.reset();
     39   }
     40 
     41   void InitBackend(bool in_mem);
     42   void StoreNexe(const std::string& key, const std::string& nexe);
     43   std::string GetNexe(const std::string& key);
     44 
     45   scoped_ptr<PnaclTranslationCache> cache_;
     46   content::TestBrowserThreadBundle thread_bundle_;
     47   base::ScopedTempDir temp_dir_;
     48 };
     49 
     50 void PnaclTranslationCacheTest::InitBackend(bool in_mem) {
     51   net::TestCompletionCallback init_cb;
     52   if (!in_mem) {
     53     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
     54   }
     55   // Use the private init method so we can control the size
     56   int rv = cache_->Init(in_mem ? net::MEMORY_CACHE : net::PNACL_CACHE,
     57                         temp_dir_.path(),
     58                         in_mem ? kMaxMemCacheSize : kTestDiskCacheSize,
     59                         init_cb.callback());
     60   if (in_mem)
     61     ASSERT_EQ(net::OK, rv);
     62   ASSERT_EQ(net::OK, init_cb.GetResult(rv));
     63   ASSERT_EQ(0, cache_->Size());
     64 }
     65 
     66 void PnaclTranslationCacheTest::StoreNexe(const std::string& key,
     67                                           const std::string& nexe) {
     68   net::TestCompletionCallback store_cb;
     69   scoped_refptr<net::DrainableIOBuffer> nexe_buf(
     70       new net::DrainableIOBuffer(new net::StringIOBuffer(nexe), nexe.size()));
     71   cache_->StoreNexe(key, nexe_buf.get(), store_cb.callback());
     72   // Using ERR_IO_PENDING here causes the callback to wait for the result
     73   // which should be harmless even if it returns OK immediately. This is because
     74   // we don't plumb the intermediate writing stages all the way out.
     75   EXPECT_EQ(net::OK, store_cb.GetResult(net::ERR_IO_PENDING));
     76 }
     77 
     78 // Inspired by net::TestCompletionCallback. Instantiate a TestNexeCallback and
     79 // pass the GetNexeCallback returned by the callback() method to GetNexe.
     80 // Then call GetResult, which will pump the message loop until it gets a result,
     81 // return the resulting IOBuffer and fill in the return value
     82 class TestNexeCallback {
     83  public:
     84   TestNexeCallback()
     85       : have_result_(false),
     86         result_(-1),
     87         cb_(base::Bind(&TestNexeCallback::SetResult, base::Unretained(this))) {}
     88   GetNexeCallback callback() { return cb_; }
     89   net::DrainableIOBuffer* GetResult(int* result) {
     90     while (!have_result_)
     91       base::RunLoop().RunUntilIdle();
     92     have_result_ = false;
     93     *result = result_;
     94     return buf_.get();
     95   }
     96 
     97  private:
     98   void SetResult(int rv, scoped_refptr<net::DrainableIOBuffer> buf) {
     99     have_result_ = true;
    100     result_ = rv;
    101     buf_ = buf;
    102   }
    103   bool have_result_;
    104   int result_;
    105   scoped_refptr<net::DrainableIOBuffer> buf_;
    106   const GetNexeCallback cb_;
    107 };
    108 
    109 std::string PnaclTranslationCacheTest::GetNexe(const std::string& key) {
    110   TestNexeCallback load_cb;
    111   cache_->GetNexe(key, load_cb.callback());
    112   int rv;
    113   scoped_refptr<net::DrainableIOBuffer> buf(load_cb.GetResult(&rv));
    114   EXPECT_EQ(net::OK, rv);
    115   if (buf.get() == NULL) // for some reason ASSERT macros don't work here.
    116     return std::string();
    117   std::string nexe(buf->data(), buf->size());
    118   return nexe;
    119 }
    120 
    121 static const std::string test_key("1");
    122 static const std::string test_store_val("testnexe");
    123 static const int kLargeNexeSize = 8 * 1024 * 1024;
    124 
    125 TEST(PnaclTranslationCacheKeyTest, CacheKeyTest) {
    126   nacl::PnaclCacheInfo info;
    127   info.pexe_url = GURL("http://www.google.com");
    128   info.abi_version = 0;
    129   info.opt_level = 0;
    130   info.sandbox_isa = "x86-32";
    131   std::string test_time("Wed, 15 Nov 1995 06:25:24 GMT");
    132   base::Time::FromString(test_time.c_str(), &info.last_modified);
    133   // Basic check for URL and time components
    134   EXPECT_EQ("ABI:0;opt:0;URL:http://www.google.com/;"
    135             "modified:1995:11:15:6:25:24:0:UTC;etag:;"
    136             "sandbox:x86-32;extra_flags:;",
    137             PnaclTranslationCache::GetKey(info));
    138   // Check that query portion of URL is not stripped
    139   info.pexe_url = GURL("http://www.google.com/?foo=bar");
    140   EXPECT_EQ("ABI:0;opt:0;URL:http://www.google.com/?foo=bar;"
    141             "modified:1995:11:15:6:25:24:0:UTC;etag:;"
    142             "sandbox:x86-32;extra_flags:;",
    143             PnaclTranslationCache::GetKey(info));
    144   // Check that username, password, and normal port are stripped
    145   info.pexe_url = GURL("https://user:host@www.google.com:443/");
    146   EXPECT_EQ("ABI:0;opt:0;URL:https://www.google.com/;"
    147             "modified:1995:11:15:6:25:24:0:UTC;etag:;"
    148             "sandbox:x86-32;extra_flags:;",
    149             PnaclTranslationCache::GetKey(info));
    150   // Check that unusual port is not stripped but ref is stripped
    151   info.pexe_url = GURL("https://www.google.com:444/#foo");
    152   EXPECT_EQ("ABI:0;opt:0;URL:https://www.google.com:444/;"
    153             "modified:1995:11:15:6:25:24:0:UTC;etag:;"
    154             "sandbox:x86-32;extra_flags:;",
    155             PnaclTranslationCache::GetKey(info));
    156   // Check chrome-extesnsion scheme
    157   info.pexe_url = GURL("chrome-extension://ljacajndfccfgnfohlgkdphmbnpkjflk/");
    158   EXPECT_EQ("ABI:0;opt:0;"
    159             "URL:chrome-extension://ljacajndfccfgnfohlgkdphmbnpkjflk/;"
    160             "modified:1995:11:15:6:25:24:0:UTC;etag:;"
    161             "sandbox:x86-32;extra_flags:;",
    162             PnaclTranslationCache::GetKey(info));
    163   // Check that ABI version, opt level, and etag are in the key
    164   info.pexe_url = GURL("http://www.google.com/");
    165   info.abi_version = 2;
    166   EXPECT_EQ("ABI:2;opt:0;URL:http://www.google.com/;"
    167             "modified:1995:11:15:6:25:24:0:UTC;etag:;"
    168             "sandbox:x86-32;extra_flags:;",
    169             PnaclTranslationCache::GetKey(info));
    170   info.opt_level = 2;
    171   EXPECT_EQ("ABI:2;opt:2;URL:http://www.google.com/;"
    172             "modified:1995:11:15:6:25:24:0:UTC;etag:;"
    173             "sandbox:x86-32;extra_flags:;",
    174             PnaclTranslationCache::GetKey(info));
    175   info.etag = std::string("etag");
    176   EXPECT_EQ("ABI:2;opt:2;URL:http://www.google.com/;"
    177             "modified:1995:11:15:6:25:24:0:UTC;etag:etag;"
    178             "sandbox:x86-32;extra_flags:;",
    179             PnaclTranslationCache::GetKey(info));
    180 
    181   info.extra_flags = "-mavx-neon";
    182   EXPECT_EQ("ABI:2;opt:2;URL:http://www.google.com/;"
    183             "modified:1995:11:15:6:25:24:0:UTC;etag:etag;"
    184             "sandbox:x86-32;extra_flags:-mavx-neon;",
    185             PnaclTranslationCache::GetKey(info));
    186 
    187   // Check for all the time components, and null time
    188   info.last_modified = base::Time();
    189   EXPECT_EQ("ABI:2;opt:2;URL:http://www.google.com/;"
    190             "modified:0:0:0:0:0:0:0:UTC;etag:etag;"
    191             "sandbox:x86-32;extra_flags:-mavx-neon;",
    192             PnaclTranslationCache::GetKey(info));
    193   test_time.assign("Fri, 29 Feb 2008 13:04:12 GMT");
    194   base::Time::FromString(test_time.c_str(), &info.last_modified);
    195   EXPECT_EQ("ABI:2;opt:2;URL:http://www.google.com/;"
    196             "modified:2008:2:29:13:4:12:0:UTC;etag:etag;"
    197             "sandbox:x86-32;extra_flags:-mavx-neon;",
    198             PnaclTranslationCache::GetKey(info));
    199 }
    200 
    201 TEST_F(PnaclTranslationCacheTest, StoreSmallInMem) {
    202   // Test that a single store puts something in the mem backend
    203   InitBackend(true);
    204   StoreNexe(test_key, test_store_val);
    205   EXPECT_EQ(1, cache_->Size());
    206 }
    207 
    208 TEST_F(PnaclTranslationCacheTest, StoreSmallOnDisk) {
    209   // Test that a single store puts something in the disk backend
    210   InitBackend(false);
    211   StoreNexe(test_key, test_store_val);
    212   EXPECT_EQ(1, cache_->Size());
    213 }
    214 
    215 TEST_F(PnaclTranslationCacheTest, StoreLargeOnDisk) {
    216   // Test a value too large(?) for a single I/O operation
    217   InitBackend(false);
    218   const std::string large_buffer(kLargeNexeSize, 'a');
    219   StoreNexe(test_key, large_buffer);
    220   EXPECT_EQ(1, cache_->Size());
    221 }
    222 
    223 TEST_F(PnaclTranslationCacheTest, InMemSizeLimit) {
    224   InitBackend(true);
    225   scoped_refptr<net::DrainableIOBuffer> large_buffer(new net::DrainableIOBuffer(
    226       new net::StringIOBuffer(std::string(kMaxMemCacheSize + 1, 'a')),
    227       kMaxMemCacheSize + 1));
    228   net::TestCompletionCallback store_cb;
    229   cache_->StoreNexe(test_key, large_buffer.get(), store_cb.callback());
    230   EXPECT_EQ(net::ERR_FAILED, store_cb.GetResult(net::ERR_IO_PENDING));
    231   base::RunLoop().RunUntilIdle();  // Ensure the entry is closed.
    232   EXPECT_EQ(0, cache_->Size());
    233 }
    234 
    235 TEST_F(PnaclTranslationCacheTest, GetOneInMem) {
    236   InitBackend(true);
    237   StoreNexe(test_key, test_store_val);
    238   EXPECT_EQ(1, cache_->Size());
    239   EXPECT_EQ(0, GetNexe(test_key).compare(test_store_val));
    240 }
    241 
    242 TEST_F(PnaclTranslationCacheTest, GetOneOnDisk) {
    243   InitBackend(false);
    244   StoreNexe(test_key, test_store_val);
    245   EXPECT_EQ(1, cache_->Size());
    246   EXPECT_EQ(0, GetNexe(test_key).compare(test_store_val));
    247 }
    248 
    249 TEST_F(PnaclTranslationCacheTest, GetLargeOnDisk) {
    250   InitBackend(false);
    251   const std::string large_buffer(kLargeNexeSize, 'a');
    252   StoreNexe(test_key, large_buffer);
    253   EXPECT_EQ(1, cache_->Size());
    254   EXPECT_EQ(0, GetNexe(test_key).compare(large_buffer));
    255 }
    256 
    257 TEST_F(PnaclTranslationCacheTest, StoreTwice) {
    258   // Test that storing twice with the same key overwrites
    259   InitBackend(true);
    260   StoreNexe(test_key, test_store_val);
    261   StoreNexe(test_key, test_store_val + "aaa");
    262   EXPECT_EQ(1, cache_->Size());
    263   EXPECT_EQ(0, GetNexe(test_key).compare(test_store_val + "aaa"));
    264 }
    265 
    266 TEST_F(PnaclTranslationCacheTest, StoreTwo) {
    267   InitBackend(true);
    268   StoreNexe(test_key, test_store_val);
    269   StoreNexe(test_key + "a", test_store_val + "aaa");
    270   EXPECT_EQ(2, cache_->Size());
    271   EXPECT_EQ(0, GetNexe(test_key).compare(test_store_val));
    272   EXPECT_EQ(0, GetNexe(test_key + "a").compare(test_store_val + "aaa"));
    273 }
    274 
    275 TEST_F(PnaclTranslationCacheTest, GetMiss) {
    276   InitBackend(true);
    277   StoreNexe(test_key, test_store_val);
    278   TestNexeCallback load_cb;
    279   std::string nexe;
    280   cache_->GetNexe(test_key + "a", load_cb.callback());
    281   int rv;
    282   scoped_refptr<net::DrainableIOBuffer> buf(load_cb.GetResult(&rv));
    283   EXPECT_EQ(net::ERR_FAILED, rv);
    284 }
    285 
    286 }  // namespace pnacl
    287