Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / components / nacl / browser / pnacl_host_unittest.cc
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 <string>
9
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"
20
21 #if defined(OS_WIN)
22 #define snprintf _snprintf
23 #endif
24
25 namespace pnacl {
26 namespace {
27
28 // Size of a buffer used for writing and reading from a file.
29 const size_t kBufferSize = 16u;
30
31 }  // namespace
32
33 class PnaclHostTest : public testing::Test {
34  protected:
35   PnaclHostTest()
36       : host_(NULL),
37         temp_callback_count_(0),
38         write_callback_count_(0),
39         thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {}
40   virtual void SetUp() {
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_);
46   }
47   virtual void TearDown() {
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_);
53     delete host_;
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(), 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
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[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_++;
84   }
85   void CallbackExpectHit(const base::File& file, bool is_hit) {
86     EXPECT_TRUE(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_++;
102   }
103
104  protected:
105   PnaclHost* host_;
106   int temp_callback_count_;
107   int write_callback_count_;
108   content::TestBrowserThreadBundle thread_bundle_;
109   base::ScopedTempDir temp_dir_;
110 };
111
112 static nacl::PnaclCacheInfo GetTestCacheInfo() {
113   nacl::PnaclCacheInfo info;
114   info.pexe_url = GURL("http://www.google.com");
115   info.abi_version = 0;
116   info.opt_level = 0;
117   info.has_no_store_header = false;
118   return info;
119 }
120
121 #define GET_NEXE_FD(renderer, instance, incognito, info, expect_hit) \
122   do {                                                               \
123     SCOPED_TRACE("");                                                \
124     host_->GetNexeFd(                                                \
125         renderer,                                                    \
126         0, /* ignore render_view_id for now */                       \
127         instance,                                                    \
128         incognito,                                                   \
129         info,                                                        \
130         base::Bind(expect_hit ? &PnaclHostTest::CallbackExpectHit    \
131                               : &PnaclHostTest::CallbackExpectMiss,  \
132                    base::Unretained(this)));                         \
133   } while (0)
134
135 TEST_F(PnaclHostTest, BasicMiss) {
136   nacl::PnaclCacheInfo info = GetTestCacheInfo();
137   // Test cold miss.
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());
156 }
157
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
169 }
170
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());
182 }
183
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());
195   ReInitBackend();
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());
203
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());
218 }
219
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());
238 }
239
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());
255 }
256
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());
272 }
273
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());
289 }
290
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);
308 }
309
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_);
327 }
328
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());
341
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);
351 }
352
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());
370 }
371
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());
383 }
384
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());
399
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);
409 }
410
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());
434 }
435
436 // A version of PnaclHostTest that initializes cache on disk.
437 class PnaclHostTestDisk : public PnaclHostTest {
438  protected:
439   virtual void SetUp() {
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_);
444   }
445   void DeInit() {
446     host_->DeInitIfSafe();
447   }
448 };
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.
455   DeInit();
456   base::RunLoop().RunUntilIdle();
457 }
458
459 }  // namespace pnacl