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_translation_cache.h" 6 7 #include "base/files/file_path.h" 8 #include "base/files/scoped_temp_dir.h" 9 #include "base/message_loop/message_loop.h" 10 #include "base/run_loop.h" 11 #include "components/nacl/common/pnacl_types.h" 12 #include "content/public/browser/browser_thread.h" 13 #include "content/public/test/test_browser_thread_bundle.h" 14 #include "net/base/io_buffer.h" 15 #include "net/base/test_completion_callback.h" 16 #include "testing/gtest/include/gtest/gtest.h" 17 18 using content::BrowserThread; 19 using base::FilePath; 20 21 namespace pnacl { 22 23 const int kTestDiskCacheSize = 16 * 1024 * 1024; 24 25 class PnaclTranslationCacheTest : public testing::Test { 26 protected: 27 PnaclTranslationCacheTest() 28 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {} 29 virtual ~PnaclTranslationCacheTest() {} 30 virtual void SetUp() { cache_.reset(new PnaclTranslationCache()); } 31 virtual void TearDown() { 32 // The destructor of PnaclTranslationCacheWriteEntry posts a task to the IO 33 // thread to close the backend cache entry. We want to make sure the entries 34 // are closed before we delete the backend (and in particular the destructor 35 // for the memory backend has a DCHECK to verify this), so we run the loop 36 // here to ensure the task gets processed. 37 base::RunLoop().RunUntilIdle(); 38 cache_.reset(); 39 } 40 41 void InitBackend(bool in_mem); 42 void StoreNexe(const std::string& key, const std::string& nexe); 43 std::string GetNexe(const std::string& key); 44 45 scoped_ptr<PnaclTranslationCache> cache_; 46 content::TestBrowserThreadBundle thread_bundle_; 47 base::ScopedTempDir temp_dir_; 48 }; 49 50 void PnaclTranslationCacheTest::InitBackend(bool in_mem) { 51 net::TestCompletionCallback init_cb; 52 if (!in_mem) { 53 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 54 } 55 // Use the private init method so we can control the size 56 int rv = cache_->Init(in_mem ? net::MEMORY_CACHE : net::PNACL_CACHE, 57 temp_dir_.path(), 58 in_mem ? kMaxMemCacheSize : kTestDiskCacheSize, 59 init_cb.callback()); 60 if (in_mem) 61 ASSERT_EQ(net::OK, rv); 62 ASSERT_EQ(net::OK, init_cb.GetResult(rv)); 63 ASSERT_EQ(0, cache_->Size()); 64 } 65 66 void PnaclTranslationCacheTest::StoreNexe(const std::string& key, 67 const std::string& nexe) { 68 net::TestCompletionCallback store_cb; 69 scoped_refptr<net::DrainableIOBuffer> nexe_buf( 70 new net::DrainableIOBuffer(new net::StringIOBuffer(nexe), nexe.size())); 71 cache_->StoreNexe(key, nexe_buf.get(), store_cb.callback()); 72 // Using ERR_IO_PENDING here causes the callback to wait for the result 73 // which should be harmless even if it returns OK immediately. This is because 74 // we don't plumb the intermediate writing stages all the way out. 75 EXPECT_EQ(net::OK, store_cb.GetResult(net::ERR_IO_PENDING)); 76 } 77 78 // Inspired by net::TestCompletionCallback. Instantiate a TestNexeCallback and 79 // pass the GetNexeCallback returned by the callback() method to GetNexe. 80 // Then call GetResult, which will pump the message loop until it gets a result, 81 // return the resulting IOBuffer and fill in the return value 82 class TestNexeCallback { 83 public: 84 TestNexeCallback() 85 : have_result_(false), 86 result_(-1), 87 cb_(base::Bind(&TestNexeCallback::SetResult, base::Unretained(this))) {} 88 GetNexeCallback callback() { return cb_; } 89 net::DrainableIOBuffer* GetResult(int* result) { 90 while (!have_result_) 91 base::RunLoop().RunUntilIdle(); 92 have_result_ = false; 93 *result = result_; 94 return buf_.get(); 95 } 96 97 private: 98 void SetResult(int rv, scoped_refptr<net::DrainableIOBuffer> buf) { 99 have_result_ = true; 100 result_ = rv; 101 buf_ = buf; 102 } 103 bool have_result_; 104 int result_; 105 scoped_refptr<net::DrainableIOBuffer> buf_; 106 const GetNexeCallback cb_; 107 }; 108 109 std::string PnaclTranslationCacheTest::GetNexe(const std::string& key) { 110 TestNexeCallback load_cb; 111 cache_->GetNexe(key, load_cb.callback()); 112 int rv; 113 scoped_refptr<net::DrainableIOBuffer> buf(load_cb.GetResult(&rv)); 114 EXPECT_EQ(net::OK, rv); 115 if (buf.get() == NULL) // for some reason ASSERT macros don't work here. 116 return std::string(); 117 std::string nexe(buf->data(), buf->size()); 118 return nexe; 119 } 120 121 static const std::string test_key("1"); 122 static const std::string test_store_val("testnexe"); 123 static const int kLargeNexeSize = 8 * 1024 * 1024; 124 125 TEST(PnaclTranslationCacheKeyTest, CacheKeyTest) { 126 nacl::PnaclCacheInfo info; 127 info.pexe_url = GURL("http://www.google.com"); 128 info.abi_version = 0; 129 info.opt_level = 0; 130 info.sandbox_isa = "x86-32"; 131 std::string test_time("Wed, 15 Nov 1995 06:25:24 GMT"); 132 base::Time::FromString(test_time.c_str(), &info.last_modified); 133 // Basic check for URL and time components 134 EXPECT_EQ("ABI:0;opt:0;URL:http://www.google.com/;" 135 "modified:1995:11:15:6:25:24:0:UTC;etag:;" 136 "sandbox:x86-32;extra_flags:;", 137 PnaclTranslationCache::GetKey(info)); 138 // Check that query portion of URL is not stripped 139 info.pexe_url = GURL("http://www.google.com/?foo=bar"); 140 EXPECT_EQ("ABI:0;opt:0;URL:http://www.google.com/?foo=bar;" 141 "modified:1995:11:15:6:25:24:0:UTC;etag:;" 142 "sandbox:x86-32;extra_flags:;", 143 PnaclTranslationCache::GetKey(info)); 144 // Check that username, password, and normal port are stripped 145 info.pexe_url = GURL("https://user:host@www.google.com:443/"); 146 EXPECT_EQ("ABI:0;opt:0;URL:https://www.google.com/;" 147 "modified:1995:11:15:6:25:24:0:UTC;etag:;" 148 "sandbox:x86-32;extra_flags:;", 149 PnaclTranslationCache::GetKey(info)); 150 // Check that unusual port is not stripped but ref is stripped 151 info.pexe_url = GURL("https://www.google.com:444/#foo"); 152 EXPECT_EQ("ABI:0;opt:0;URL:https://www.google.com:444/;" 153 "modified:1995:11:15:6:25:24:0:UTC;etag:;" 154 "sandbox:x86-32;extra_flags:;", 155 PnaclTranslationCache::GetKey(info)); 156 // Check chrome-extesnsion scheme 157 info.pexe_url = GURL("chrome-extension://ljacajndfccfgnfohlgkdphmbnpkjflk/"); 158 EXPECT_EQ("ABI:0;opt:0;" 159 "URL:chrome-extension://ljacajndfccfgnfohlgkdphmbnpkjflk/;" 160 "modified:1995:11:15:6:25:24:0:UTC;etag:;" 161 "sandbox:x86-32;extra_flags:;", 162 PnaclTranslationCache::GetKey(info)); 163 // Check that ABI version, opt level, and etag are in the key 164 info.pexe_url = GURL("http://www.google.com/"); 165 info.abi_version = 2; 166 EXPECT_EQ("ABI:2;opt:0;URL:http://www.google.com/;" 167 "modified:1995:11:15:6:25:24:0:UTC;etag:;" 168 "sandbox:x86-32;extra_flags:;", 169 PnaclTranslationCache::GetKey(info)); 170 info.opt_level = 2; 171 EXPECT_EQ("ABI:2;opt:2;URL:http://www.google.com/;" 172 "modified:1995:11:15:6:25:24:0:UTC;etag:;" 173 "sandbox:x86-32;extra_flags:;", 174 PnaclTranslationCache::GetKey(info)); 175 info.etag = std::string("etag"); 176 EXPECT_EQ("ABI:2;opt:2;URL:http://www.google.com/;" 177 "modified:1995:11:15:6:25:24:0:UTC;etag:etag;" 178 "sandbox:x86-32;extra_flags:;", 179 PnaclTranslationCache::GetKey(info)); 180 181 info.extra_flags = "-mavx-neon"; 182 EXPECT_EQ("ABI:2;opt:2;URL:http://www.google.com/;" 183 "modified:1995:11:15:6:25:24:0:UTC;etag:etag;" 184 "sandbox:x86-32;extra_flags:-mavx-neon;", 185 PnaclTranslationCache::GetKey(info)); 186 187 // Check for all the time components, and null time 188 info.last_modified = base::Time(); 189 EXPECT_EQ("ABI:2;opt:2;URL:http://www.google.com/;" 190 "modified:0:0:0:0:0:0:0:UTC;etag:etag;" 191 "sandbox:x86-32;extra_flags:-mavx-neon;", 192 PnaclTranslationCache::GetKey(info)); 193 test_time.assign("Fri, 29 Feb 2008 13:04:12 GMT"); 194 base::Time::FromString(test_time.c_str(), &info.last_modified); 195 EXPECT_EQ("ABI:2;opt:2;URL:http://www.google.com/;" 196 "modified:2008:2:29:13:4:12:0:UTC;etag:etag;" 197 "sandbox:x86-32;extra_flags:-mavx-neon;", 198 PnaclTranslationCache::GetKey(info)); 199 } 200 201 TEST_F(PnaclTranslationCacheTest, StoreSmallInMem) { 202 // Test that a single store puts something in the mem backend 203 InitBackend(true); 204 StoreNexe(test_key, test_store_val); 205 EXPECT_EQ(1, cache_->Size()); 206 } 207 208 TEST_F(PnaclTranslationCacheTest, StoreSmallOnDisk) { 209 // Test that a single store puts something in the disk backend 210 InitBackend(false); 211 StoreNexe(test_key, test_store_val); 212 EXPECT_EQ(1, cache_->Size()); 213 } 214 215 TEST_F(PnaclTranslationCacheTest, StoreLargeOnDisk) { 216 // Test a value too large(?) for a single I/O operation 217 InitBackend(false); 218 const std::string large_buffer(kLargeNexeSize, 'a'); 219 StoreNexe(test_key, large_buffer); 220 EXPECT_EQ(1, cache_->Size()); 221 } 222 223 TEST_F(PnaclTranslationCacheTest, InMemSizeLimit) { 224 InitBackend(true); 225 scoped_refptr<net::DrainableIOBuffer> large_buffer(new net::DrainableIOBuffer( 226 new net::StringIOBuffer(std::string(kMaxMemCacheSize + 1, 'a')), 227 kMaxMemCacheSize + 1)); 228 net::TestCompletionCallback store_cb; 229 cache_->StoreNexe(test_key, large_buffer.get(), store_cb.callback()); 230 EXPECT_EQ(net::ERR_FAILED, store_cb.GetResult(net::ERR_IO_PENDING)); 231 base::RunLoop().RunUntilIdle(); // Ensure the entry is closed. 232 EXPECT_EQ(0, cache_->Size()); 233 } 234 235 TEST_F(PnaclTranslationCacheTest, GetOneInMem) { 236 InitBackend(true); 237 StoreNexe(test_key, test_store_val); 238 EXPECT_EQ(1, cache_->Size()); 239 EXPECT_EQ(0, GetNexe(test_key).compare(test_store_val)); 240 } 241 242 TEST_F(PnaclTranslationCacheTest, GetOneOnDisk) { 243 InitBackend(false); 244 StoreNexe(test_key, test_store_val); 245 EXPECT_EQ(1, cache_->Size()); 246 EXPECT_EQ(0, GetNexe(test_key).compare(test_store_val)); 247 } 248 249 TEST_F(PnaclTranslationCacheTest, GetLargeOnDisk) { 250 InitBackend(false); 251 const std::string large_buffer(kLargeNexeSize, 'a'); 252 StoreNexe(test_key, large_buffer); 253 EXPECT_EQ(1, cache_->Size()); 254 EXPECT_EQ(0, GetNexe(test_key).compare(large_buffer)); 255 } 256 257 TEST_F(PnaclTranslationCacheTest, StoreTwice) { 258 // Test that storing twice with the same key overwrites 259 InitBackend(true); 260 StoreNexe(test_key, test_store_val); 261 StoreNexe(test_key, test_store_val + "aaa"); 262 EXPECT_EQ(1, cache_->Size()); 263 EXPECT_EQ(0, GetNexe(test_key).compare(test_store_val + "aaa")); 264 } 265 266 TEST_F(PnaclTranslationCacheTest, StoreTwo) { 267 InitBackend(true); 268 StoreNexe(test_key, test_store_val); 269 StoreNexe(test_key + "a", test_store_val + "aaa"); 270 EXPECT_EQ(2, cache_->Size()); 271 EXPECT_EQ(0, GetNexe(test_key).compare(test_store_val)); 272 EXPECT_EQ(0, GetNexe(test_key + "a").compare(test_store_val + "aaa")); 273 } 274 275 TEST_F(PnaclTranslationCacheTest, GetMiss) { 276 InitBackend(true); 277 StoreNexe(test_key, test_store_val); 278 TestNexeCallback load_cb; 279 std::string nexe; 280 cache_->GetNexe(test_key + "a", load_cb.callback()); 281 int rv; 282 scoped_refptr<net::DrainableIOBuffer> buf(load_cb.GetResult(&rv)); 283 EXPECT_EQ(net::ERR_FAILED, rv); 284 } 285 286 } // namespace pnacl 287