xref: /aosp_15_r20/external/cronet/components/nacl/browser/pnacl_host_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2013 The Chromium Authors
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 <stddef.h>
8 #include <stdio.h>
9 #include <string.h>
10 #include <string>
11 
12 #include "base/files/scoped_temp_dir.h"
13 #include "base/functional/bind.h"
14 #include "base/memory/raw_ptr.h"
15 #include "base/run_loop.h"
16 #include "build/build_config.h"
17 #include "components/nacl/browser/pnacl_translation_cache.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "content/public/test/browser_task_environment.h"
20 #include "content/public/test/test_utils.h"
21 #include "net/base/test_completion_callback.h"
22 #include "net/disk_cache/disk_cache.h"
23 #include "testing/gtest/include/gtest/gtest.h"
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:
PnaclHostTest()35   PnaclHostTest()
36       : host_(nullptr),
37         temp_callback_count_(0),
38         write_callback_count_(0),
39         task_environment_(content::BrowserTaskEnvironment::IO_MAINLOOP) {}
SetUp()40   void SetUp() override {
41     host_ = PnaclHost::GetInstance();
42     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
43     host_->InitForTest(temp_dir_.GetPath(), true);
44     base::RunLoop().RunUntilIdle();
45     EXPECT_EQ(PnaclHost::CacheReady, host_->cache_state_);
46   }
TearDown()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::RunAllTasksUntilIdle();
52     disk_cache::FlushCacheThreadForTesting();
53     EXPECT_EQ(PnaclHost::CacheUninitialized, host_->cache_state_);
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_.GetPath(), 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[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   }
CallbackExpectHit(const base::File & file,bool is_hit)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   raw_ptr<PnaclHost> host_;
106   int temp_callback_count_;
107   int write_callback_count_;
108   content::BrowserTaskEnvironment task_environment_;
109   base::ScopedTempDir temp_dir_;
110 };
111 
GetTestCacheInfo()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;
120 }
121 
122 #define GET_NEXE_FD(renderer, instance, incognito, info, expect_hit)         \
123   do {                                                                       \
124     SCOPED_TRACE("");                                                        \
125     host_->GetNexeFd(                                                        \
126         renderer, instance, incognito, info,                                 \
127         base::BindRepeating(expect_hit ? &PnaclHostTest::CallbackExpectHit   \
128                                        : &PnaclHostTest::CallbackExpectMiss, \
129                             base::Unretained(this)));                        \
130   } while (0)
131 
TEST_F(PnaclHostTest,BasicMiss)132 TEST_F(PnaclHostTest, BasicMiss) {
133   nacl::PnaclCacheInfo info = GetTestCacheInfo();
134   // Test cold miss.
135   GET_NEXE_FD(0, 0, false, info, false);
136   EXPECT_EQ(1U, host_->pending_translations());
137   content::RunAllTasksUntilIdle();
138   EXPECT_EQ(1U, host_->pending_translations());
139   EXPECT_EQ(1, temp_callback_count_);
140   host_->TranslationFinished(0, 0, true);
141   content::RunAllTasksUntilIdle();
142   EXPECT_EQ(0U, host_->pending_translations());
143   // Test that a different cache info field also misses.
144   info.etag = std::string("something else");
145   GET_NEXE_FD(0, 0, false, info, false);
146   content::RunAllTasksUntilIdle();
147   EXPECT_EQ(2, temp_callback_count_);
148   EXPECT_EQ(1U, host_->pending_translations());
149   host_->RendererClosing(0);
150   content::RunAllTasksUntilIdle();
151   // Check that the cache has de-initialized after the last renderer goes away.
152   EXPECT_FALSE(CacheIsInitialized());
153 }
154 
TEST_F(PnaclHostTest,BadArguments)155 TEST_F(PnaclHostTest, BadArguments) {
156   nacl::PnaclCacheInfo info = GetTestCacheInfo();
157   GET_NEXE_FD(0, 0, false, info, false);
158   EXPECT_EQ(1U, host_->pending_translations());
159   host_->TranslationFinished(0, 1, true);  // nonexistent translation
160   EXPECT_EQ(1U, host_->pending_translations());
161   host_->RendererClosing(1);  // nonexistent renderer
162   EXPECT_EQ(1U, host_->pending_translations());
163   content::RunAllTasksUntilIdle();
164   EXPECT_EQ(1, temp_callback_count_);
165   host_->RendererClosing(0);  // close without finishing
166 }
167 
TEST_F(PnaclHostTest,BasicHit)168 TEST_F(PnaclHostTest, BasicHit) {
169   nacl::PnaclCacheInfo info = GetTestCacheInfo();
170   GET_NEXE_FD(0, 0, false, info, false);
171   content::RunAllTasksUntilIdle();
172   EXPECT_EQ(1, temp_callback_count_);
173   host_->TranslationFinished(0, 0, true);
174   content::RunAllTasksUntilIdle();
175   GET_NEXE_FD(0, 1, false, info, true);
176   content::RunAllTasksUntilIdle();
177   EXPECT_EQ(2, temp_callback_count_);
178   EXPECT_EQ(0U, host_->pending_translations());
179 }
180 
TEST_F(PnaclHostTest,TranslationErrors)181 TEST_F(PnaclHostTest, TranslationErrors) {
182   nacl::PnaclCacheInfo info = GetTestCacheInfo();
183   GET_NEXE_FD(0, 0, false, info, false);
184   // Early abort, before temp file request returns
185   host_->TranslationFinished(0, 0, false);
186   content::RunAllTasksUntilIdle();
187   EXPECT_EQ(0U, host_->pending_translations());
188   EXPECT_EQ(0, temp_callback_count_);
189   // The backend will have been freed when the query comes back and there
190   // are no pending translations.
191   EXPECT_FALSE(CacheIsInitialized());
192   ReInitBackend();
193   // Check that another request for the same info misses successfully.
194   GET_NEXE_FD(0, 0, false, info, false);
195   content::RunAllTasksUntilIdle();
196   host_->TranslationFinished(0, 0, true);
197   content::RunAllTasksUntilIdle();
198   EXPECT_EQ(1, temp_callback_count_);
199   EXPECT_EQ(0U, host_->pending_translations());
200 
201   // Now try sending the error after the temp file request returns
202   info.abi_version = 222;
203   GET_NEXE_FD(0, 0, false, info, false);
204   content::RunAllTasksUntilIdle();
205   EXPECT_EQ(2, temp_callback_count_);
206   host_->TranslationFinished(0, 0, false);
207   content::RunAllTasksUntilIdle();
208   EXPECT_EQ(0U, host_->pending_translations());
209   // Check another successful miss
210   GET_NEXE_FD(0, 0, false, info, false);
211   content::RunAllTasksUntilIdle();
212   EXPECT_EQ(3, temp_callback_count_);
213   host_->TranslationFinished(0, 0, false);
214   EXPECT_EQ(0U, host_->pending_translations());
215 }
216 
TEST_F(PnaclHostTest,OverlappedMissesAfterTempReturn)217 TEST_F(PnaclHostTest, OverlappedMissesAfterTempReturn) {
218   nacl::PnaclCacheInfo info = GetTestCacheInfo();
219   GET_NEXE_FD(0, 0, false, info, false);
220   content::RunAllTasksUntilIdle();
221   EXPECT_EQ(1, temp_callback_count_);
222   EXPECT_EQ(1U, host_->pending_translations());
223   // Test that a second request for the same nexe while the first one is still
224   // outstanding eventually hits.
225   GET_NEXE_FD(0, 1, false, info, true);
226   content::RunAllTasksUntilIdle();
227   EXPECT_EQ(2U, host_->pending_translations());
228   // The temp file should not be returned to the second request until after the
229   // first is finished translating.
230   EXPECT_EQ(1, temp_callback_count_);
231   host_->TranslationFinished(0, 0, true);
232   content::RunAllTasksUntilIdle();
233   EXPECT_EQ(2, temp_callback_count_);
234   EXPECT_EQ(0U, host_->pending_translations());
235 }
236 
TEST_F(PnaclHostTest,OverlappedMissesBeforeTempReturn)237 TEST_F(PnaclHostTest, OverlappedMissesBeforeTempReturn) {
238   nacl::PnaclCacheInfo info = GetTestCacheInfo();
239   GET_NEXE_FD(0, 0, false, info, false);
240   // Send the 2nd fd request before the first one returns a temp file.
241   GET_NEXE_FD(0, 1, false, info, true);
242   content::RunAllTasksUntilIdle();
243   EXPECT_EQ(1, temp_callback_count_);
244   EXPECT_EQ(2U, host_->pending_translations());
245   content::RunAllTasksUntilIdle();
246   EXPECT_EQ(2U, host_->pending_translations());
247   EXPECT_EQ(1, temp_callback_count_);
248   host_->TranslationFinished(0, 0, true);
249   content::RunAllTasksUntilIdle();
250   EXPECT_EQ(2, temp_callback_count_);
251   EXPECT_EQ(0U, host_->pending_translations());
252 }
253 
TEST_F(PnaclHostTest,OverlappedHitsBeforeTempReturn)254 TEST_F(PnaclHostTest, OverlappedHitsBeforeTempReturn) {
255   nacl::PnaclCacheInfo info = GetTestCacheInfo();
256   // Store one in the cache and complete it.
257   GET_NEXE_FD(0, 0, false, info, false);
258   content::RunAllTasksUntilIdle();
259   EXPECT_EQ(1, temp_callback_count_);
260   host_->TranslationFinished(0, 0, true);
261   content::RunAllTasksUntilIdle();
262   EXPECT_EQ(0U, host_->pending_translations());
263   GET_NEXE_FD(0, 0, false, info, true);
264   // Request the second before the first temp file returns.
265   GET_NEXE_FD(0, 1, false, info, true);
266   content::RunAllTasksUntilIdle();
267   EXPECT_EQ(3, temp_callback_count_);
268   EXPECT_EQ(0U, host_->pending_translations());
269 }
270 
TEST_F(PnaclHostTest,OverlappedHitsAfterTempReturn)271 TEST_F(PnaclHostTest, OverlappedHitsAfterTempReturn) {
272   nacl::PnaclCacheInfo info = GetTestCacheInfo();
273   // Store one in the cache and complete it.
274   GET_NEXE_FD(0, 0, false, info, false);
275   content::RunAllTasksUntilIdle();
276   EXPECT_EQ(1, temp_callback_count_);
277   host_->TranslationFinished(0, 0, true);
278   content::RunAllTasksUntilIdle();
279   EXPECT_EQ(0U, host_->pending_translations());
280   GET_NEXE_FD(0, 0, false, info, true);
281   content::RunAllTasksUntilIdle();
282   GET_NEXE_FD(0, 1, false, info, true);
283   content::RunAllTasksUntilIdle();
284   EXPECT_EQ(3, temp_callback_count_);
285   EXPECT_EQ(0U, host_->pending_translations());
286 }
287 
TEST_F(PnaclHostTest,OverlappedMissesRendererClosing)288 TEST_F(PnaclHostTest, OverlappedMissesRendererClosing) {
289   nacl::PnaclCacheInfo info = GetTestCacheInfo();
290   GET_NEXE_FD(0, 0, false, info, false);
291   // Send the 2nd fd request from a different renderer.
292   // Test that it eventually gets an fd after the first renderer closes.
293   GET_NEXE_FD(1, 1, false, info, false);
294   content::RunAllTasksUntilIdle();
295   EXPECT_EQ(1, temp_callback_count_);
296   EXPECT_EQ(2U, host_->pending_translations());
297   content::RunAllTasksUntilIdle();
298   EXPECT_EQ(2U, host_->pending_translations());
299   EXPECT_EQ(1, temp_callback_count_);
300   host_->RendererClosing(0);
301   content::RunAllTasksUntilIdle();
302   EXPECT_EQ(2, temp_callback_count_);
303   EXPECT_EQ(1U, host_->pending_translations());
304   host_->RendererClosing(1);
305 }
306 
TEST_F(PnaclHostTest,Incognito)307 TEST_F(PnaclHostTest, Incognito) {
308   nacl::PnaclCacheInfo info = GetTestCacheInfo();
309   GET_NEXE_FD(0, 0, true, info, false);
310   content::RunAllTasksUntilIdle();
311   EXPECT_EQ(1, temp_callback_count_);
312   host_->TranslationFinished(0, 0, true);
313   content::RunAllTasksUntilIdle();
314   // Check that an incognito translation is not stored in the cache
315   GET_NEXE_FD(0, 0, false, info, false);
316   content::RunAllTasksUntilIdle();
317   EXPECT_EQ(2, temp_callback_count_);
318   host_->TranslationFinished(0, 0, true);
319   content::RunAllTasksUntilIdle();
320   // Check that an incognito translation can hit from a normal one.
321   GET_NEXE_FD(0, 0, true, info, true);
322   content::RunAllTasksUntilIdle();
323   EXPECT_EQ(3, temp_callback_count_);
324 }
325 
TEST_F(PnaclHostTest,IncognitoOverlappedMiss)326 TEST_F(PnaclHostTest, IncognitoOverlappedMiss) {
327   nacl::PnaclCacheInfo info = GetTestCacheInfo();
328   GET_NEXE_FD(0, 0, true, info, false);
329   GET_NEXE_FD(0, 1, false, info, false);
330   content::RunAllTasksUntilIdle();
331   // Check that both translations have returned misses, (i.e. that the
332   // second one has not blocked on the incognito one)
333   EXPECT_EQ(2, temp_callback_count_);
334   host_->TranslationFinished(0, 0, true);
335   host_->TranslationFinished(0, 1, true);
336   content::RunAllTasksUntilIdle();
337   EXPECT_EQ(0U, host_->pending_translations());
338 
339   // Same test, but issue the 2nd request after the first has returned a miss.
340   info.abi_version = 222;
341   GET_NEXE_FD(0, 0, true, info, false);
342   content::RunAllTasksUntilIdle();
343   EXPECT_EQ(3, temp_callback_count_);
344   GET_NEXE_FD(0, 1, false, info, false);
345   content::RunAllTasksUntilIdle();
346   EXPECT_EQ(4, temp_callback_count_);
347   host_->RendererClosing(0);
348 }
349 
TEST_F(PnaclHostTest,IncognitoSecondOverlappedMiss)350 TEST_F(PnaclHostTest, IncognitoSecondOverlappedMiss) {
351   // If the non-incognito request comes first, it should
352   // behave exactly like OverlappedMissBeforeTempReturn
353   nacl::PnaclCacheInfo info = GetTestCacheInfo();
354   GET_NEXE_FD(0, 0, false, info, false);
355   // Send the 2nd fd request before the first one returns a temp file.
356   GET_NEXE_FD(0, 1, true, info, true);
357   content::RunAllTasksUntilIdle();
358   EXPECT_EQ(1, temp_callback_count_);
359   EXPECT_EQ(2U, host_->pending_translations());
360   content::RunAllTasksUntilIdle();
361   EXPECT_EQ(2U, host_->pending_translations());
362   EXPECT_EQ(1, temp_callback_count_);
363   host_->TranslationFinished(0, 0, true);
364   content::RunAllTasksUntilIdle();
365   EXPECT_EQ(2, temp_callback_count_);
366   EXPECT_EQ(0U, host_->pending_translations());
367 }
368 
369 // Test that pexes with the no-store header do not get cached.
TEST_F(PnaclHostTest,CacheControlNoStore)370 TEST_F(PnaclHostTest, CacheControlNoStore) {
371   nacl::PnaclCacheInfo info = GetTestCacheInfo();
372   info.has_no_store_header = true;
373   GET_NEXE_FD(0, 0, false, info, false);
374   content::RunAllTasksUntilIdle();
375   EXPECT_EQ(1, temp_callback_count_);
376   host_->TranslationFinished(0, 0, true);
377   content::RunAllTasksUntilIdle();
378   EXPECT_EQ(0U, host_->pending_translations());
379   EXPECT_EQ(0, GetCacheSize());
380 }
381 
382 // Test that no-store pexes do not wait, but do duplicate translations
TEST_F(PnaclHostTest,NoStoreOverlappedMiss)383 TEST_F(PnaclHostTest, NoStoreOverlappedMiss) {
384   nacl::PnaclCacheInfo info = GetTestCacheInfo();
385   info.has_no_store_header = true;
386   GET_NEXE_FD(0, 0, false, info, false);
387   GET_NEXE_FD(0, 1, false, info, false);
388   content::RunAllTasksUntilIdle();
389   // Check that both translations have returned misses, (i.e. that the
390   // second one has not blocked on the first one)
391   EXPECT_EQ(2, temp_callback_count_);
392   host_->TranslationFinished(0, 0, true);
393   host_->TranslationFinished(0, 1, true);
394   content::RunAllTasksUntilIdle();
395   EXPECT_EQ(0U, host_->pending_translations());
396 
397   // Same test, but issue the 2nd request after the first has returned a miss.
398   info.abi_version = 222;
399   GET_NEXE_FD(0, 0, false, info, false);
400   content::RunAllTasksUntilIdle();
401   EXPECT_EQ(3, temp_callback_count_);
402   GET_NEXE_FD(0, 1, false, info, false);
403   content::RunAllTasksUntilIdle();
404   EXPECT_EQ(4, temp_callback_count_);
405   host_->RendererClosing(0);
406 }
407 
TEST_F(PnaclHostTest,ClearTranslationCache)408 TEST_F(PnaclHostTest, ClearTranslationCache) {
409   nacl::PnaclCacheInfo info = GetTestCacheInfo();
410   // Add 2 entries in the cache
411   GET_NEXE_FD(0, 0, false, info, false);
412   info.abi_version = 222;
413   GET_NEXE_FD(0, 1, false, info, false);
414   content::RunAllTasksUntilIdle();
415   EXPECT_EQ(2, temp_callback_count_);
416   host_->TranslationFinished(0, 0, true);
417   host_->TranslationFinished(0, 1, true);
418   content::RunAllTasksUntilIdle();
419   EXPECT_EQ(0U, host_->pending_translations());
420   EXPECT_EQ(2, GetCacheSize());
421   net::TestCompletionCallback cb;
422   // Since we are using a memory backend, the clear should happen immediately.
423   host_->ClearTranslationCacheEntriesBetween(base::Time(), base::Time(),
424                                              base::BindOnce(cb.callback(), 0));
425   // Check that the translation cache has been cleared before flushing the
426   // queues, because the backend will be freed once it is.
427   EXPECT_EQ(0, GetCacheSize());
428   EXPECT_EQ(0, cb.GetResult(net::ERR_IO_PENDING));
429   // Call posted PnaclHost::CopyFileToBuffer() tasks.
430   base::RunLoop().RunUntilIdle();
431   // Now check that the backend has been freed.
432   EXPECT_FALSE(CacheIsInitialized());
433 }
434 
435 // A version of PnaclHostTest that initializes cache on disk.
436 class PnaclHostTestDisk : public PnaclHostTest {
437  protected:
SetUp()438   void SetUp() override {
439     host_ = PnaclHost::GetInstance();
440     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
441     host_->InitForTest(temp_dir_.GetPath(), false);
442     EXPECT_EQ(PnaclHost::CacheInitializing, host_->cache_state_);
443   }
DeInit()444   void DeInit() {
445     host_->DeInitIfSafe();
446   }
447 };
TEST_F(PnaclHostTestDisk,DeInitWhileInitializing)448 TEST_F(PnaclHostTestDisk, DeInitWhileInitializing) {
449   // Since there's no easy way to pump message queues one message at a time, we
450   // have to simulate what would happen if 1 DeInitIfsafe task gets queued, then
451   // a GetNexeFd gets queued, and then another DeInitIfSafe gets queued before
452   // the first one runs. We can just shortcut and call DeInitIfSafe while the
453   // cache is still initializing.
454   DeInit();
455 
456   // Now let it finish initializing. (Other tests don't need this since they
457   // use in-memory storage).
458   disk_cache::FlushCacheThreadForTesting();
459   base::RunLoop().RunUntilIdle();
460   EXPECT_TRUE(CacheIsInitialized());
461 }
462 
463 }  // namespace pnacl
464