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 "components/nacl/browser/pnacl_host.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"
19 #define snprintf _snprintf
24 class PnaclHostTest
: public testing::Test
{
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(), true);
35 base::RunLoop().RunUntilIdle();
36 EXPECT_EQ(PnaclHost::CacheReady
, host_
->cache_state_
);
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);
43 EXPECT_EQ(PnaclHost::CacheUninitialized
, host_
->cache_state_
);
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.
50 content::BrowserThread::GetBlockingPool()->FlushForTesting();
51 base::RunLoop().RunUntilIdle();
52 content::BrowserThread::GetBlockingPool()->FlushForTesting();
53 base::RunLoop().RunUntilIdle();
55 int GetCacheSize() { return host_
->disk_cache_
->Size(); }
56 int CacheIsInitialized() {
57 return host_
->cache_state_
== PnaclHost::CacheReady
;
59 void ReInitBackend() {
60 host_
->InitForTest(temp_dir_
.path(), true);
61 base::RunLoop().RunUntilIdle();
62 EXPECT_EQ(PnaclHost::CacheReady
, host_
->cache_state_
);
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
70 void CallbackExpectMiss(const base::File
& file
, bool is_hit
) {
72 ASSERT_TRUE(file
.IsValid());
73 base::File::Info info
;
74 base::File
* mutable_file
= const_cast<base::File
*>(&file
);
75 EXPECT_TRUE(mutable_file
->GetInfo(&info
));
76 EXPECT_FALSE(info
.is_directory
);
77 EXPECT_EQ(0LL, info
.size
);
80 snprintf(str
, 16, "testdata%d", ++write_callback_count_
);
81 EXPECT_EQ(16, mutable_file
->Write(0, str
, 16));
82 temp_callback_count_
++;
84 void CallbackExpectHit(const base::File
& file
, bool is_hit
) {
86 ASSERT_TRUE(file
.IsValid());
87 base::File::Info info
;
88 base::File
* mutable_file
= const_cast<base::File
*>(&file
);
89 EXPECT_TRUE(mutable_file
->GetInfo(&info
));
90 EXPECT_FALSE(info
.is_directory
);
91 EXPECT_EQ(16LL, info
.size
);
93 memset(data
, 0x0, 16);
96 snprintf(str
, 16, "testdata%d", write_callback_count_
);
97 EXPECT_EQ(16, mutable_file
->Read(0, data
, 16));
98 EXPECT_STREQ(str
, data
);
99 temp_callback_count_
++;
104 int temp_callback_count_
;
105 int write_callback_count_
;
106 content::TestBrowserThreadBundle thread_bundle_
;
107 base::ScopedTempDir temp_dir_
;
110 static nacl::PnaclCacheInfo
GetTestCacheInfo() {
111 nacl::PnaclCacheInfo info
;
112 info
.pexe_url
= GURL("http://www.google.com");
113 info
.abi_version
= 0;
115 info
.has_no_store_header
= false;
119 #define GET_NEXE_FD(renderer, instance, incognito, info, expect_hit) \
124 0, /* ignore render_view_id for now */ \
128 base::Bind(expect_hit ? &PnaclHostTest::CallbackExpectHit \
129 : &PnaclHostTest::CallbackExpectMiss, \
130 base::Unretained(this))); \
133 TEST_F(PnaclHostTest
, BasicMiss
) {
134 nacl::PnaclCacheInfo info
= GetTestCacheInfo();
136 GET_NEXE_FD(0, 0, false, info
, false);
137 EXPECT_EQ(1U, host_
->pending_translations());
139 EXPECT_EQ(1U, host_
->pending_translations());
140 EXPECT_EQ(1, temp_callback_count_
);
141 host_
->TranslationFinished(0, 0, true);
143 EXPECT_EQ(0U, host_
->pending_translations());
144 // Test that a different cache info field also misses.
145 info
.etag
= std::string("something else");
146 GET_NEXE_FD(0, 0, false, info
, false);
148 EXPECT_EQ(2, temp_callback_count_
);
149 EXPECT_EQ(1U, host_
->pending_translations());
150 host_
->RendererClosing(0);
152 // Check that the cache has de-initialized after the last renderer goes away.
153 EXPECT_FALSE(CacheIsInitialized());
156 TEST_F(PnaclHostTest
, BadArguments
) {
157 nacl::PnaclCacheInfo info
= GetTestCacheInfo();
158 GET_NEXE_FD(0, 0, false, info
, false);
159 EXPECT_EQ(1U, host_
->pending_translations());
160 host_
->TranslationFinished(0, 1, true); // nonexistent translation
161 EXPECT_EQ(1U, host_
->pending_translations());
162 host_
->RendererClosing(1); // nonexistent renderer
163 EXPECT_EQ(1U, host_
->pending_translations());
165 EXPECT_EQ(1, temp_callback_count_
);
166 host_
->RendererClosing(0); // close without finishing
169 TEST_F(PnaclHostTest
, BasicHit
) {
170 nacl::PnaclCacheInfo info
= GetTestCacheInfo();
171 GET_NEXE_FD(0, 0, false, info
, false);
173 EXPECT_EQ(1, temp_callback_count_
);
174 host_
->TranslationFinished(0, 0, true);
176 GET_NEXE_FD(0, 1, false, info
, true);
178 EXPECT_EQ(2, temp_callback_count_
);
179 EXPECT_EQ(0U, host_
->pending_translations());
182 TEST_F(PnaclHostTest
, TranslationErrors
) {
183 nacl::PnaclCacheInfo info
= GetTestCacheInfo();
184 GET_NEXE_FD(0, 0, false, info
, false);
185 // Early abort, before temp file request returns
186 host_
->TranslationFinished(0, 0, false);
188 EXPECT_EQ(0U, host_
->pending_translations());
189 EXPECT_EQ(0, temp_callback_count_
);
190 // The backend will have been freed when the query comes back and there
191 // are no pending translations.
192 EXPECT_FALSE(CacheIsInitialized());
194 // Check that another request for the same info misses successfully.
195 GET_NEXE_FD(0, 0, false, info
, false);
197 host_
->TranslationFinished(0, 0, true);
199 EXPECT_EQ(1, temp_callback_count_
);
200 EXPECT_EQ(0U, host_
->pending_translations());
202 // Now try sending the error after the temp file request returns
203 info
.abi_version
= 222;
204 GET_NEXE_FD(0, 0, false, info
, false);
206 EXPECT_EQ(2, temp_callback_count_
);
207 host_
->TranslationFinished(0, 0, false);
209 EXPECT_EQ(0U, host_
->pending_translations());
210 // Check another successful miss
211 GET_NEXE_FD(0, 0, false, info
, false);
213 EXPECT_EQ(3, temp_callback_count_
);
214 host_
->TranslationFinished(0, 0, false);
215 EXPECT_EQ(0U, host_
->pending_translations());
218 TEST_F(PnaclHostTest
, OverlappedMissesAfterTempReturn
) {
219 nacl::PnaclCacheInfo info
= GetTestCacheInfo();
220 GET_NEXE_FD(0, 0, false, info
, false);
222 EXPECT_EQ(1, temp_callback_count_
);
223 EXPECT_EQ(1U, host_
->pending_translations());
224 // Test that a second request for the same nexe while the first one is still
225 // outstanding eventually hits.
226 GET_NEXE_FD(0, 1, false, info
, true);
228 EXPECT_EQ(2U, host_
->pending_translations());
229 // The temp file should not be returned to the second request until after the
230 // first is finished translating.
231 EXPECT_EQ(1, temp_callback_count_
);
232 host_
->TranslationFinished(0, 0, true);
234 EXPECT_EQ(2, temp_callback_count_
);
235 EXPECT_EQ(0U, host_
->pending_translations());
238 TEST_F(PnaclHostTest
, OverlappedMissesBeforeTempReturn
) {
239 nacl::PnaclCacheInfo info
= GetTestCacheInfo();
240 GET_NEXE_FD(0, 0, false, info
, false);
241 // Send the 2nd fd request before the first one returns a temp file.
242 GET_NEXE_FD(0, 1, false, info
, true);
244 EXPECT_EQ(1, temp_callback_count_
);
245 EXPECT_EQ(2U, host_
->pending_translations());
247 EXPECT_EQ(2U, host_
->pending_translations());
248 EXPECT_EQ(1, temp_callback_count_
);
249 host_
->TranslationFinished(0, 0, true);
251 EXPECT_EQ(2, temp_callback_count_
);
252 EXPECT_EQ(0U, host_
->pending_translations());
255 TEST_F(PnaclHostTest
, OverlappedHitsBeforeTempReturn
) {
256 nacl::PnaclCacheInfo info
= GetTestCacheInfo();
257 // Store one in the cache and complete it.
258 GET_NEXE_FD(0, 0, false, info
, false);
260 EXPECT_EQ(1, temp_callback_count_
);
261 host_
->TranslationFinished(0, 0, true);
263 EXPECT_EQ(0U, host_
->pending_translations());
264 GET_NEXE_FD(0, 0, false, info
, true);
265 // Request the second before the first temp file returns.
266 GET_NEXE_FD(0, 1, false, info
, true);
268 EXPECT_EQ(3, temp_callback_count_
);
269 EXPECT_EQ(0U, host_
->pending_translations());
272 TEST_F(PnaclHostTest
, OverlappedHitsAfterTempReturn
) {
273 nacl::PnaclCacheInfo info
= GetTestCacheInfo();
274 // Store one in the cache and complete it.
275 GET_NEXE_FD(0, 0, false, info
, false);
277 EXPECT_EQ(1, temp_callback_count_
);
278 host_
->TranslationFinished(0, 0, true);
280 EXPECT_EQ(0U, host_
->pending_translations());
281 GET_NEXE_FD(0, 0, false, info
, true);
283 GET_NEXE_FD(0, 1, false, info
, true);
285 EXPECT_EQ(3, temp_callback_count_
);
286 EXPECT_EQ(0U, host_
->pending_translations());
289 TEST_F(PnaclHostTest
, OverlappedMissesRendererClosing
) {
290 nacl::PnaclCacheInfo info
= GetTestCacheInfo();
291 GET_NEXE_FD(0, 0, false, info
, false);
292 // Send the 2nd fd request from a different renderer.
293 // Test that it eventually gets an fd after the first renderer closes.
294 GET_NEXE_FD(1, 1, false, info
, false);
296 EXPECT_EQ(1, temp_callback_count_
);
297 EXPECT_EQ(2U, host_
->pending_translations());
299 EXPECT_EQ(2U, host_
->pending_translations());
300 EXPECT_EQ(1, temp_callback_count_
);
301 host_
->RendererClosing(0);
303 EXPECT_EQ(2, temp_callback_count_
);
304 EXPECT_EQ(1U, host_
->pending_translations());
305 host_
->RendererClosing(1);
308 TEST_F(PnaclHostTest
, Incognito
) {
309 nacl::PnaclCacheInfo info
= GetTestCacheInfo();
310 GET_NEXE_FD(0, 0, true, info
, false);
312 EXPECT_EQ(1, temp_callback_count_
);
313 host_
->TranslationFinished(0, 0, true);
315 // Check that an incognito translation is not stored in the cache
316 GET_NEXE_FD(0, 0, false, info
, false);
318 EXPECT_EQ(2, temp_callback_count_
);
319 host_
->TranslationFinished(0, 0, true);
321 // Check that an incognito translation can hit from a normal one.
322 GET_NEXE_FD(0, 0, true, info
, true);
324 EXPECT_EQ(3, temp_callback_count_
);
327 TEST_F(PnaclHostTest
, IncognitoOverlappedMiss
) {
328 nacl::PnaclCacheInfo info
= GetTestCacheInfo();
329 GET_NEXE_FD(0, 0, true, info
, false);
330 GET_NEXE_FD(0, 1, false, info
, false);
332 // Check that both translations have returned misses, (i.e. that the
333 // second one has not blocked on the incognito one)
334 EXPECT_EQ(2, temp_callback_count_
);
335 host_
->TranslationFinished(0, 0, true);
336 host_
->TranslationFinished(0, 1, true);
338 EXPECT_EQ(0U, host_
->pending_translations());
340 // Same test, but issue the 2nd request after the first has returned a miss.
341 info
.abi_version
= 222;
342 GET_NEXE_FD(0, 0, true, info
, false);
344 EXPECT_EQ(3, temp_callback_count_
);
345 GET_NEXE_FD(0, 1, false, info
, false);
347 EXPECT_EQ(4, temp_callback_count_
);
348 host_
->RendererClosing(0);
351 TEST_F(PnaclHostTest
, IncognitoSecondOverlappedMiss
) {
352 // If the non-incognito request comes first, it should
353 // behave exactly like OverlappedMissBeforeTempReturn
354 nacl::PnaclCacheInfo info
= GetTestCacheInfo();
355 GET_NEXE_FD(0, 0, false, info
, false);
356 // Send the 2nd fd request before the first one returns a temp file.
357 GET_NEXE_FD(0, 1, true, info
, true);
359 EXPECT_EQ(1, temp_callback_count_
);
360 EXPECT_EQ(2U, host_
->pending_translations());
362 EXPECT_EQ(2U, host_
->pending_translations());
363 EXPECT_EQ(1, temp_callback_count_
);
364 host_
->TranslationFinished(0, 0, true);
366 EXPECT_EQ(2, temp_callback_count_
);
367 EXPECT_EQ(0U, host_
->pending_translations());
370 // Test that pexes with the no-store header do not get cached.
371 TEST_F(PnaclHostTest
, CacheControlNoStore
) {
372 nacl::PnaclCacheInfo info
= GetTestCacheInfo();
373 info
.has_no_store_header
= true;
374 GET_NEXE_FD(0, 0, false, info
, false);
376 EXPECT_EQ(1, temp_callback_count_
);
377 host_
->TranslationFinished(0, 0, true);
379 EXPECT_EQ(0U, host_
->pending_translations());
380 EXPECT_EQ(0, GetCacheSize());
383 // Test that no-store pexes do not wait, but do duplicate translations
384 TEST_F(PnaclHostTest
, NoStoreOverlappedMiss
) {
385 nacl::PnaclCacheInfo info
= GetTestCacheInfo();
386 info
.has_no_store_header
= true;
387 GET_NEXE_FD(0, 0, false, info
, false);
388 GET_NEXE_FD(0, 1, false, info
, false);
390 // Check that both translations have returned misses, (i.e. that the
391 // second one has not blocked on the first one)
392 EXPECT_EQ(2, temp_callback_count_
);
393 host_
->TranslationFinished(0, 0, true);
394 host_
->TranslationFinished(0, 1, true);
396 EXPECT_EQ(0U, host_
->pending_translations());
398 // Same test, but issue the 2nd request after the first has returned a miss.
399 info
.abi_version
= 222;
400 GET_NEXE_FD(0, 0, false, info
, false);
402 EXPECT_EQ(3, temp_callback_count_
);
403 GET_NEXE_FD(0, 1, false, info
, false);
405 EXPECT_EQ(4, temp_callback_count_
);
406 host_
->RendererClosing(0);
409 TEST_F(PnaclHostTest
, ClearTranslationCache
) {
410 nacl::PnaclCacheInfo info
= GetTestCacheInfo();
411 // Add 2 entries in the cache
412 GET_NEXE_FD(0, 0, false, info
, false);
413 info
.abi_version
= 222;
414 GET_NEXE_FD(0, 1, false, info
, false);
416 EXPECT_EQ(2, temp_callback_count_
);
417 host_
->TranslationFinished(0, 0, true);
418 host_
->TranslationFinished(0, 1, true);
420 EXPECT_EQ(0U, host_
->pending_translations());
421 EXPECT_EQ(2, GetCacheSize());
422 net::TestCompletionCallback cb
;
423 // Since we are using a memory backend, the clear should happen immediately.
424 host_
->ClearTranslationCacheEntriesBetween(
425 base::Time(), base::Time(), base::Bind(cb
.callback(), 0));
426 // Check that the translation cache has been cleared before flushing the
427 // queues, because the backend will be freed once it is.
428 EXPECT_EQ(0, GetCacheSize());
429 EXPECT_EQ(0, cb
.GetResult(net::ERR_IO_PENDING
));
430 // Now check that the backend has been freed.
431 EXPECT_FALSE(CacheIsInitialized());
434 // A version of PnaclHostTest that initializes cache on disk.
435 class PnaclHostTestDisk
: public PnaclHostTest
{
437 virtual void SetUp() {
438 host_
= new PnaclHost();
439 ASSERT_TRUE(temp_dir_
.CreateUniqueTempDir());
440 host_
->InitForTest(temp_dir_
.path(), false);
441 EXPECT_EQ(PnaclHost::CacheInitializing
, host_
->cache_state_
);
444 host_
->DeInitIfSafe();
447 TEST_F(PnaclHostTestDisk
, DeInitWhileInitializing
) {
448 // Since there's no easy way to pump message queues one message at a time, we
449 // have to simulate what would happen if 1 DeInitIfsafe task gets queued, then
450 // a GetNexeFd gets queued, and then another DeInitIfSafe gets queued before
451 // the first one runs. We can just shortcut and call DeInitIfSafe while the
452 // cache is still initializing.
454 base::RunLoop().RunUntilIdle();