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