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