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 "chrome/browser/nacl_host/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 "chrome/browser/nacl_host/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
22namespace pnacl {
23
24class PnaclHostTest : public testing::Test {
25 protected:
26  PnaclHostTest()
27      : host_(NULL),
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());
35    EXPECT_EQ(host_->cache_state_, PnaclHost::CacheReady);
36  }
37  virtual void TearDown() {
38    EXPECT_EQ(0U, host_->pending_translations());
39    delete host_;
40  }
41  // Flush the blocking pool first, then any tasks it posted to the IO thread.
42  // Do 2 rounds of flushing, because some operations require 2 trips back and
43  // forth between the threads.
44  void FlushQueues() {
45    content::BrowserThread::GetBlockingPool()->FlushForTesting();
46    base::RunLoop().RunUntilIdle();
47    content::BrowserThread::GetBlockingPool()->FlushForTesting();
48    base::RunLoop().RunUntilIdle();
49  }
50  int GetCacheSize() {
51    return host_->disk_cache_->Size();
52  }
53
54 public:  // Required for derived classes to bind this method
55  // Callbacks used by tests which call GetNexeFd.
56  // CallbackExpectMiss checks that the fd is valid and a miss is reported,
57  // and also writes some data into the file, which is read back by
58  // CallbackExpectHit
59  void CallbackExpectMiss(base::PlatformFile fd, bool is_hit) {
60    EXPECT_FALSE(is_hit);
61    ASSERT_FALSE(fd == base::kInvalidPlatformFileValue);
62    base::PlatformFileInfo info;
63    EXPECT_TRUE(base::GetPlatformFileInfo(fd, &info));
64    EXPECT_FALSE(info.is_directory);
65    EXPECT_EQ(0LL, info.size);
66    char str[16];
67    memset(str, 0x0, 16);
68    snprintf(str, 16, "testdata%d", ++write_callback_count_);
69    EXPECT_EQ(16, base::WritePlatformFile(fd, 0, str, 16));
70    temp_callback_count_++;
71  }
72  void CallbackExpectHit(base::PlatformFile fd, bool is_hit) {
73    EXPECT_TRUE(is_hit);
74    ASSERT_FALSE(fd == base::kInvalidPlatformFileValue);
75    base::PlatformFileInfo info;
76    EXPECT_TRUE(base::GetPlatformFileInfo(fd, &info));
77    EXPECT_FALSE(info.is_directory);
78    EXPECT_EQ(16LL, info.size);
79    char data[16];
80    memset(data, 0x0, 16);
81    char str[16];
82    memset(str, 0x0, 16);
83    snprintf(str, 16, "testdata%d", write_callback_count_);
84    EXPECT_EQ(16, base::ReadPlatformFile(fd, 0, data, 16));
85    EXPECT_STREQ(str, data);
86    temp_callback_count_++;
87  }
88
89 protected:
90  PnaclHost* host_;
91  int temp_callback_count_;
92  int write_callback_count_;
93  content::TestBrowserThreadBundle thread_bundle_;
94  base::ScopedTempDir temp_dir_;
95};
96
97static nacl::PnaclCacheInfo GetTestCacheInfo() {
98  nacl::PnaclCacheInfo info;
99  info.pexe_url = GURL("http://www.google.com");
100  info.abi_version = 0;
101  info.opt_level = 0;
102  return info;
103}
104
105#define GET_NEXE_FD(renderer, instance, incognito, info, expect_hit)\
106  do {                                                              \
107    SCOPED_TRACE("");                                               \
108    host_->GetNexeFd(                                               \
109        renderer,                                                   \
110        0, /* ignore render_view_id for now */                      \
111        instance,                                                   \
112        incognito,                                                  \
113        info,                                                       \
114        base::Bind(expect_hit ? &PnaclHostTest::CallbackExpectHit   \
115                              : &PnaclHostTest::CallbackExpectMiss, \
116                   base::Unretained(this)));                        \
117  } while (0)
118
119TEST_F(PnaclHostTest, BasicMiss) {
120  nacl::PnaclCacheInfo info = GetTestCacheInfo();
121  // Test cold miss.
122  GET_NEXE_FD(0, 0, false, info, false);
123  EXPECT_EQ(1U, host_->pending_translations());
124  FlushQueues();
125  EXPECT_EQ(1U, host_->pending_translations());
126  EXPECT_EQ(1, temp_callback_count_);
127  host_->TranslationFinished(0, 0, true);
128  FlushQueues();
129  EXPECT_EQ(0U, host_->pending_translations());
130  // Test that a different cache info field also misses.
131  info.etag = std::string("something else");
132  GET_NEXE_FD(0, 0, false, info, false);
133  FlushQueues();
134  EXPECT_EQ(2, temp_callback_count_);
135  EXPECT_EQ(1U, host_->pending_translations());
136  host_->RendererClosing(0);
137}
138
139TEST_F(PnaclHostTest, BadArguments) {
140  nacl::PnaclCacheInfo info = GetTestCacheInfo();
141  GET_NEXE_FD(0, 0, false, info, false);
142  EXPECT_EQ(1U, host_->pending_translations());
143  host_->TranslationFinished(0, 1, true);  // nonexistent translation
144  EXPECT_EQ(1U, host_->pending_translations());
145  host_->RendererClosing(1);  // nonexistent renderer
146  EXPECT_EQ(1U, host_->pending_translations());
147  FlushQueues();
148  EXPECT_EQ(1, temp_callback_count_);
149  host_->RendererClosing(0);  // close without finishing
150}
151
152TEST_F(PnaclHostTest, BasicHit) {
153  nacl::PnaclCacheInfo info = GetTestCacheInfo();
154  GET_NEXE_FD(0, 0, false, info, false);
155  FlushQueues();
156  EXPECT_EQ(1, temp_callback_count_);
157  host_->TranslationFinished(0, 0, true);
158  FlushQueues();
159  GET_NEXE_FD(0, 1, false, info, true);
160  FlushQueues();
161  EXPECT_EQ(2, temp_callback_count_);
162  EXPECT_EQ(0U, host_->pending_translations());
163}
164
165TEST_F(PnaclHostTest, TranslationErrors) {
166  nacl::PnaclCacheInfo info = GetTestCacheInfo();
167  info.pexe_url = GURL("http://www.google.com");
168  GET_NEXE_FD(0, 0, false, info, false);
169  // Early abort, before temp file request returns
170  host_->TranslationFinished(0, 0, false);
171  FlushQueues();
172  EXPECT_EQ(0U, host_->pending_translations());
173  EXPECT_EQ(0, temp_callback_count_);
174  // Check that another request for the same info misses successfully.
175  GET_NEXE_FD(0, 0, false, info, false);
176  FlushQueues();
177  host_->TranslationFinished(0, 0, true);
178  FlushQueues();
179  EXPECT_EQ(1, temp_callback_count_);
180  EXPECT_EQ(0U, host_->pending_translations());
181
182  // Now try sending the error after the temp file request returns
183  info.abi_version = 222;
184  GET_NEXE_FD(0, 0, false, info, false);
185  FlushQueues();
186  EXPECT_EQ(2, temp_callback_count_);
187  host_->TranslationFinished(0, 0, false);
188  FlushQueues();
189  EXPECT_EQ(0U, host_->pending_translations());
190  // Check another successful miss
191  GET_NEXE_FD(0, 0, false, info, false);
192  FlushQueues();
193  EXPECT_EQ(3, temp_callback_count_);
194  host_->TranslationFinished(0, 0, false);
195  EXPECT_EQ(0U, host_->pending_translations());
196}
197
198TEST_F(PnaclHostTest, OverlappedMissesAfterTempReturn) {
199  nacl::PnaclCacheInfo info = GetTestCacheInfo();
200  GET_NEXE_FD(0, 0, false, info, false);
201  FlushQueues();
202  EXPECT_EQ(1, temp_callback_count_);
203  EXPECT_EQ(1U, host_->pending_translations());
204  // Test that a second request for the same nexe while the first one is still
205  // outstanding eventually hits.
206  GET_NEXE_FD(0, 1, false, info, true);
207  FlushQueues();
208  EXPECT_EQ(2U, host_->pending_translations());
209  // The temp file should not be returned to the second request until after the
210  // first is finished translating.
211  EXPECT_EQ(1, temp_callback_count_);
212  host_->TranslationFinished(0, 0, true);
213  FlushQueues();
214  EXPECT_EQ(2, temp_callback_count_);
215  EXPECT_EQ(0U, host_->pending_translations());
216}
217
218TEST_F(PnaclHostTest, OverlappedMissesBeforeTempReturn) {
219  nacl::PnaclCacheInfo info = GetTestCacheInfo();
220  GET_NEXE_FD(0, 0, false, info, false);
221  // Send the 2nd fd request before the first one returns a temp file.
222  GET_NEXE_FD(0, 1, false, info, true);
223  FlushQueues();
224  EXPECT_EQ(1, temp_callback_count_);
225  EXPECT_EQ(2U, host_->pending_translations());
226  FlushQueues();
227  EXPECT_EQ(2U, host_->pending_translations());
228  EXPECT_EQ(1, temp_callback_count_);
229  host_->TranslationFinished(0, 0, true);
230  FlushQueues();
231  EXPECT_EQ(2, temp_callback_count_);
232  EXPECT_EQ(0U, host_->pending_translations());
233}
234
235TEST_F(PnaclHostTest, OverlappedHitsBeforeTempReturn) {
236  nacl::PnaclCacheInfo info = GetTestCacheInfo();
237  // Store one in the cache and complete it.
238  GET_NEXE_FD(0, 0, false, info, false);
239  FlushQueues();
240  EXPECT_EQ(1, temp_callback_count_);
241  host_->TranslationFinished(0, 0, true);
242  FlushQueues();
243  EXPECT_EQ(0U, host_->pending_translations());
244  GET_NEXE_FD(0, 0, false, info, true);
245  // Request the second before the first temp file returns.
246  GET_NEXE_FD(0, 1, false, info, true);
247  FlushQueues();
248  EXPECT_EQ(3, temp_callback_count_);
249  EXPECT_EQ(0U, host_->pending_translations());
250}
251
252TEST_F(PnaclHostTest, OverlappedHitsAfterTempReturn) {
253  nacl::PnaclCacheInfo info = GetTestCacheInfo();
254  // Store one in the cache and complete it.
255  GET_NEXE_FD(0, 0, false, info, false);
256  FlushQueues();
257  EXPECT_EQ(1, temp_callback_count_);
258  host_->TranslationFinished(0, 0, true);
259  FlushQueues();
260  EXPECT_EQ(0U, host_->pending_translations());
261  GET_NEXE_FD(0, 0, false, info, true);
262  FlushQueues();
263  GET_NEXE_FD(0, 1, false, info, true);
264  FlushQueues();
265  EXPECT_EQ(3, temp_callback_count_);
266  EXPECT_EQ(0U, host_->pending_translations());
267}
268
269TEST_F(PnaclHostTest, OverlappedMissesRendererClosing) {
270  nacl::PnaclCacheInfo info = GetTestCacheInfo();
271  GET_NEXE_FD(0, 0, false, info, false);
272  // Send the 2nd fd request from a different renderer.
273  // Test that it eventually gets an fd after the first renderer closes.
274  GET_NEXE_FD(1, 1, false, info, false);
275  FlushQueues();
276  EXPECT_EQ(1, temp_callback_count_);
277  EXPECT_EQ(2U, host_->pending_translations());
278  FlushQueues();
279  EXPECT_EQ(2U, host_->pending_translations());
280  EXPECT_EQ(1, temp_callback_count_);
281  host_->RendererClosing(0);
282  FlushQueues();
283  EXPECT_EQ(2, temp_callback_count_);
284  EXPECT_EQ(1U, host_->pending_translations());
285  host_->RendererClosing(1);
286}
287
288TEST_F(PnaclHostTest, Incognito) {
289  nacl::PnaclCacheInfo info = GetTestCacheInfo();
290  GET_NEXE_FD(0, 0, true, info, false);
291  FlushQueues();
292  EXPECT_EQ(1, temp_callback_count_);
293  host_->TranslationFinished(0, 0, true);
294  FlushQueues();
295  // Check that an incognito translation is not stored in the cache
296  GET_NEXE_FD(0, 0, false, info, false);
297  FlushQueues();
298  EXPECT_EQ(2, temp_callback_count_);
299  host_->TranslationFinished(0, 0, true);
300  FlushQueues();
301  // Check that an incognito translation can hit from a normal one.
302  GET_NEXE_FD(0, 0, true, info, true);
303  FlushQueues();
304  EXPECT_EQ(3, temp_callback_count_);
305}
306
307TEST_F(PnaclHostTest, IncognitoOverlappedMiss) {
308  nacl::PnaclCacheInfo info = GetTestCacheInfo();
309  GET_NEXE_FD(0, 0, true, info, false);
310  GET_NEXE_FD(0, 1, false, info, false);
311  FlushQueues();
312  // Check that both translations have returned misses, (i.e. that the
313  // second one has not blocked on the incognito one)
314  EXPECT_EQ(2, temp_callback_count_);
315  host_->TranslationFinished(0, 0, true);
316  host_->TranslationFinished(0, 1, true);
317  FlushQueues();
318  EXPECT_EQ(0U, host_->pending_translations());
319
320  // Same test, but issue the 2nd request after the first has returned a miss.
321  info.abi_version = 222;
322  GET_NEXE_FD(0, 0, true, info, false);
323  FlushQueues();
324  EXPECT_EQ(3, temp_callback_count_);
325  GET_NEXE_FD(0, 1, false, info, false);
326  FlushQueues();
327  EXPECT_EQ(4, temp_callback_count_);
328  host_->RendererClosing(0);
329}
330
331TEST_F(PnaclHostTest, IncognitoSecondOverlappedMiss) {
332  // If the non-incognito request comes first, it should
333  // behave exactly like OverlappedMissBeforeTempReturn
334  nacl::PnaclCacheInfo info = GetTestCacheInfo();
335  GET_NEXE_FD(0, 0, false, info, false);
336  // Send the 2nd fd request before the first one returns a temp file.
337  GET_NEXE_FD(0, 1, true, info, true);
338  FlushQueues();
339  EXPECT_EQ(1, temp_callback_count_);
340  EXPECT_EQ(2U, host_->pending_translations());
341  FlushQueues();
342  EXPECT_EQ(2U, host_->pending_translations());
343  EXPECT_EQ(1, temp_callback_count_);
344  host_->TranslationFinished(0, 0, true);
345  FlushQueues();
346  EXPECT_EQ(2, temp_callback_count_);
347  EXPECT_EQ(0U, host_->pending_translations());
348}
349
350TEST_F(PnaclHostTest, ClearTranslationCache) {
351  nacl::PnaclCacheInfo info = GetTestCacheInfo();
352  // Add 2 entries in the cache
353  GET_NEXE_FD(0, 0, false, info, false);
354  info.abi_version = 222;
355  GET_NEXE_FD(0, 1, false, info, false);
356  FlushQueues();
357  EXPECT_EQ(2, temp_callback_count_);
358  host_->TranslationFinished(0, 0, true);
359  host_->TranslationFinished(0, 1, true);
360  FlushQueues();
361  EXPECT_EQ(0U, host_->pending_translations());
362  EXPECT_EQ(2, GetCacheSize());
363  net::TestCompletionCallback cb;
364  // Since we are using a memory backend, the clear should happen immediately.
365  host_->ClearTranslationCacheEntriesBetween(base::Time(), base::Time(),
366                                             base::Bind(cb.callback(), 0));
367  EXPECT_EQ(0, cb.GetResult(net::ERR_IO_PENDING));
368  // Check that the translation cache has been cleared
369  EXPECT_EQ(0, GetCacheSize());
370  host_->RendererClosing(0);
371}
372
373}  // namespace pnacl
374