Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / components / nacl / browser / pnacl_host_unittest.cc
blob960f8d5fb166220dff59fe6478b088a1cfb7c2d5
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"
7 #include <stdio.h>
8 #include <string>
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"
21 #if defined(OS_WIN)
22 #define snprintf _snprintf
23 #endif
25 namespace pnacl {
26 namespace {
28 // Size of a buffer used for writing and reading from a file.
29 const size_t kBufferSize = 16u;
31 } // namespace
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 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_);
53 delete host_;
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
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_++;
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_++;
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_;
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 info.use_subzero = false;
119 return info;
122 #define GET_NEXE_FD(renderer, instance, incognito, info, expect_hit) \
123 do { \
124 SCOPED_TRACE(""); \
125 host_->GetNexeFd( \
126 renderer, \
127 0, /* ignore render_view_id for now */ \
128 instance, \
129 incognito, \
130 info, \
131 base::Bind(expect_hit ? &PnaclHostTest::CallbackExpectHit \
132 : &PnaclHostTest::CallbackExpectMiss, \
133 base::Unretained(this))); \
134 } while (0)
136 TEST_F(PnaclHostTest, BasicMiss) {
137 nacl::PnaclCacheInfo info = GetTestCacheInfo();
138 // Test cold miss.
139 GET_NEXE_FD(0, 0, false, info, false);
140 EXPECT_EQ(1U, host_->pending_translations());
141 content::RunAllBlockingPoolTasksUntilIdle();
142 EXPECT_EQ(1U, host_->pending_translations());
143 EXPECT_EQ(1, temp_callback_count_);
144 host_->TranslationFinished(0, 0, true);
145 content::RunAllBlockingPoolTasksUntilIdle();
146 EXPECT_EQ(0U, host_->pending_translations());
147 // Test that a different cache info field also misses.
148 info.etag = std::string("something else");
149 GET_NEXE_FD(0, 0, false, info, false);
150 content::RunAllBlockingPoolTasksUntilIdle();
151 EXPECT_EQ(2, temp_callback_count_);
152 EXPECT_EQ(1U, host_->pending_translations());
153 host_->RendererClosing(0);
154 content::RunAllBlockingPoolTasksUntilIdle();
155 // Check that the cache has de-initialized after the last renderer goes away.
156 EXPECT_FALSE(CacheIsInitialized());
159 TEST_F(PnaclHostTest, BadArguments) {
160 nacl::PnaclCacheInfo info = GetTestCacheInfo();
161 GET_NEXE_FD(0, 0, false, info, false);
162 EXPECT_EQ(1U, host_->pending_translations());
163 host_->TranslationFinished(0, 1, true); // nonexistent translation
164 EXPECT_EQ(1U, host_->pending_translations());
165 host_->RendererClosing(1); // nonexistent renderer
166 EXPECT_EQ(1U, host_->pending_translations());
167 content::RunAllBlockingPoolTasksUntilIdle();
168 EXPECT_EQ(1, temp_callback_count_);
169 host_->RendererClosing(0); // close without finishing
172 TEST_F(PnaclHostTest, BasicHit) {
173 nacl::PnaclCacheInfo info = GetTestCacheInfo();
174 GET_NEXE_FD(0, 0, false, info, false);
175 content::RunAllBlockingPoolTasksUntilIdle();
176 EXPECT_EQ(1, temp_callback_count_);
177 host_->TranslationFinished(0, 0, true);
178 content::RunAllBlockingPoolTasksUntilIdle();
179 GET_NEXE_FD(0, 1, false, info, true);
180 content::RunAllBlockingPoolTasksUntilIdle();
181 EXPECT_EQ(2, temp_callback_count_);
182 EXPECT_EQ(0U, host_->pending_translations());
185 TEST_F(PnaclHostTest, TranslationErrors) {
186 nacl::PnaclCacheInfo info = GetTestCacheInfo();
187 GET_NEXE_FD(0, 0, false, info, false);
188 // Early abort, before temp file request returns
189 host_->TranslationFinished(0, 0, false);
190 content::RunAllBlockingPoolTasksUntilIdle();
191 EXPECT_EQ(0U, host_->pending_translations());
192 EXPECT_EQ(0, temp_callback_count_);
193 // The backend will have been freed when the query comes back and there
194 // are no pending translations.
195 EXPECT_FALSE(CacheIsInitialized());
196 ReInitBackend();
197 // Check that another request for the same info misses successfully.
198 GET_NEXE_FD(0, 0, false, info, false);
199 content::RunAllBlockingPoolTasksUntilIdle();
200 host_->TranslationFinished(0, 0, true);
201 content::RunAllBlockingPoolTasksUntilIdle();
202 EXPECT_EQ(1, temp_callback_count_);
203 EXPECT_EQ(0U, host_->pending_translations());
205 // Now try sending the error after the temp file request returns
206 info.abi_version = 222;
207 GET_NEXE_FD(0, 0, false, info, false);
208 content::RunAllBlockingPoolTasksUntilIdle();
209 EXPECT_EQ(2, temp_callback_count_);
210 host_->TranslationFinished(0, 0, false);
211 content::RunAllBlockingPoolTasksUntilIdle();
212 EXPECT_EQ(0U, host_->pending_translations());
213 // Check another successful miss
214 GET_NEXE_FD(0, 0, false, info, false);
215 content::RunAllBlockingPoolTasksUntilIdle();
216 EXPECT_EQ(3, temp_callback_count_);
217 host_->TranslationFinished(0, 0, false);
218 EXPECT_EQ(0U, host_->pending_translations());
221 TEST_F(PnaclHostTest, OverlappedMissesAfterTempReturn) {
222 nacl::PnaclCacheInfo info = GetTestCacheInfo();
223 GET_NEXE_FD(0, 0, false, info, false);
224 content::RunAllBlockingPoolTasksUntilIdle();
225 EXPECT_EQ(1, temp_callback_count_);
226 EXPECT_EQ(1U, host_->pending_translations());
227 // Test that a second request for the same nexe while the first one is still
228 // outstanding eventually hits.
229 GET_NEXE_FD(0, 1, false, info, true);
230 content::RunAllBlockingPoolTasksUntilIdle();
231 EXPECT_EQ(2U, host_->pending_translations());
232 // The temp file should not be returned to the second request until after the
233 // first is finished translating.
234 EXPECT_EQ(1, temp_callback_count_);
235 host_->TranslationFinished(0, 0, true);
236 content::RunAllBlockingPoolTasksUntilIdle();
237 EXPECT_EQ(2, temp_callback_count_);
238 EXPECT_EQ(0U, host_->pending_translations());
241 TEST_F(PnaclHostTest, OverlappedMissesBeforeTempReturn) {
242 nacl::PnaclCacheInfo info = GetTestCacheInfo();
243 GET_NEXE_FD(0, 0, false, info, false);
244 // Send the 2nd fd request before the first one returns a temp file.
245 GET_NEXE_FD(0, 1, false, info, true);
246 content::RunAllBlockingPoolTasksUntilIdle();
247 EXPECT_EQ(1, temp_callback_count_);
248 EXPECT_EQ(2U, host_->pending_translations());
249 content::RunAllBlockingPoolTasksUntilIdle();
250 EXPECT_EQ(2U, host_->pending_translations());
251 EXPECT_EQ(1, temp_callback_count_);
252 host_->TranslationFinished(0, 0, true);
253 content::RunAllBlockingPoolTasksUntilIdle();
254 EXPECT_EQ(2, temp_callback_count_);
255 EXPECT_EQ(0U, host_->pending_translations());
258 TEST_F(PnaclHostTest, OverlappedHitsBeforeTempReturn) {
259 nacl::PnaclCacheInfo info = GetTestCacheInfo();
260 // Store one in the cache and complete it.
261 GET_NEXE_FD(0, 0, false, info, false);
262 content::RunAllBlockingPoolTasksUntilIdle();
263 EXPECT_EQ(1, temp_callback_count_);
264 host_->TranslationFinished(0, 0, true);
265 content::RunAllBlockingPoolTasksUntilIdle();
266 EXPECT_EQ(0U, host_->pending_translations());
267 GET_NEXE_FD(0, 0, false, info, true);
268 // Request the second before the first temp file returns.
269 GET_NEXE_FD(0, 1, false, info, true);
270 content::RunAllBlockingPoolTasksUntilIdle();
271 EXPECT_EQ(3, temp_callback_count_);
272 EXPECT_EQ(0U, host_->pending_translations());
275 TEST_F(PnaclHostTest, OverlappedHitsAfterTempReturn) {
276 nacl::PnaclCacheInfo info = GetTestCacheInfo();
277 // Store one in the cache and complete it.
278 GET_NEXE_FD(0, 0, false, info, false);
279 content::RunAllBlockingPoolTasksUntilIdle();
280 EXPECT_EQ(1, temp_callback_count_);
281 host_->TranslationFinished(0, 0, true);
282 content::RunAllBlockingPoolTasksUntilIdle();
283 EXPECT_EQ(0U, host_->pending_translations());
284 GET_NEXE_FD(0, 0, false, info, true);
285 content::RunAllBlockingPoolTasksUntilIdle();
286 GET_NEXE_FD(0, 1, false, info, true);
287 content::RunAllBlockingPoolTasksUntilIdle();
288 EXPECT_EQ(3, temp_callback_count_);
289 EXPECT_EQ(0U, host_->pending_translations());
292 TEST_F(PnaclHostTest, OverlappedMissesRendererClosing) {
293 nacl::PnaclCacheInfo info = GetTestCacheInfo();
294 GET_NEXE_FD(0, 0, false, info, false);
295 // Send the 2nd fd request from a different renderer.
296 // Test that it eventually gets an fd after the first renderer closes.
297 GET_NEXE_FD(1, 1, false, info, false);
298 content::RunAllBlockingPoolTasksUntilIdle();
299 EXPECT_EQ(1, temp_callback_count_);
300 EXPECT_EQ(2U, host_->pending_translations());
301 content::RunAllBlockingPoolTasksUntilIdle();
302 EXPECT_EQ(2U, host_->pending_translations());
303 EXPECT_EQ(1, temp_callback_count_);
304 host_->RendererClosing(0);
305 content::RunAllBlockingPoolTasksUntilIdle();
306 EXPECT_EQ(2, temp_callback_count_);
307 EXPECT_EQ(1U, host_->pending_translations());
308 host_->RendererClosing(1);
311 TEST_F(PnaclHostTest, Incognito) {
312 nacl::PnaclCacheInfo info = GetTestCacheInfo();
313 GET_NEXE_FD(0, 0, true, info, false);
314 content::RunAllBlockingPoolTasksUntilIdle();
315 EXPECT_EQ(1, temp_callback_count_);
316 host_->TranslationFinished(0, 0, true);
317 content::RunAllBlockingPoolTasksUntilIdle();
318 // Check that an incognito translation is not stored in the cache
319 GET_NEXE_FD(0, 0, false, info, false);
320 content::RunAllBlockingPoolTasksUntilIdle();
321 EXPECT_EQ(2, temp_callback_count_);
322 host_->TranslationFinished(0, 0, true);
323 content::RunAllBlockingPoolTasksUntilIdle();
324 // Check that an incognito translation can hit from a normal one.
325 GET_NEXE_FD(0, 0, true, info, true);
326 content::RunAllBlockingPoolTasksUntilIdle();
327 EXPECT_EQ(3, temp_callback_count_);
330 TEST_F(PnaclHostTest, IncognitoOverlappedMiss) {
331 nacl::PnaclCacheInfo info = GetTestCacheInfo();
332 GET_NEXE_FD(0, 0, true, info, false);
333 GET_NEXE_FD(0, 1, false, info, false);
334 content::RunAllBlockingPoolTasksUntilIdle();
335 // Check that both translations have returned misses, (i.e. that the
336 // second one has not blocked on the incognito one)
337 EXPECT_EQ(2, temp_callback_count_);
338 host_->TranslationFinished(0, 0, true);
339 host_->TranslationFinished(0, 1, true);
340 content::RunAllBlockingPoolTasksUntilIdle();
341 EXPECT_EQ(0U, host_->pending_translations());
343 // Same test, but issue the 2nd request after the first has returned a miss.
344 info.abi_version = 222;
345 GET_NEXE_FD(0, 0, true, info, false);
346 content::RunAllBlockingPoolTasksUntilIdle();
347 EXPECT_EQ(3, temp_callback_count_);
348 GET_NEXE_FD(0, 1, false, info, false);
349 content::RunAllBlockingPoolTasksUntilIdle();
350 EXPECT_EQ(4, temp_callback_count_);
351 host_->RendererClosing(0);
354 TEST_F(PnaclHostTest, IncognitoSecondOverlappedMiss) {
355 // If the non-incognito request comes first, it should
356 // behave exactly like OverlappedMissBeforeTempReturn
357 nacl::PnaclCacheInfo info = GetTestCacheInfo();
358 GET_NEXE_FD(0, 0, false, info, false);
359 // Send the 2nd fd request before the first one returns a temp file.
360 GET_NEXE_FD(0, 1, true, info, true);
361 content::RunAllBlockingPoolTasksUntilIdle();
362 EXPECT_EQ(1, temp_callback_count_);
363 EXPECT_EQ(2U, host_->pending_translations());
364 content::RunAllBlockingPoolTasksUntilIdle();
365 EXPECT_EQ(2U, host_->pending_translations());
366 EXPECT_EQ(1, temp_callback_count_);
367 host_->TranslationFinished(0, 0, true);
368 content::RunAllBlockingPoolTasksUntilIdle();
369 EXPECT_EQ(2, temp_callback_count_);
370 EXPECT_EQ(0U, host_->pending_translations());
373 // Test that pexes with the no-store header do not get cached.
374 TEST_F(PnaclHostTest, CacheControlNoStore) {
375 nacl::PnaclCacheInfo info = GetTestCacheInfo();
376 info.has_no_store_header = true;
377 GET_NEXE_FD(0, 0, false, info, false);
378 content::RunAllBlockingPoolTasksUntilIdle();
379 EXPECT_EQ(1, temp_callback_count_);
380 host_->TranslationFinished(0, 0, true);
381 content::RunAllBlockingPoolTasksUntilIdle();
382 EXPECT_EQ(0U, host_->pending_translations());
383 EXPECT_EQ(0, GetCacheSize());
386 // Test that no-store pexes do not wait, but do duplicate translations
387 TEST_F(PnaclHostTest, NoStoreOverlappedMiss) {
388 nacl::PnaclCacheInfo info = GetTestCacheInfo();
389 info.has_no_store_header = true;
390 GET_NEXE_FD(0, 0, false, info, false);
391 GET_NEXE_FD(0, 1, false, info, false);
392 content::RunAllBlockingPoolTasksUntilIdle();
393 // Check that both translations have returned misses, (i.e. that the
394 // second one has not blocked on the first one)
395 EXPECT_EQ(2, temp_callback_count_);
396 host_->TranslationFinished(0, 0, true);
397 host_->TranslationFinished(0, 1, true);
398 content::RunAllBlockingPoolTasksUntilIdle();
399 EXPECT_EQ(0U, host_->pending_translations());
401 // Same test, but issue the 2nd request after the first has returned a miss.
402 info.abi_version = 222;
403 GET_NEXE_FD(0, 0, false, info, false);
404 content::RunAllBlockingPoolTasksUntilIdle();
405 EXPECT_EQ(3, temp_callback_count_);
406 GET_NEXE_FD(0, 1, false, info, false);
407 content::RunAllBlockingPoolTasksUntilIdle();
408 EXPECT_EQ(4, temp_callback_count_);
409 host_->RendererClosing(0);
412 TEST_F(PnaclHostTest, ClearTranslationCache) {
413 nacl::PnaclCacheInfo info = GetTestCacheInfo();
414 // Add 2 entries in the cache
415 GET_NEXE_FD(0, 0, false, info, false);
416 info.abi_version = 222;
417 GET_NEXE_FD(0, 1, false, info, false);
418 content::RunAllBlockingPoolTasksUntilIdle();
419 EXPECT_EQ(2, temp_callback_count_);
420 host_->TranslationFinished(0, 0, true);
421 host_->TranslationFinished(0, 1, true);
422 content::RunAllBlockingPoolTasksUntilIdle();
423 EXPECT_EQ(0U, host_->pending_translations());
424 EXPECT_EQ(2, GetCacheSize());
425 net::TestCompletionCallback cb;
426 // Since we are using a memory backend, the clear should happen immediately.
427 host_->ClearTranslationCacheEntriesBetween(
428 base::Time(), base::Time(), base::Bind(cb.callback(), 0));
429 // Check that the translation cache has been cleared before flushing the
430 // queues, because the backend will be freed once it is.
431 EXPECT_EQ(0, GetCacheSize());
432 EXPECT_EQ(0, cb.GetResult(net::ERR_IO_PENDING));
433 // Now check that the backend has been freed.
434 EXPECT_FALSE(CacheIsInitialized());
437 // A version of PnaclHostTest that initializes cache on disk.
438 class PnaclHostTestDisk : public PnaclHostTest {
439 protected:
440 void SetUp() override {
441 host_ = new PnaclHost();
442 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
443 host_->InitForTest(temp_dir_.path(), false);
444 EXPECT_EQ(PnaclHost::CacheInitializing, host_->cache_state_);
446 void DeInit() {
447 host_->DeInitIfSafe();
450 TEST_F(PnaclHostTestDisk, DeInitWhileInitializing) {
451 // Since there's no easy way to pump message queues one message at a time, we
452 // have to simulate what would happen if 1 DeInitIfsafe task gets queued, then
453 // a GetNexeFd gets queued, and then another DeInitIfSafe gets queued before
454 // the first one runs. We can just shortcut and call DeInitIfSafe while the
455 // cache is still initializing.
456 DeInit();
457 base::RunLoop().RunUntilIdle();
460 } // namespace pnacl