• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "components/nacl/browser/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:
PnaclHostTest()26   PnaclHostTest()
27       : host_(NULL),
28         temp_callback_count_(0),
29         write_callback_count_(0),
30         thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {}
SetUp()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_);
37   }
TearDown()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.
FlushQueues()49   void FlushQueues() {
50     content::BrowserThread::GetBlockingPool()->FlushForTesting();
51     base::RunLoop().RunUntilIdle();
52     content::BrowserThread::GetBlockingPool()->FlushForTesting();
53     base::RunLoop().RunUntilIdle();
54   }
GetCacheSize()55   int GetCacheSize() { return host_->disk_cache_->Size(); }
CacheIsInitialized()56   int CacheIsInitialized() {
57     return host_->cache_state_ == PnaclHost::CacheReady;
58   }
ReInitBackend()59   void ReInitBackend() {
60     host_->InitForTest(temp_dir_.path(), true);
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
CallbackExpectMiss(const base::File & file,bool is_hit)70   void CallbackExpectMiss(const base::File& file, bool is_hit) {
71     EXPECT_FALSE(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[16];
79     memset(str, 0x0, 16);
80     snprintf(str, 16, "testdata%d", ++write_callback_count_);
81     EXPECT_EQ(16, mutable_file->Write(0, str, 16));
82     temp_callback_count_++;
83   }
CallbackExpectHit(const base::File & file,bool is_hit)84   void CallbackExpectHit(const base::File& file, bool is_hit) {
85     EXPECT_TRUE(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);
92     char data[16];
93     memset(data, 0x0, 16);
94     char str[16];
95     memset(str, 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_++;
100   }
101 
102  protected:
103   PnaclHost* host_;
104   int temp_callback_count_;
105   int write_callback_count_;
106   content::TestBrowserThreadBundle thread_bundle_;
107   base::ScopedTempDir temp_dir_;
108 };
109 
GetTestCacheInfo()110 static nacl::PnaclCacheInfo GetTestCacheInfo() {
111   nacl::PnaclCacheInfo info;
112   info.pexe_url = GURL("http://www.google.com");
113   info.abi_version = 0;
114   info.opt_level = 0;
115   info.has_no_store_header = false;
116   return info;
117 }
118 
119 #define GET_NEXE_FD(renderer, instance, incognito, info, expect_hit) \
120   do {                                                               \
121     SCOPED_TRACE("");                                                \
122     host_->GetNexeFd(                                                \
123         renderer,                                                    \
124         0, /* ignore render_view_id for now */                       \
125         instance,                                                    \
126         incognito,                                                   \
127         info,                                                        \
128         base::Bind(expect_hit ? &PnaclHostTest::CallbackExpectHit    \
129                               : &PnaclHostTest::CallbackExpectMiss,  \
130                    base::Unretained(this)));                         \
131   } while (0)
132 
TEST_F(PnaclHostTest,BasicMiss)133 TEST_F(PnaclHostTest, BasicMiss) {
134   nacl::PnaclCacheInfo info = GetTestCacheInfo();
135   // Test cold miss.
136   GET_NEXE_FD(0, 0, false, info, false);
137   EXPECT_EQ(1U, host_->pending_translations());
138   FlushQueues();
139   EXPECT_EQ(1U, host_->pending_translations());
140   EXPECT_EQ(1, temp_callback_count_);
141   host_->TranslationFinished(0, 0, true);
142   FlushQueues();
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);
147   FlushQueues();
148   EXPECT_EQ(2, temp_callback_count_);
149   EXPECT_EQ(1U, host_->pending_translations());
150   host_->RendererClosing(0);
151   FlushQueues();
152   // Check that the cache has de-initialized after the last renderer goes away.
153   EXPECT_FALSE(CacheIsInitialized());
154 }
155 
TEST_F(PnaclHostTest,BadArguments)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());
164   FlushQueues();
165   EXPECT_EQ(1, temp_callback_count_);
166   host_->RendererClosing(0);  // close without finishing
167 }
168 
TEST_F(PnaclHostTest,BasicHit)169 TEST_F(PnaclHostTest, BasicHit) {
170   nacl::PnaclCacheInfo info = GetTestCacheInfo();
171   GET_NEXE_FD(0, 0, false, info, false);
172   FlushQueues();
173   EXPECT_EQ(1, temp_callback_count_);
174   host_->TranslationFinished(0, 0, true);
175   FlushQueues();
176   GET_NEXE_FD(0, 1, false, info, true);
177   FlushQueues();
178   EXPECT_EQ(2, temp_callback_count_);
179   EXPECT_EQ(0U, host_->pending_translations());
180 }
181 
TEST_F(PnaclHostTest,TranslationErrors)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);
187   FlushQueues();
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());
193   ReInitBackend();
194   // Check that another request for the same info misses successfully.
195   GET_NEXE_FD(0, 0, false, info, false);
196   FlushQueues();
197   host_->TranslationFinished(0, 0, true);
198   FlushQueues();
199   EXPECT_EQ(1, temp_callback_count_);
200   EXPECT_EQ(0U, host_->pending_translations());
201 
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);
205   FlushQueues();
206   EXPECT_EQ(2, temp_callback_count_);
207   host_->TranslationFinished(0, 0, false);
208   FlushQueues();
209   EXPECT_EQ(0U, host_->pending_translations());
210   // Check another successful miss
211   GET_NEXE_FD(0, 0, false, info, false);
212   FlushQueues();
213   EXPECT_EQ(3, temp_callback_count_);
214   host_->TranslationFinished(0, 0, false);
215   EXPECT_EQ(0U, host_->pending_translations());
216 }
217 
TEST_F(PnaclHostTest,OverlappedMissesAfterTempReturn)218 TEST_F(PnaclHostTest, OverlappedMissesAfterTempReturn) {
219   nacl::PnaclCacheInfo info = GetTestCacheInfo();
220   GET_NEXE_FD(0, 0, false, info, false);
221   FlushQueues();
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);
227   FlushQueues();
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);
233   FlushQueues();
234   EXPECT_EQ(2, temp_callback_count_);
235   EXPECT_EQ(0U, host_->pending_translations());
236 }
237 
TEST_F(PnaclHostTest,OverlappedMissesBeforeTempReturn)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);
243   FlushQueues();
244   EXPECT_EQ(1, temp_callback_count_);
245   EXPECT_EQ(2U, host_->pending_translations());
246   FlushQueues();
247   EXPECT_EQ(2U, host_->pending_translations());
248   EXPECT_EQ(1, temp_callback_count_);
249   host_->TranslationFinished(0, 0, true);
250   FlushQueues();
251   EXPECT_EQ(2, temp_callback_count_);
252   EXPECT_EQ(0U, host_->pending_translations());
253 }
254 
TEST_F(PnaclHostTest,OverlappedHitsBeforeTempReturn)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);
259   FlushQueues();
260   EXPECT_EQ(1, temp_callback_count_);
261   host_->TranslationFinished(0, 0, true);
262   FlushQueues();
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);
267   FlushQueues();
268   EXPECT_EQ(3, temp_callback_count_);
269   EXPECT_EQ(0U, host_->pending_translations());
270 }
271 
TEST_F(PnaclHostTest,OverlappedHitsAfterTempReturn)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);
276   FlushQueues();
277   EXPECT_EQ(1, temp_callback_count_);
278   host_->TranslationFinished(0, 0, true);
279   FlushQueues();
280   EXPECT_EQ(0U, host_->pending_translations());
281   GET_NEXE_FD(0, 0, false, info, true);
282   FlushQueues();
283   GET_NEXE_FD(0, 1, false, info, true);
284   FlushQueues();
285   EXPECT_EQ(3, temp_callback_count_);
286   EXPECT_EQ(0U, host_->pending_translations());
287 }
288 
TEST_F(PnaclHostTest,OverlappedMissesRendererClosing)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);
295   FlushQueues();
296   EXPECT_EQ(1, temp_callback_count_);
297   EXPECT_EQ(2U, host_->pending_translations());
298   FlushQueues();
299   EXPECT_EQ(2U, host_->pending_translations());
300   EXPECT_EQ(1, temp_callback_count_);
301   host_->RendererClosing(0);
302   FlushQueues();
303   EXPECT_EQ(2, temp_callback_count_);
304   EXPECT_EQ(1U, host_->pending_translations());
305   host_->RendererClosing(1);
306 }
307 
TEST_F(PnaclHostTest,Incognito)308 TEST_F(PnaclHostTest, Incognito) {
309   nacl::PnaclCacheInfo info = GetTestCacheInfo();
310   GET_NEXE_FD(0, 0, true, info, false);
311   FlushQueues();
312   EXPECT_EQ(1, temp_callback_count_);
313   host_->TranslationFinished(0, 0, true);
314   FlushQueues();
315   // Check that an incognito translation is not stored in the cache
316   GET_NEXE_FD(0, 0, false, info, false);
317   FlushQueues();
318   EXPECT_EQ(2, temp_callback_count_);
319   host_->TranslationFinished(0, 0, true);
320   FlushQueues();
321   // Check that an incognito translation can hit from a normal one.
322   GET_NEXE_FD(0, 0, true, info, true);
323   FlushQueues();
324   EXPECT_EQ(3, temp_callback_count_);
325 }
326 
TEST_F(PnaclHostTest,IncognitoOverlappedMiss)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);
331   FlushQueues();
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);
337   FlushQueues();
338   EXPECT_EQ(0U, host_->pending_translations());
339 
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);
343   FlushQueues();
344   EXPECT_EQ(3, temp_callback_count_);
345   GET_NEXE_FD(0, 1, false, info, false);
346   FlushQueues();
347   EXPECT_EQ(4, temp_callback_count_);
348   host_->RendererClosing(0);
349 }
350 
TEST_F(PnaclHostTest,IncognitoSecondOverlappedMiss)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);
358   FlushQueues();
359   EXPECT_EQ(1, temp_callback_count_);
360   EXPECT_EQ(2U, host_->pending_translations());
361   FlushQueues();
362   EXPECT_EQ(2U, host_->pending_translations());
363   EXPECT_EQ(1, temp_callback_count_);
364   host_->TranslationFinished(0, 0, true);
365   FlushQueues();
366   EXPECT_EQ(2, temp_callback_count_);
367   EXPECT_EQ(0U, host_->pending_translations());
368 }
369 
370 // Test that pexes with the no-store header do not get cached.
TEST_F(PnaclHostTest,CacheControlNoStore)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);
375   FlushQueues();
376   EXPECT_EQ(1, temp_callback_count_);
377   host_->TranslationFinished(0, 0, true);
378   FlushQueues();
379   EXPECT_EQ(0U, host_->pending_translations());
380   EXPECT_EQ(0, GetCacheSize());
381 }
382 
383 // Test that no-store pexes do not wait, but do duplicate translations
TEST_F(PnaclHostTest,NoStoreOverlappedMiss)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);
389   FlushQueues();
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);
395   FlushQueues();
396   EXPECT_EQ(0U, host_->pending_translations());
397 
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);
401   FlushQueues();
402   EXPECT_EQ(3, temp_callback_count_);
403   GET_NEXE_FD(0, 1, false, info, false);
404   FlushQueues();
405   EXPECT_EQ(4, temp_callback_count_);
406   host_->RendererClosing(0);
407 }
408 
TEST_F(PnaclHostTest,ClearTranslationCache)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);
415   FlushQueues();
416   EXPECT_EQ(2, temp_callback_count_);
417   host_->TranslationFinished(0, 0, true);
418   host_->TranslationFinished(0, 1, true);
419   FlushQueues();
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());
432 }
433 
434 // A version of PnaclHostTest that initializes cache on disk.
435 class PnaclHostTestDisk : public PnaclHostTest {
436  protected:
SetUp()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_);
442   }
DeInit()443   void DeInit() {
444     host_->DeInitIfSafe();
445   }
446 };
TEST_F(PnaclHostTestDisk,DeInitWhileInitializing)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.
453   DeInit();
454   base::RunLoop().RunUntilIdle();
455 }
456 
457 }  // namespace pnacl
458