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.
5 #include "chrome/browser/nacl_host/pnacl_translation_cache.h"
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"
18 using content::BrowserThread;
23 const int kTestDiskCacheSize = 16 * 1024 * 1024;
25 class PnaclTranslationCacheTest : public testing::Test {
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();
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);
45 scoped_ptr<PnaclTranslationCache> cache_;
46 content::TestBrowserThreadBundle thread_bundle_;
47 base::ScopedTempDir temp_dir_;
50 void PnaclTranslationCacheTest::InitBackend(bool in_mem) {
51 net::TestCompletionCallback init_cb;
53 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
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,
58 in_mem ? kMaxMemCacheSize : kTestDiskCacheSize,
61 ASSERT_EQ(net::OK, rv);
62 ASSERT_EQ(net::OK, init_cb.GetResult(rv));
63 ASSERT_EQ(0, cache_->Size());
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));
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 {
85 : have_result_(false),
87 cb_(base::Bind(&TestNexeCallback::SetResult, base::Unretained(this))) {}
88 GetNexeCallback callback() { return cb_; }
89 net::DrainableIOBuffer* GetResult(int* result) {
91 base::RunLoop().RunUntilIdle();
98 void SetResult(int rv, scoped_refptr<net::DrainableIOBuffer> buf) {
105 scoped_refptr<net::DrainableIOBuffer> buf_;
106 const GetNexeCallback cb_;
109 std::string PnaclTranslationCacheTest::GetNexe(const std::string& key) {
110 TestNexeCallback load_cb;
111 cache_->GetNexe(key, load_cb.callback());
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());
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;
125 TEST(PnaclTranslationCacheKeyTest, CacheKeyTest) {
126 nacl::PnaclCacheInfo info;
127 info.pexe_url = GURL("http://www.google.com");
128 info.abi_version = 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));
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));
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));
184 TEST_F(PnaclTranslationCacheTest, StoreSmallInMem) {
185 // Test that a single store puts something in the mem backend
187 StoreNexe(test_key, test_store_val);
188 EXPECT_EQ(1, cache_->Size());
191 TEST_F(PnaclTranslationCacheTest, StoreSmallOnDisk) {
192 // Test that a single store puts something in the disk backend
194 StoreNexe(test_key, test_store_val);
195 EXPECT_EQ(1, cache_->Size());
198 TEST_F(PnaclTranslationCacheTest, StoreLargeOnDisk) {
199 // Test a value too large(?) for a single I/O operation
201 const std::string large_buffer(kLargeNexeSize, 'a');
202 StoreNexe(test_key, large_buffer);
203 EXPECT_EQ(1, cache_->Size());
206 TEST_F(PnaclTranslationCacheTest, InMemSizeLimit) {
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());
218 TEST_F(PnaclTranslationCacheTest, GetOneInMem) {
220 StoreNexe(test_key, test_store_val);
221 EXPECT_EQ(1, cache_->Size());
222 EXPECT_EQ(0, GetNexe(test_key).compare(test_store_val));
225 TEST_F(PnaclTranslationCacheTest, GetOneOnDisk) {
227 StoreNexe(test_key, test_store_val);
228 EXPECT_EQ(1, cache_->Size());
229 EXPECT_EQ(0, GetNexe(test_key).compare(test_store_val));
232 TEST_F(PnaclTranslationCacheTest, GetLargeOnDisk) {
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));
240 TEST_F(PnaclTranslationCacheTest, StoreTwice) {
241 // Test that storing twice with the same key overwrites
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"));
249 TEST_F(PnaclTranslationCacheTest, StoreTwo) {
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"));
258 TEST_F(PnaclTranslationCacheTest, GetMiss) {
260 StoreNexe(test_key, test_store_val);
261 TestNexeCallback load_cb;
263 cache_->GetNexe(test_key + "a", load_cb.callback());
265 scoped_refptr<net::DrainableIOBuffer> buf(load_cb.GetResult(&rv));
266 EXPECT_EQ(net::ERR_FAILED, rv);