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"
10 #include "base/bind.h"
11 #include "base/files/scoped_temp_dir.h"
12 #include "base/run_loop.h"
13 #include "base/threading/sequenced_worker_pool.h"
14 #include "components/nacl/browser/pnacl_translation_cache.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "content/public/test/test_browser_thread_bundle.h"
17 #include "content/public/test/test_utils.h"
18 #include "net/base/test_completion_callback.h"
19 #include "testing/gtest/include/gtest/gtest.h"
22 #define snprintf _snprintf
28 // Size of a buffer used for writing and reading from a file.
29 const size_t kBufferSize
= 16u;
33 class PnaclHostTest
: public testing::Test
{
37 temp_callback_count_(0),
38 write_callback_count_(0),
39 thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP
) {}
40 void SetUp() override
{
41 host_
= new PnaclHost();
42 ASSERT_TRUE(temp_dir_
.CreateUniqueTempDir());
43 host_
->InitForTest(temp_dir_
.path(), true);
44 base::RunLoop().RunUntilIdle();
45 EXPECT_EQ(PnaclHost::CacheReady
, host_
->cache_state_
);
47 void TearDown() override
{
48 EXPECT_EQ(0U, host_
->pending_translations());
49 // Give the host a chance to de-init the backend, and then delete it.
50 host_
->RendererClosing(0);
51 content::RunAllBlockingPoolTasksUntilIdle();
52 EXPECT_EQ(PnaclHost::CacheUninitialized
, host_
->cache_state_
);
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
);
78 char str
[kBufferSize
];
79 memset(str
, 0x0, kBufferSize
);
80 snprintf(str
, kBufferSize
, "testdata%d", ++write_callback_count_
);
81 EXPECT_EQ(kBufferSize
,
82 static_cast<size_t>(mutable_file
->Write(0, str
, kBufferSize
)));
83 temp_callback_count_
++;
85 void CallbackExpectHit(const base::File
& file
, bool is_hit
) {
87 ASSERT_TRUE(file
.IsValid());
88 base::File::Info info
;
89 base::File
* mutable_file
= const_cast<base::File
*>(&file
);
90 EXPECT_TRUE(mutable_file
->GetInfo(&info
));
91 EXPECT_FALSE(info
.is_directory
);
92 EXPECT_EQ(kBufferSize
, static_cast<size_t>(info
.size
));
93 char data
[kBufferSize
];
94 memset(data
, 0x0, kBufferSize
);
95 char str
[kBufferSize
];
96 memset(str
, 0x0, kBufferSize
);
97 snprintf(str
, kBufferSize
, "testdata%d", write_callback_count_
);
98 EXPECT_EQ(kBufferSize
,
99 static_cast<size_t>(mutable_file
->Read(0, data
, kBufferSize
)));
100 EXPECT_STREQ(str
, data
);
101 temp_callback_count_
++;
106 int temp_callback_count_
;
107 int write_callback_count_
;
108 content::TestBrowserThreadBundle thread_bundle_
;
109 base::ScopedTempDir temp_dir_
;
112 static nacl::PnaclCacheInfo
GetTestCacheInfo() {
113 nacl::PnaclCacheInfo info
;
114 info
.pexe_url
= GURL("http://www.google.com");
115 info
.abi_version
= 0;
117 info
.has_no_store_header
= false;
121 #define GET_NEXE_FD(renderer, instance, incognito, info, expect_hit) \
126 0, /* ignore render_view_id for now */ \
130 base::Bind(expect_hit ? &PnaclHostTest::CallbackExpectHit \
131 : &PnaclHostTest::CallbackExpectMiss, \
132 base::Unretained(this))); \
135 TEST_F(PnaclHostTest
, BasicMiss
) {
136 nacl::PnaclCacheInfo info
= GetTestCacheInfo();
138 GET_NEXE_FD(0, 0, false, info
, false);
139 EXPECT_EQ(1U, host_
->pending_translations());
140 content::RunAllBlockingPoolTasksUntilIdle();
141 EXPECT_EQ(1U, host_
->pending_translations());
142 EXPECT_EQ(1, temp_callback_count_
);
143 host_
->TranslationFinished(0, 0, true);
144 content::RunAllBlockingPoolTasksUntilIdle();
145 EXPECT_EQ(0U, host_
->pending_translations());
146 // Test that a different cache info field also misses.
147 info
.etag
= std::string("something else");
148 GET_NEXE_FD(0, 0, false, info
, false);
149 content::RunAllBlockingPoolTasksUntilIdle();
150 EXPECT_EQ(2, temp_callback_count_
);
151 EXPECT_EQ(1U, host_
->pending_translations());
152 host_
->RendererClosing(0);
153 content::RunAllBlockingPoolTasksUntilIdle();
154 // Check that the cache has de-initialized after the last renderer goes away.
155 EXPECT_FALSE(CacheIsInitialized());
158 TEST_F(PnaclHostTest
, BadArguments
) {
159 nacl::PnaclCacheInfo info
= GetTestCacheInfo();
160 GET_NEXE_FD(0, 0, false, info
, false);
161 EXPECT_EQ(1U, host_
->pending_translations());
162 host_
->TranslationFinished(0, 1, true); // nonexistent translation
163 EXPECT_EQ(1U, host_
->pending_translations());
164 host_
->RendererClosing(1); // nonexistent renderer
165 EXPECT_EQ(1U, host_
->pending_translations());
166 content::RunAllBlockingPoolTasksUntilIdle();
167 EXPECT_EQ(1, temp_callback_count_
);
168 host_
->RendererClosing(0); // close without finishing
171 TEST_F(PnaclHostTest
, BasicHit
) {
172 nacl::PnaclCacheInfo info
= GetTestCacheInfo();
173 GET_NEXE_FD(0, 0, false, info
, false);
174 content::RunAllBlockingPoolTasksUntilIdle();
175 EXPECT_EQ(1, temp_callback_count_
);
176 host_
->TranslationFinished(0, 0, true);
177 content::RunAllBlockingPoolTasksUntilIdle();
178 GET_NEXE_FD(0, 1, false, info
, true);
179 content::RunAllBlockingPoolTasksUntilIdle();
180 EXPECT_EQ(2, temp_callback_count_
);
181 EXPECT_EQ(0U, host_
->pending_translations());
184 TEST_F(PnaclHostTest
, TranslationErrors
) {
185 nacl::PnaclCacheInfo info
= GetTestCacheInfo();
186 GET_NEXE_FD(0, 0, false, info
, false);
187 // Early abort, before temp file request returns
188 host_
->TranslationFinished(0, 0, false);
189 content::RunAllBlockingPoolTasksUntilIdle();
190 EXPECT_EQ(0U, host_
->pending_translations());
191 EXPECT_EQ(0, temp_callback_count_
);
192 // The backend will have been freed when the query comes back and there
193 // are no pending translations.
194 EXPECT_FALSE(CacheIsInitialized());
196 // Check that another request for the same info misses successfully.
197 GET_NEXE_FD(0, 0, false, info
, false);
198 content::RunAllBlockingPoolTasksUntilIdle();
199 host_
->TranslationFinished(0, 0, true);
200 content::RunAllBlockingPoolTasksUntilIdle();
201 EXPECT_EQ(1, temp_callback_count_
);
202 EXPECT_EQ(0U, host_
->pending_translations());
204 // Now try sending the error after the temp file request returns
205 info
.abi_version
= 222;
206 GET_NEXE_FD(0, 0, false, info
, false);
207 content::RunAllBlockingPoolTasksUntilIdle();
208 EXPECT_EQ(2, temp_callback_count_
);
209 host_
->TranslationFinished(0, 0, false);
210 content::RunAllBlockingPoolTasksUntilIdle();
211 EXPECT_EQ(0U, host_
->pending_translations());
212 // Check another successful miss
213 GET_NEXE_FD(0, 0, false, info
, false);
214 content::RunAllBlockingPoolTasksUntilIdle();
215 EXPECT_EQ(3, temp_callback_count_
);
216 host_
->TranslationFinished(0, 0, false);
217 EXPECT_EQ(0U, host_
->pending_translations());
220 TEST_F(PnaclHostTest
, OverlappedMissesAfterTempReturn
) {
221 nacl::PnaclCacheInfo info
= GetTestCacheInfo();
222 GET_NEXE_FD(0, 0, false, info
, false);
223 content::RunAllBlockingPoolTasksUntilIdle();
224 EXPECT_EQ(1, temp_callback_count_
);
225 EXPECT_EQ(1U, host_
->pending_translations());
226 // Test that a second request for the same nexe while the first one is still
227 // outstanding eventually hits.
228 GET_NEXE_FD(0, 1, false, info
, true);
229 content::RunAllBlockingPoolTasksUntilIdle();
230 EXPECT_EQ(2U, host_
->pending_translations());
231 // The temp file should not be returned to the second request until after the
232 // first is finished translating.
233 EXPECT_EQ(1, temp_callback_count_
);
234 host_
->TranslationFinished(0, 0, true);
235 content::RunAllBlockingPoolTasksUntilIdle();
236 EXPECT_EQ(2, temp_callback_count_
);
237 EXPECT_EQ(0U, host_
->pending_translations());
240 TEST_F(PnaclHostTest
, OverlappedMissesBeforeTempReturn
) {
241 nacl::PnaclCacheInfo info
= GetTestCacheInfo();
242 GET_NEXE_FD(0, 0, false, info
, false);
243 // Send the 2nd fd request before the first one returns a temp file.
244 GET_NEXE_FD(0, 1, false, info
, true);
245 content::RunAllBlockingPoolTasksUntilIdle();
246 EXPECT_EQ(1, temp_callback_count_
);
247 EXPECT_EQ(2U, host_
->pending_translations());
248 content::RunAllBlockingPoolTasksUntilIdle();
249 EXPECT_EQ(2U, host_
->pending_translations());
250 EXPECT_EQ(1, temp_callback_count_
);
251 host_
->TranslationFinished(0, 0, true);
252 content::RunAllBlockingPoolTasksUntilIdle();
253 EXPECT_EQ(2, temp_callback_count_
);
254 EXPECT_EQ(0U, host_
->pending_translations());
257 TEST_F(PnaclHostTest
, OverlappedHitsBeforeTempReturn
) {
258 nacl::PnaclCacheInfo info
= GetTestCacheInfo();
259 // Store one in the cache and complete it.
260 GET_NEXE_FD(0, 0, false, info
, false);
261 content::RunAllBlockingPoolTasksUntilIdle();
262 EXPECT_EQ(1, temp_callback_count_
);
263 host_
->TranslationFinished(0, 0, true);
264 content::RunAllBlockingPoolTasksUntilIdle();
265 EXPECT_EQ(0U, host_
->pending_translations());
266 GET_NEXE_FD(0, 0, false, info
, true);
267 // Request the second before the first temp file returns.
268 GET_NEXE_FD(0, 1, false, info
, true);
269 content::RunAllBlockingPoolTasksUntilIdle();
270 EXPECT_EQ(3, temp_callback_count_
);
271 EXPECT_EQ(0U, host_
->pending_translations());
274 TEST_F(PnaclHostTest
, OverlappedHitsAfterTempReturn
) {
275 nacl::PnaclCacheInfo info
= GetTestCacheInfo();
276 // Store one in the cache and complete it.
277 GET_NEXE_FD(0, 0, false, info
, false);
278 content::RunAllBlockingPoolTasksUntilIdle();
279 EXPECT_EQ(1, temp_callback_count_
);
280 host_
->TranslationFinished(0, 0, true);
281 content::RunAllBlockingPoolTasksUntilIdle();
282 EXPECT_EQ(0U, host_
->pending_translations());
283 GET_NEXE_FD(0, 0, false, info
, true);
284 content::RunAllBlockingPoolTasksUntilIdle();
285 GET_NEXE_FD(0, 1, false, info
, true);
286 content::RunAllBlockingPoolTasksUntilIdle();
287 EXPECT_EQ(3, temp_callback_count_
);
288 EXPECT_EQ(0U, host_
->pending_translations());
291 TEST_F(PnaclHostTest
, OverlappedMissesRendererClosing
) {
292 nacl::PnaclCacheInfo info
= GetTestCacheInfo();
293 GET_NEXE_FD(0, 0, false, info
, false);
294 // Send the 2nd fd request from a different renderer.
295 // Test that it eventually gets an fd after the first renderer closes.
296 GET_NEXE_FD(1, 1, false, info
, false);
297 content::RunAllBlockingPoolTasksUntilIdle();
298 EXPECT_EQ(1, temp_callback_count_
);
299 EXPECT_EQ(2U, host_
->pending_translations());
300 content::RunAllBlockingPoolTasksUntilIdle();
301 EXPECT_EQ(2U, host_
->pending_translations());
302 EXPECT_EQ(1, temp_callback_count_
);
303 host_
->RendererClosing(0);
304 content::RunAllBlockingPoolTasksUntilIdle();
305 EXPECT_EQ(2, temp_callback_count_
);
306 EXPECT_EQ(1U, host_
->pending_translations());
307 host_
->RendererClosing(1);
310 TEST_F(PnaclHostTest
, Incognito
) {
311 nacl::PnaclCacheInfo info
= GetTestCacheInfo();
312 GET_NEXE_FD(0, 0, true, info
, false);
313 content::RunAllBlockingPoolTasksUntilIdle();
314 EXPECT_EQ(1, temp_callback_count_
);
315 host_
->TranslationFinished(0, 0, true);
316 content::RunAllBlockingPoolTasksUntilIdle();
317 // Check that an incognito translation is not stored in the cache
318 GET_NEXE_FD(0, 0, false, info
, false);
319 content::RunAllBlockingPoolTasksUntilIdle();
320 EXPECT_EQ(2, temp_callback_count_
);
321 host_
->TranslationFinished(0, 0, true);
322 content::RunAllBlockingPoolTasksUntilIdle();
323 // Check that an incognito translation can hit from a normal one.
324 GET_NEXE_FD(0, 0, true, info
, true);
325 content::RunAllBlockingPoolTasksUntilIdle();
326 EXPECT_EQ(3, temp_callback_count_
);
329 TEST_F(PnaclHostTest
, IncognitoOverlappedMiss
) {
330 nacl::PnaclCacheInfo info
= GetTestCacheInfo();
331 GET_NEXE_FD(0, 0, true, info
, false);
332 GET_NEXE_FD(0, 1, false, info
, false);
333 content::RunAllBlockingPoolTasksUntilIdle();
334 // Check that both translations have returned misses, (i.e. that the
335 // second one has not blocked on the incognito one)
336 EXPECT_EQ(2, temp_callback_count_
);
337 host_
->TranslationFinished(0, 0, true);
338 host_
->TranslationFinished(0, 1, true);
339 content::RunAllBlockingPoolTasksUntilIdle();
340 EXPECT_EQ(0U, host_
->pending_translations());
342 // Same test, but issue the 2nd request after the first has returned a miss.
343 info
.abi_version
= 222;
344 GET_NEXE_FD(0, 0, true, info
, false);
345 content::RunAllBlockingPoolTasksUntilIdle();
346 EXPECT_EQ(3, temp_callback_count_
);
347 GET_NEXE_FD(0, 1, false, info
, false);
348 content::RunAllBlockingPoolTasksUntilIdle();
349 EXPECT_EQ(4, temp_callback_count_
);
350 host_
->RendererClosing(0);
353 TEST_F(PnaclHostTest
, IncognitoSecondOverlappedMiss
) {
354 // If the non-incognito request comes first, it should
355 // behave exactly like OverlappedMissBeforeTempReturn
356 nacl::PnaclCacheInfo info
= GetTestCacheInfo();
357 GET_NEXE_FD(0, 0, false, info
, false);
358 // Send the 2nd fd request before the first one returns a temp file.
359 GET_NEXE_FD(0, 1, true, info
, true);
360 content::RunAllBlockingPoolTasksUntilIdle();
361 EXPECT_EQ(1, temp_callback_count_
);
362 EXPECT_EQ(2U, host_
->pending_translations());
363 content::RunAllBlockingPoolTasksUntilIdle();
364 EXPECT_EQ(2U, host_
->pending_translations());
365 EXPECT_EQ(1, temp_callback_count_
);
366 host_
->TranslationFinished(0, 0, true);
367 content::RunAllBlockingPoolTasksUntilIdle();
368 EXPECT_EQ(2, temp_callback_count_
);
369 EXPECT_EQ(0U, host_
->pending_translations());
372 // Test that pexes with the no-store header do not get cached.
373 TEST_F(PnaclHostTest
, CacheControlNoStore
) {
374 nacl::PnaclCacheInfo info
= GetTestCacheInfo();
375 info
.has_no_store_header
= true;
376 GET_NEXE_FD(0, 0, false, info
, false);
377 content::RunAllBlockingPoolTasksUntilIdle();
378 EXPECT_EQ(1, temp_callback_count_
);
379 host_
->TranslationFinished(0, 0, true);
380 content::RunAllBlockingPoolTasksUntilIdle();
381 EXPECT_EQ(0U, host_
->pending_translations());
382 EXPECT_EQ(0, GetCacheSize());
385 // Test that no-store pexes do not wait, but do duplicate translations
386 TEST_F(PnaclHostTest
, NoStoreOverlappedMiss
) {
387 nacl::PnaclCacheInfo info
= GetTestCacheInfo();
388 info
.has_no_store_header
= true;
389 GET_NEXE_FD(0, 0, false, info
, false);
390 GET_NEXE_FD(0, 1, false, info
, false);
391 content::RunAllBlockingPoolTasksUntilIdle();
392 // Check that both translations have returned misses, (i.e. that the
393 // second one has not blocked on the first one)
394 EXPECT_EQ(2, temp_callback_count_
);
395 host_
->TranslationFinished(0, 0, true);
396 host_
->TranslationFinished(0, 1, true);
397 content::RunAllBlockingPoolTasksUntilIdle();
398 EXPECT_EQ(0U, host_
->pending_translations());
400 // Same test, but issue the 2nd request after the first has returned a miss.
401 info
.abi_version
= 222;
402 GET_NEXE_FD(0, 0, false, info
, false);
403 content::RunAllBlockingPoolTasksUntilIdle();
404 EXPECT_EQ(3, temp_callback_count_
);
405 GET_NEXE_FD(0, 1, false, info
, false);
406 content::RunAllBlockingPoolTasksUntilIdle();
407 EXPECT_EQ(4, temp_callback_count_
);
408 host_
->RendererClosing(0);
411 TEST_F(PnaclHostTest
, ClearTranslationCache
) {
412 nacl::PnaclCacheInfo info
= GetTestCacheInfo();
413 // Add 2 entries in the cache
414 GET_NEXE_FD(0, 0, false, info
, false);
415 info
.abi_version
= 222;
416 GET_NEXE_FD(0, 1, false, info
, false);
417 content::RunAllBlockingPoolTasksUntilIdle();
418 EXPECT_EQ(2, temp_callback_count_
);
419 host_
->TranslationFinished(0, 0, true);
420 host_
->TranslationFinished(0, 1, true);
421 content::RunAllBlockingPoolTasksUntilIdle();
422 EXPECT_EQ(0U, host_
->pending_translations());
423 EXPECT_EQ(2, GetCacheSize());
424 net::TestCompletionCallback cb
;
425 // Since we are using a memory backend, the clear should happen immediately.
426 host_
->ClearTranslationCacheEntriesBetween(
427 base::Time(), base::Time(), base::Bind(cb
.callback(), 0));
428 // Check that the translation cache has been cleared before flushing the
429 // queues, because the backend will be freed once it is.
430 EXPECT_EQ(0, GetCacheSize());
431 EXPECT_EQ(0, cb
.GetResult(net::ERR_IO_PENDING
));
432 // Now check that the backend has been freed.
433 EXPECT_FALSE(CacheIsInitialized());
436 // A version of PnaclHostTest that initializes cache on disk.
437 class PnaclHostTestDisk
: public PnaclHostTest
{
439 void SetUp() override
{
440 host_
= new PnaclHost();
441 ASSERT_TRUE(temp_dir_
.CreateUniqueTempDir());
442 host_
->InitForTest(temp_dir_
.path(), false);
443 EXPECT_EQ(PnaclHost::CacheInitializing
, host_
->cache_state_
);
446 host_
->DeInitIfSafe();
449 TEST_F(PnaclHostTestDisk
, DeInitWhileInitializing
) {
450 // Since there's no easy way to pump message queues one message at a time, we
451 // have to simulate what would happen if 1 DeInitIfsafe task gets queued, then
452 // a GetNexeFd gets queued, and then another DeInitIfSafe gets queued before
453 // the first one runs. We can just shortcut and call DeInitIfSafe while the
454 // cache is still initializing.
456 base::RunLoop().RunUntilIdle();