• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
PnaclTranslationCacheTest()27   PnaclTranslationCacheTest()
28       : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {}
~PnaclTranslationCacheTest()29   virtual ~PnaclTranslationCacheTest() {}
SetUp()30   virtual void SetUp() { cache_.reset(new PnaclTranslationCache()); }
TearDown()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 
InitBackend(bool in_mem)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 
StoreNexe(const std::string & key,const std::string & nexe)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, 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:
TestNexeCallback()84   TestNexeCallback()
85       : have_result_(false),
86         result_(-1),
87         cb_(base::Bind(&TestNexeCallback::SetResult, base::Unretained(this))) {}
callback()88   GetNexeCallback callback() { return cb_; }
GetResult(int * result)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:
SetResult(int rv,scoped_refptr<net::DrainableIOBuffer> buf)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 
GetNexe(const std::string & key)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 
TEST(PnaclTranslationCacheKeyTest,CacheKeyTest)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   std::string test_time("Wed, 15 Nov 1995 06:25:24 GMT");
131   base::Time::FromString(test_time.c_str(), &info.last_modified);
132   // Basic check for URL and time components
133   EXPECT_EQ("ABI:0;opt:0;URL:http://www.google.com/;"
134             "modified:1995:11:15:6:25:24:0:UTC;etag:",
135             PnaclTranslationCache::GetKey(info));
136   // Check that query portion of URL is not stripped
137   info.pexe_url = GURL("http://www.google.com/?foo=bar");
138   EXPECT_EQ("ABI:0;opt:0;URL:http://www.google.com/?foo=bar;"
139             "modified:1995:11:15:6:25:24:0:UTC;etag:",
140             PnaclTranslationCache::GetKey(info));
141   // Check that username, password, and normal port are stripped
142   info.pexe_url = GURL("https://user:host@www.google.com:443/");
143   EXPECT_EQ("ABI:0;opt:0;URL:https://www.google.com/;"
144             "modified:1995:11:15:6:25:24:0:UTC;etag:",
145             PnaclTranslationCache::GetKey(info));
146   // Check that unusual port is not stripped but ref is stripped
147   info.pexe_url = GURL("https://www.google.com:444/#foo");
148   EXPECT_EQ("ABI:0;opt:0;URL:https://www.google.com:444/;"
149             "modified:1995:11:15:6:25:24:0:UTC;etag:",
150             PnaclTranslationCache::GetKey(info));
151   // Check chrome-extesnsion scheme
152   info.pexe_url = GURL("chrome-extension://ljacajndfccfgnfohlgkdphmbnpkjflk/");
153   EXPECT_EQ("ABI:0;opt:0;"
154             "URL:chrome-extension://ljacajndfccfgnfohlgkdphmbnpkjflk/;"
155             "modified:1995:11:15:6:25:24:0:UTC;etag:",
156             PnaclTranslationCache::GetKey(info));
157   // Check that ABI version, opt level, and etag are in the key
158   info.pexe_url = GURL("http://www.google.com/");
159   info.abi_version = 2;
160   EXPECT_EQ("ABI:2;opt:0;URL:http://www.google.com/;"
161             "modified:1995:11:15:6:25:24:0:UTC;etag:",
162             PnaclTranslationCache::GetKey(info));
163   info.opt_level = 2;
164   EXPECT_EQ("ABI:2;opt:2;URL:http://www.google.com/;"
165             "modified:1995:11:15:6:25:24:0:UTC;etag:",
166             PnaclTranslationCache::GetKey(info));
167   info.etag = std::string("etag");
168   EXPECT_EQ("ABI:2;opt:2;URL:http://www.google.com/;"
169             "modified:1995:11:15:6:25:24:0:UTC;etag:etag",
170             PnaclTranslationCache::GetKey(info));
171 
172   // Check for all the time components, and null time
173   info.last_modified = base::Time();
174   EXPECT_EQ("ABI:2;opt:2;URL:http://www.google.com/;"
175             "modified:0:0:0:0:0:0:0:UTC;etag:etag",
176             PnaclTranslationCache::GetKey(info));
177   test_time.assign("Fri, 29 Feb 2008 13:04:12 GMT");
178   base::Time::FromString(test_time.c_str(), &info.last_modified);
179   EXPECT_EQ("ABI:2;opt:2;URL:http://www.google.com/;"
180             "modified:2008:2:29:13:4:12:0:UTC;etag:etag",
181             PnaclTranslationCache::GetKey(info));
182 }
183 
TEST_F(PnaclTranslationCacheTest,StoreSmallInMem)184 TEST_F(PnaclTranslationCacheTest, StoreSmallInMem) {
185   // Test that a single store puts something in the mem backend
186   InitBackend(true);
187   StoreNexe(test_key, test_store_val);
188   EXPECT_EQ(1, cache_->Size());
189 }
190 
TEST_F(PnaclTranslationCacheTest,StoreSmallOnDisk)191 TEST_F(PnaclTranslationCacheTest, StoreSmallOnDisk) {
192   // Test that a single store puts something in the disk backend
193   InitBackend(false);
194   StoreNexe(test_key, test_store_val);
195   EXPECT_EQ(1, cache_->Size());
196 }
197 
TEST_F(PnaclTranslationCacheTest,StoreLargeOnDisk)198 TEST_F(PnaclTranslationCacheTest, StoreLargeOnDisk) {
199   // Test a value too large(?) for a single I/O operation
200   InitBackend(false);
201   const std::string large_buffer(kLargeNexeSize, 'a');
202   StoreNexe(test_key, large_buffer);
203   EXPECT_EQ(1, cache_->Size());
204 }
205 
TEST_F(PnaclTranslationCacheTest,InMemSizeLimit)206 TEST_F(PnaclTranslationCacheTest, InMemSizeLimit) {
207   InitBackend(true);
208   scoped_refptr<net::DrainableIOBuffer> large_buffer(new net::DrainableIOBuffer(
209       new net::StringIOBuffer(std::string(kMaxMemCacheSize + 1, 'a')),
210       kMaxMemCacheSize + 1));
211   net::TestCompletionCallback store_cb;
212   cache_->StoreNexe(test_key, large_buffer, store_cb.callback());
213   EXPECT_EQ(net::ERR_FAILED, store_cb.GetResult(net::ERR_IO_PENDING));
214   base::RunLoop().RunUntilIdle();  // Ensure the entry is closed.
215   EXPECT_EQ(0, cache_->Size());
216 }
217 
TEST_F(PnaclTranslationCacheTest,GetOneInMem)218 TEST_F(PnaclTranslationCacheTest, GetOneInMem) {
219   InitBackend(true);
220   StoreNexe(test_key, test_store_val);
221   EXPECT_EQ(1, cache_->Size());
222   EXPECT_EQ(0, GetNexe(test_key).compare(test_store_val));
223 }
224 
TEST_F(PnaclTranslationCacheTest,GetOneOnDisk)225 TEST_F(PnaclTranslationCacheTest, GetOneOnDisk) {
226   InitBackend(false);
227   StoreNexe(test_key, test_store_val);
228   EXPECT_EQ(1, cache_->Size());
229   EXPECT_EQ(0, GetNexe(test_key).compare(test_store_val));
230 }
231 
TEST_F(PnaclTranslationCacheTest,GetLargeOnDisk)232 TEST_F(PnaclTranslationCacheTest, GetLargeOnDisk) {
233   InitBackend(false);
234   const std::string large_buffer(kLargeNexeSize, 'a');
235   StoreNexe(test_key, large_buffer);
236   EXPECT_EQ(1, cache_->Size());
237   EXPECT_EQ(0, GetNexe(test_key).compare(large_buffer));
238 }
239 
TEST_F(PnaclTranslationCacheTest,StoreTwice)240 TEST_F(PnaclTranslationCacheTest, StoreTwice) {
241   // Test that storing twice with the same key overwrites
242   InitBackend(true);
243   StoreNexe(test_key, test_store_val);
244   StoreNexe(test_key, test_store_val + "aaa");
245   EXPECT_EQ(1, cache_->Size());
246   EXPECT_EQ(0, GetNexe(test_key).compare(test_store_val + "aaa"));
247 }
248 
TEST_F(PnaclTranslationCacheTest,StoreTwo)249 TEST_F(PnaclTranslationCacheTest, StoreTwo) {
250   InitBackend(true);
251   StoreNexe(test_key, test_store_val);
252   StoreNexe(test_key + "a", test_store_val + "aaa");
253   EXPECT_EQ(2, cache_->Size());
254   EXPECT_EQ(0, GetNexe(test_key).compare(test_store_val));
255   EXPECT_EQ(0, GetNexe(test_key + "a").compare(test_store_val + "aaa"));
256 }
257 
TEST_F(PnaclTranslationCacheTest,GetMiss)258 TEST_F(PnaclTranslationCacheTest, GetMiss) {
259   InitBackend(true);
260   StoreNexe(test_key, test_store_val);
261   TestNexeCallback load_cb;
262   std::string nexe;
263   cache_->GetNexe(test_key + "a", load_cb.callback());
264   int rv;
265   scoped_refptr<net::DrainableIOBuffer> buf(load_cb.GetResult(&rv));
266   EXPECT_EQ(net::ERR_FAILED, rv);
267 }
268 
269 }  // namespace pnacl
270