OLD | NEW |
| (Empty) |
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 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, 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 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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
OLD | NEW |