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_host.h" | |
6 | |
7 #include <stdio.h> | |
8 #include "base/bind.h" | |
9 #include "base/files/scoped_temp_dir.h" | |
10 #include "base/run_loop.h" | |
11 #include "base/threading/sequenced_worker_pool.h" | |
12 #include "components/nacl/browser/pnacl_translation_cache.h" | |
13 #include "content/public/browser/browser_thread.h" | |
14 #include "content/public/test/test_browser_thread_bundle.h" | |
15 #include "net/base/test_completion_callback.h" | |
16 #include "testing/gtest/include/gtest/gtest.h" | |
17 | |
18 #if defined(OS_WIN) | |
19 #define snprintf _snprintf | |
20 #endif | |
21 | |
22 namespace pnacl { | |
23 | |
24 class PnaclHostTest : public testing::Test { | |
25 protected: | |
26 PnaclHostTest() | |
27 : host_(NULL), | |
28 temp_callback_count_(0), | |
29 write_callback_count_(0), | |
30 thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {} | |
31 virtual void SetUp() { | |
32 host_ = new PnaclHost(); | |
33 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); | |
34 host_->InitForTest(temp_dir_.path()); | |
35 base::RunLoop().RunUntilIdle(); | |
36 EXPECT_EQ(PnaclHost::CacheReady, host_->cache_state_); | |
37 } | |
38 virtual void TearDown() { | |
39 EXPECT_EQ(0U, host_->pending_translations()); | |
40 // Give the host a chance to de-init the backend, and then delete it. | |
41 host_->RendererClosing(0); | |
42 FlushQueues(); | |
43 EXPECT_EQ(PnaclHost::CacheUninitialized, host_->cache_state_); | |
44 delete host_; | |
45 } | |
46 // Flush the blocking pool first, then any tasks it posted to the IO thread. | |
47 // Do 2 rounds of flushing, because some operations require 2 trips back and | |
48 // forth between the threads. | |
49 void FlushQueues() { | |
50 content::BrowserThread::GetBlockingPool()->FlushForTesting(); | |
51 base::RunLoop().RunUntilIdle(); | |
52 content::BrowserThread::GetBlockingPool()->FlushForTesting(); | |
53 base::RunLoop().RunUntilIdle(); | |
54 } | |
55 int GetCacheSize() { return host_->disk_cache_->Size(); } | |
56 int CacheIsInitialized() { | |
57 return host_->cache_state_ == PnaclHost::CacheReady; | |
58 } | |
59 void ReInitBackend() { | |
60 host_->InitForTest(temp_dir_.path()); | |
61 base::RunLoop().RunUntilIdle(); | |
62 EXPECT_EQ(PnaclHost::CacheReady, host_->cache_state_); | |
63 } | |
64 | |
65 public: // Required for derived classes to bind this method | |
66 // Callbacks used by tests which call GetNexeFd. | |
67 // CallbackExpectMiss checks that the fd is valid and a miss is reported, | |
68 // and also writes some data into the file, which is read back by | |
69 // CallbackExpectHit | |
70 void CallbackExpectMiss(base::PlatformFile fd, bool is_hit) { | |
71 EXPECT_FALSE(is_hit); | |
72 ASSERT_FALSE(fd == base::kInvalidPlatformFileValue); | |
73 base::PlatformFileInfo info; | |
74 EXPECT_TRUE(base::GetPlatformFileInfo(fd, &info)); | |
75 EXPECT_FALSE(info.is_directory); | |
76 EXPECT_EQ(0LL, info.size); | |
77 char str[16]; | |
78 memset(str, 0x0, 16); | |
79 snprintf(str, 16, "testdata%d", ++write_callback_count_); | |
80 EXPECT_EQ(16, base::WritePlatformFile(fd, 0, str, 16)); | |
81 temp_callback_count_++; | |
82 } | |
83 void CallbackExpectHit(base::PlatformFile fd, bool is_hit) { | |
84 EXPECT_TRUE(is_hit); | |
85 ASSERT_FALSE(fd == base::kInvalidPlatformFileValue); | |
86 base::PlatformFileInfo info; | |
87 EXPECT_TRUE(base::GetPlatformFileInfo(fd, &info)); | |
88 EXPECT_FALSE(info.is_directory); | |
89 EXPECT_EQ(16LL, info.size); | |
90 char data[16]; | |
91 memset(data, 0x0, 16); | |
92 char str[16]; | |
93 memset(str, 0x0, 16); | |
94 snprintf(str, 16, "testdata%d", write_callback_count_); | |
95 EXPECT_EQ(16, base::ReadPlatformFile(fd, 0, data, 16)); | |
96 EXPECT_STREQ(str, data); | |
97 temp_callback_count_++; | |
98 } | |
99 | |
100 protected: | |
101 PnaclHost* host_; | |
102 int temp_callback_count_; | |
103 int write_callback_count_; | |
104 content::TestBrowserThreadBundle thread_bundle_; | |
105 base::ScopedTempDir temp_dir_; | |
106 }; | |
107 | |
108 static nacl::PnaclCacheInfo GetTestCacheInfo() { | |
109 nacl::PnaclCacheInfo info; | |
110 info.pexe_url = GURL("http://www.google.com"); | |
111 info.abi_version = 0; | |
112 info.opt_level = 0; | |
113 info.has_no_store_header = false; | |
114 return info; | |
115 } | |
116 | |
117 #define GET_NEXE_FD(renderer, instance, incognito, info, expect_hit) \ | |
118 do { \ | |
119 SCOPED_TRACE(""); \ | |
120 host_->GetNexeFd( \ | |
121 renderer, \ | |
122 0, /* ignore render_view_id for now */ \ | |
123 instance, \ | |
124 incognito, \ | |
125 info, \ | |
126 base::Bind(expect_hit ? &PnaclHostTest::CallbackExpectHit \ | |
127 : &PnaclHostTest::CallbackExpectMiss, \ | |
128 base::Unretained(this))); \ | |
129 } while (0) | |
130 | |
131 TEST_F(PnaclHostTest, BasicMiss) { | |
132 nacl::PnaclCacheInfo info = GetTestCacheInfo(); | |
133 // Test cold miss. | |
134 GET_NEXE_FD(0, 0, false, info, false); | |
135 EXPECT_EQ(1U, host_->pending_translations()); | |
136 FlushQueues(); | |
137 EXPECT_EQ(1U, host_->pending_translations()); | |
138 EXPECT_EQ(1, temp_callback_count_); | |
139 host_->TranslationFinished(0, 0, true); | |
140 FlushQueues(); | |
141 EXPECT_EQ(0U, host_->pending_translations()); | |
142 // Test that a different cache info field also misses. | |
143 info.etag = std::string("something else"); | |
144 GET_NEXE_FD(0, 0, false, info, false); | |
145 FlushQueues(); | |
146 EXPECT_EQ(2, temp_callback_count_); | |
147 EXPECT_EQ(1U, host_->pending_translations()); | |
148 host_->RendererClosing(0); | |
149 FlushQueues(); | |
150 // Check that the cache has de-initialized after the last renderer goes away. | |
151 EXPECT_FALSE(CacheIsInitialized()); | |
152 } | |
153 | |
154 TEST_F(PnaclHostTest, BadArguments) { | |
155 nacl::PnaclCacheInfo info = GetTestCacheInfo(); | |
156 GET_NEXE_FD(0, 0, false, info, false); | |
157 EXPECT_EQ(1U, host_->pending_translations()); | |
158 host_->TranslationFinished(0, 1, true); // nonexistent translation | |
159 EXPECT_EQ(1U, host_->pending_translations()); | |
160 host_->RendererClosing(1); // nonexistent renderer | |
161 EXPECT_EQ(1U, host_->pending_translations()); | |
162 FlushQueues(); | |
163 EXPECT_EQ(1, temp_callback_count_); | |
164 host_->RendererClosing(0); // close without finishing | |
165 } | |
166 | |
167 TEST_F(PnaclHostTest, BasicHit) { | |
168 nacl::PnaclCacheInfo info = GetTestCacheInfo(); | |
169 GET_NEXE_FD(0, 0, false, info, false); | |
170 FlushQueues(); | |
171 EXPECT_EQ(1, temp_callback_count_); | |
172 host_->TranslationFinished(0, 0, true); | |
173 FlushQueues(); | |
174 GET_NEXE_FD(0, 1, false, info, true); | |
175 FlushQueues(); | |
176 EXPECT_EQ(2, temp_callback_count_); | |
177 EXPECT_EQ(0U, host_->pending_translations()); | |
178 } | |
179 | |
180 TEST_F(PnaclHostTest, TranslationErrors) { | |
181 nacl::PnaclCacheInfo info = GetTestCacheInfo(); | |
182 GET_NEXE_FD(0, 0, false, info, false); | |
183 // Early abort, before temp file request returns | |
184 host_->TranslationFinished(0, 0, false); | |
185 FlushQueues(); | |
186 EXPECT_EQ(0U, host_->pending_translations()); | |
187 EXPECT_EQ(0, temp_callback_count_); | |
188 // The backend will have been freed when the query comes back and there | |
189 // are no pending translations. | |
190 EXPECT_FALSE(CacheIsInitialized()); | |
191 ReInitBackend(); | |
192 // Check that another request for the same info misses successfully. | |
193 GET_NEXE_FD(0, 0, false, info, false); | |
194 FlushQueues(); | |
195 host_->TranslationFinished(0, 0, true); | |
196 FlushQueues(); | |
197 EXPECT_EQ(1, temp_callback_count_); | |
198 EXPECT_EQ(0U, host_->pending_translations()); | |
199 | |
200 // Now try sending the error after the temp file request returns | |
201 info.abi_version = 222; | |
202 GET_NEXE_FD(0, 0, false, info, false); | |
203 FlushQueues(); | |
204 EXPECT_EQ(2, temp_callback_count_); | |
205 host_->TranslationFinished(0, 0, false); | |
206 FlushQueues(); | |
207 EXPECT_EQ(0U, host_->pending_translations()); | |
208 // Check another successful miss | |
209 GET_NEXE_FD(0, 0, false, info, false); | |
210 FlushQueues(); | |
211 EXPECT_EQ(3, temp_callback_count_); | |
212 host_->TranslationFinished(0, 0, false); | |
213 EXPECT_EQ(0U, host_->pending_translations()); | |
214 } | |
215 | |
216 TEST_F(PnaclHostTest, OverlappedMissesAfterTempReturn) { | |
217 nacl::PnaclCacheInfo info = GetTestCacheInfo(); | |
218 GET_NEXE_FD(0, 0, false, info, false); | |
219 FlushQueues(); | |
220 EXPECT_EQ(1, temp_callback_count_); | |
221 EXPECT_EQ(1U, host_->pending_translations()); | |
222 // Test that a second request for the same nexe while the first one is still | |
223 // outstanding eventually hits. | |
224 GET_NEXE_FD(0, 1, false, info, true); | |
225 FlushQueues(); | |
226 EXPECT_EQ(2U, host_->pending_translations()); | |
227 // The temp file should not be returned to the second request until after the | |
228 // first is finished translating. | |
229 EXPECT_EQ(1, temp_callback_count_); | |
230 host_->TranslationFinished(0, 0, true); | |
231 FlushQueues(); | |
232 EXPECT_EQ(2, temp_callback_count_); | |
233 EXPECT_EQ(0U, host_->pending_translations()); | |
234 } | |
235 | |
236 TEST_F(PnaclHostTest, OverlappedMissesBeforeTempReturn) { | |
237 nacl::PnaclCacheInfo info = GetTestCacheInfo(); | |
238 GET_NEXE_FD(0, 0, false, info, false); | |
239 // Send the 2nd fd request before the first one returns a temp file. | |
240 GET_NEXE_FD(0, 1, false, info, true); | |
241 FlushQueues(); | |
242 EXPECT_EQ(1, temp_callback_count_); | |
243 EXPECT_EQ(2U, host_->pending_translations()); | |
244 FlushQueues(); | |
245 EXPECT_EQ(2U, host_->pending_translations()); | |
246 EXPECT_EQ(1, temp_callback_count_); | |
247 host_->TranslationFinished(0, 0, true); | |
248 FlushQueues(); | |
249 EXPECT_EQ(2, temp_callback_count_); | |
250 EXPECT_EQ(0U, host_->pending_translations()); | |
251 } | |
252 | |
253 TEST_F(PnaclHostTest, OverlappedHitsBeforeTempReturn) { | |
254 nacl::PnaclCacheInfo info = GetTestCacheInfo(); | |
255 // Store one in the cache and complete it. | |
256 GET_NEXE_FD(0, 0, false, info, false); | |
257 FlushQueues(); | |
258 EXPECT_EQ(1, temp_callback_count_); | |
259 host_->TranslationFinished(0, 0, true); | |
260 FlushQueues(); | |
261 EXPECT_EQ(0U, host_->pending_translations()); | |
262 GET_NEXE_FD(0, 0, false, info, true); | |
263 // Request the second before the first temp file returns. | |
264 GET_NEXE_FD(0, 1, false, info, true); | |
265 FlushQueues(); | |
266 EXPECT_EQ(3, temp_callback_count_); | |
267 EXPECT_EQ(0U, host_->pending_translations()); | |
268 } | |
269 | |
270 TEST_F(PnaclHostTest, OverlappedHitsAfterTempReturn) { | |
271 nacl::PnaclCacheInfo info = GetTestCacheInfo(); | |
272 // Store one in the cache and complete it. | |
273 GET_NEXE_FD(0, 0, false, info, false); | |
274 FlushQueues(); | |
275 EXPECT_EQ(1, temp_callback_count_); | |
276 host_->TranslationFinished(0, 0, true); | |
277 FlushQueues(); | |
278 EXPECT_EQ(0U, host_->pending_translations()); | |
279 GET_NEXE_FD(0, 0, false, info, true); | |
280 FlushQueues(); | |
281 GET_NEXE_FD(0, 1, false, info, true); | |
282 FlushQueues(); | |
283 EXPECT_EQ(3, temp_callback_count_); | |
284 EXPECT_EQ(0U, host_->pending_translations()); | |
285 } | |
286 | |
287 TEST_F(PnaclHostTest, OverlappedMissesRendererClosing) { | |
288 nacl::PnaclCacheInfo info = GetTestCacheInfo(); | |
289 GET_NEXE_FD(0, 0, false, info, false); | |
290 // Send the 2nd fd request from a different renderer. | |
291 // Test that it eventually gets an fd after the first renderer closes. | |
292 GET_NEXE_FD(1, 1, false, info, false); | |
293 FlushQueues(); | |
294 EXPECT_EQ(1, temp_callback_count_); | |
295 EXPECT_EQ(2U, host_->pending_translations()); | |
296 FlushQueues(); | |
297 EXPECT_EQ(2U, host_->pending_translations()); | |
298 EXPECT_EQ(1, temp_callback_count_); | |
299 host_->RendererClosing(0); | |
300 FlushQueues(); | |
301 EXPECT_EQ(2, temp_callback_count_); | |
302 EXPECT_EQ(1U, host_->pending_translations()); | |
303 host_->RendererClosing(1); | |
304 } | |
305 | |
306 TEST_F(PnaclHostTest, Incognito) { | |
307 nacl::PnaclCacheInfo info = GetTestCacheInfo(); | |
308 GET_NEXE_FD(0, 0, true, info, false); | |
309 FlushQueues(); | |
310 EXPECT_EQ(1, temp_callback_count_); | |
311 host_->TranslationFinished(0, 0, true); | |
312 FlushQueues(); | |
313 // Check that an incognito translation is not stored in the cache | |
314 GET_NEXE_FD(0, 0, false, info, false); | |
315 FlushQueues(); | |
316 EXPECT_EQ(2, temp_callback_count_); | |
317 host_->TranslationFinished(0, 0, true); | |
318 FlushQueues(); | |
319 // Check that an incognito translation can hit from a normal one. | |
320 GET_NEXE_FD(0, 0, true, info, true); | |
321 FlushQueues(); | |
322 EXPECT_EQ(3, temp_callback_count_); | |
323 } | |
324 | |
325 TEST_F(PnaclHostTest, IncognitoOverlappedMiss) { | |
326 nacl::PnaclCacheInfo info = GetTestCacheInfo(); | |
327 GET_NEXE_FD(0, 0, true, info, false); | |
328 GET_NEXE_FD(0, 1, false, info, false); | |
329 FlushQueues(); | |
330 // Check that both translations have returned misses, (i.e. that the | |
331 // second one has not blocked on the incognito one) | |
332 EXPECT_EQ(2, temp_callback_count_); | |
333 host_->TranslationFinished(0, 0, true); | |
334 host_->TranslationFinished(0, 1, true); | |
335 FlushQueues(); | |
336 EXPECT_EQ(0U, host_->pending_translations()); | |
337 | |
338 // Same test, but issue the 2nd request after the first has returned a miss. | |
339 info.abi_version = 222; | |
340 GET_NEXE_FD(0, 0, true, info, false); | |
341 FlushQueues(); | |
342 EXPECT_EQ(3, temp_callback_count_); | |
343 GET_NEXE_FD(0, 1, false, info, false); | |
344 FlushQueues(); | |
345 EXPECT_EQ(4, temp_callback_count_); | |
346 host_->RendererClosing(0); | |
347 } | |
348 | |
349 TEST_F(PnaclHostTest, IncognitoSecondOverlappedMiss) { | |
350 // If the non-incognito request comes first, it should | |
351 // behave exactly like OverlappedMissBeforeTempReturn | |
352 nacl::PnaclCacheInfo info = GetTestCacheInfo(); | |
353 GET_NEXE_FD(0, 0, false, info, false); | |
354 // Send the 2nd fd request before the first one returns a temp file. | |
355 GET_NEXE_FD(0, 1, true, info, true); | |
356 FlushQueues(); | |
357 EXPECT_EQ(1, temp_callback_count_); | |
358 EXPECT_EQ(2U, host_->pending_translations()); | |
359 FlushQueues(); | |
360 EXPECT_EQ(2U, host_->pending_translations()); | |
361 EXPECT_EQ(1, temp_callback_count_); | |
362 host_->TranslationFinished(0, 0, true); | |
363 FlushQueues(); | |
364 EXPECT_EQ(2, temp_callback_count_); | |
365 EXPECT_EQ(0U, host_->pending_translations()); | |
366 } | |
367 | |
368 // Test that pexes with the no-store header do not get cached. | |
369 TEST_F(PnaclHostTest, CacheControlNoStore) { | |
370 nacl::PnaclCacheInfo info = GetTestCacheInfo(); | |
371 info.has_no_store_header = true; | |
372 GET_NEXE_FD(0, 0, false, info, false); | |
373 FlushQueues(); | |
374 EXPECT_EQ(1, temp_callback_count_); | |
375 host_->TranslationFinished(0, 0, true); | |
376 FlushQueues(); | |
377 EXPECT_EQ(0U, host_->pending_translations()); | |
378 EXPECT_EQ(0, GetCacheSize()); | |
379 } | |
380 | |
381 // Test that no-store pexes do not wait, but do duplicate translations | |
382 TEST_F(PnaclHostTest, NoStoreOverlappedMiss) { | |
383 nacl::PnaclCacheInfo info = GetTestCacheInfo(); | |
384 info.has_no_store_header = true; | |
385 GET_NEXE_FD(0, 0, false, info, false); | |
386 GET_NEXE_FD(0, 1, false, info, false); | |
387 FlushQueues(); | |
388 // Check that both translations have returned misses, (i.e. that the | |
389 // second one has not blocked on the first one) | |
390 EXPECT_EQ(2, temp_callback_count_); | |
391 host_->TranslationFinished(0, 0, true); | |
392 host_->TranslationFinished(0, 1, true); | |
393 FlushQueues(); | |
394 EXPECT_EQ(0U, host_->pending_translations()); | |
395 | |
396 // Same test, but issue the 2nd request after the first has returned a miss. | |
397 info.abi_version = 222; | |
398 GET_NEXE_FD(0, 0, false, info, false); | |
399 FlushQueues(); | |
400 EXPECT_EQ(3, temp_callback_count_); | |
401 GET_NEXE_FD(0, 1, false, info, false); | |
402 FlushQueues(); | |
403 EXPECT_EQ(4, temp_callback_count_); | |
404 host_->RendererClosing(0); | |
405 } | |
406 | |
407 TEST_F(PnaclHostTest, ClearTranslationCache) { | |
408 nacl::PnaclCacheInfo info = GetTestCacheInfo(); | |
409 // Add 2 entries in the cache | |
410 GET_NEXE_FD(0, 0, false, info, false); | |
411 info.abi_version = 222; | |
412 GET_NEXE_FD(0, 1, false, info, false); | |
413 FlushQueues(); | |
414 EXPECT_EQ(2, temp_callback_count_); | |
415 host_->TranslationFinished(0, 0, true); | |
416 host_->TranslationFinished(0, 1, true); | |
417 FlushQueues(); | |
418 EXPECT_EQ(0U, host_->pending_translations()); | |
419 EXPECT_EQ(2, GetCacheSize()); | |
420 net::TestCompletionCallback cb; | |
421 // Since we are using a memory backend, the clear should happen immediately. | |
422 host_->ClearTranslationCacheEntriesBetween( | |
423 base::Time(), base::Time(), base::Bind(cb.callback(), 0)); | |
424 // Check that the translation cache has been cleared before flushing the | |
425 // queues, because the backend will be freed once it is. | |
426 EXPECT_EQ(0, GetCacheSize()); | |
427 EXPECT_EQ(0, cb.GetResult(net::ERR_IO_PENDING)); | |
428 // Now check that the backend has been freed. | |
429 EXPECT_FALSE(CacheIsInitialized()); | |
430 } | |
431 | |
432 } // namespace pnacl | |
OLD | NEW |