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(base::PlatformFile fd, bool 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);
79 snprintf(str, 16, "testdata%d", ++write_callback_count_);
80 EXPECT_EQ(16, base::WritePlatformFile(fd, 0, str, 16));
81 temp_callback_count_++;
83 void CallbackExpectHit(base::PlatformFile fd, bool 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);
91 memset(data, 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_++;
102 int temp_callback_count_;
103 int write_callback_count_;
104 content::TestBrowserThreadBundle thread_bundle_;
105 base::ScopedTempDir temp_dir_;
108 static nacl::PnaclCacheInfo GetTestCacheInfo() {
109 nacl::PnaclCacheInfo info;
110 info.pexe_url = GURL("http://www.google.com");
111 info.abi_version = 0;
113 info.has_no_store_header = false;
117 #define GET_NEXE_FD(renderer, instance, incognito, info, expect_hit) \
122 0, /* ignore render_view_id for now */ \
126 base::Bind(expect_hit ? &PnaclHostTest::CallbackExpectHit \
127 : &PnaclHostTest::CallbackExpectMiss, \
128 base::Unretained(this))); \
131 TEST_F(PnaclHostTest, BasicMiss) {
132 nacl::PnaclCacheInfo info = GetTestCacheInfo();
134 GET_NEXE_FD(0, 0, false, info, false);
135 EXPECT_EQ(1U, host_->pending_translations());
137 EXPECT_EQ(1U, host_->pending_translations());
138 EXPECT_EQ(1, temp_callback_count_);
139 host_->TranslationFinished(0, 0, true);
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);
146 EXPECT_EQ(2, temp_callback_count_);
147 EXPECT_EQ(1U, host_->pending_translations());
148 host_->RendererClosing(0);
150 // Check that the cache has de-initialized after the last renderer goes away.
151 EXPECT_FALSE(CacheIsInitialized());
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());
163 EXPECT_EQ(1, temp_callback_count_);
164 host_->RendererClosing(0); // close without finishing
167 TEST_F(PnaclHostTest, BasicHit) {
168 nacl::PnaclCacheInfo info = GetTestCacheInfo();
169 GET_NEXE_FD(0, 0, false, info, false);
171 EXPECT_EQ(1, temp_callback_count_);
172 host_->TranslationFinished(0, 0, true);
174 GET_NEXE_FD(0, 1, false, info, true);
176 EXPECT_EQ(2, temp_callback_count_);
177 EXPECT_EQ(0U, host_->pending_translations());
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);
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());
192 // Check that another request for the same info misses successfully.
193 GET_NEXE_FD(0, 0, false, info, false);
195 host_->TranslationFinished(0, 0, true);
197 EXPECT_EQ(1, temp_callback_count_);
198 EXPECT_EQ(0U, host_->pending_translations());
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);
204 EXPECT_EQ(2, temp_callback_count_);
205 host_->TranslationFinished(0, 0, false);
207 EXPECT_EQ(0U, host_->pending_translations());
208 // Check another successful miss
209 GET_NEXE_FD(0, 0, false, info, false);
211 EXPECT_EQ(3, temp_callback_count_);
212 host_->TranslationFinished(0, 0, false);
213 EXPECT_EQ(0U, host_->pending_translations());
216 TEST_F(PnaclHostTest, OverlappedMissesAfterTempReturn) {
217 nacl::PnaclCacheInfo info = GetTestCacheInfo();
218 GET_NEXE_FD(0, 0, false, info, false);
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);
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);
232 EXPECT_EQ(2, temp_callback_count_);
233 EXPECT_EQ(0U, host_->pending_translations());
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);
242 EXPECT_EQ(1, temp_callback_count_);
243 EXPECT_EQ(2U, host_->pending_translations());
245 EXPECT_EQ(2U, host_->pending_translations());
246 EXPECT_EQ(1, temp_callback_count_);
247 host_->TranslationFinished(0, 0, true);
249 EXPECT_EQ(2, temp_callback_count_);
250 EXPECT_EQ(0U, host_->pending_translations());
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);
258 EXPECT_EQ(1, temp_callback_count_);
259 host_->TranslationFinished(0, 0, true);
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);
266 EXPECT_EQ(3, temp_callback_count_);
267 EXPECT_EQ(0U, host_->pending_translations());
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);
275 EXPECT_EQ(1, temp_callback_count_);
276 host_->TranslationFinished(0, 0, true);
278 EXPECT_EQ(0U, host_->pending_translations());
279 GET_NEXE_FD(0, 0, false, info, true);
281 GET_NEXE_FD(0, 1, false, info, true);
283 EXPECT_EQ(3, temp_callback_count_);
284 EXPECT_EQ(0U, host_->pending_translations());
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);
294 EXPECT_EQ(1, temp_callback_count_);
295 EXPECT_EQ(2U, host_->pending_translations());
297 EXPECT_EQ(2U, host_->pending_translations());
298 EXPECT_EQ(1, temp_callback_count_);
299 host_->RendererClosing(0);
301 EXPECT_EQ(2, temp_callback_count_);
302 EXPECT_EQ(1U, host_->pending_translations());
303 host_->RendererClosing(1);
306 TEST_F(PnaclHostTest, Incognito) {
307 nacl::PnaclCacheInfo info = GetTestCacheInfo();
308 GET_NEXE_FD(0, 0, true, info, false);
310 EXPECT_EQ(1, temp_callback_count_);
311 host_->TranslationFinished(0, 0, true);
313 // Check that an incognito translation is not stored in the cache
314 GET_NEXE_FD(0, 0, false, info, false);
316 EXPECT_EQ(2, temp_callback_count_);
317 host_->TranslationFinished(0, 0, true);
319 // Check that an incognito translation can hit from a normal one.
320 GET_NEXE_FD(0, 0, true, info, true);
322 EXPECT_EQ(3, temp_callback_count_);
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);
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);
336 EXPECT_EQ(0U, host_->pending_translations());
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);
342 EXPECT_EQ(3, temp_callback_count_);
343 GET_NEXE_FD(0, 1, false, info, false);
345 EXPECT_EQ(4, temp_callback_count_);
346 host_->RendererClosing(0);
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);
357 EXPECT_EQ(1, temp_callback_count_);
358 EXPECT_EQ(2U, host_->pending_translations());
360 EXPECT_EQ(2U, host_->pending_translations());
361 EXPECT_EQ(1, temp_callback_count_);
362 host_->TranslationFinished(0, 0, true);
364 EXPECT_EQ(2, temp_callback_count_);
365 EXPECT_EQ(0U, host_->pending_translations());
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);
374 EXPECT_EQ(1, temp_callback_count_);
375 host_->TranslationFinished(0, 0, true);
377 EXPECT_EQ(0U, host_->pending_translations());
378 EXPECT_EQ(0, GetCacheSize());
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);
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);
394 EXPECT_EQ(0U, host_->pending_translations());
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);
400 EXPECT_EQ(3, temp_callback_count_);
401 GET_NEXE_FD(0, 1, false, info, false);
403 EXPECT_EQ(4, temp_callback_count_);
404 host_->RendererClosing(0);
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);
414 EXPECT_EQ(2, temp_callback_count_);
415 host_->TranslationFinished(0, 0, true);
416 host_->TranslationFinished(0, 1, true);
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());
432 // A version of PnaclHostTest that initializes cache on disk.
433 class PnaclHostTestDisk : public PnaclHostTest {
435 virtual void SetUp() {
436 host_ = new PnaclHost();
437 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
438 host_->InitForTest(temp_dir_.path(), false);
439 EXPECT_EQ(PnaclHost::CacheInitializing, host_->cache_state_);
442 host_->DeInitIfSafe();
445 TEST_F(PnaclHostTestDisk, DeInitWhileInitializing) {
446 // Since there's no easy way to pump message queues one message at a time, we
447 // have to simulate what would happen if 1 DeInitIfsafe task gets queued, then
448 // a GetNexeFd gets queued, and then another DeInitIfSafe gets queued before
449 // the first one runs. We can just shortcut and call DeInitIfSafe while the
450 // cache is still initializing.
452 base::RunLoop().RunUntilIdle();