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, 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 std::string test_time("Wed, 15 Nov 1995 06:25:24 GMT"); 131 base::Time::FromString(test_time.c_str(), &info.last_modified); 132 // Basic check for URL and time components 133 EXPECT_EQ("ABI:0;opt:0;URL:http://www.google.com/;" 134 "modified:1995:11:15:6:25:24:0:UTC;etag:", 135 PnaclTranslationCache::GetKey(info)); 136 // Check that query portion of URL is not stripped 137 info.pexe_url = GURL("http://www.google.com/?foo=bar"); 138 EXPECT_EQ("ABI:0;opt:0;URL:http://www.google.com/?foo=bar;" 139 "modified:1995:11:15:6:25:24:0:UTC;etag:", 140 PnaclTranslationCache::GetKey(info)); 141 // Check that username, password, and normal port are stripped 142 info.pexe_url = GURL("https://user:host@www.google.com:443/"); 143 EXPECT_EQ("ABI:0;opt:0;URL:https://www.google.com/;" 144 "modified:1995:11:15:6:25:24:0:UTC;etag:", 145 PnaclTranslationCache::GetKey(info)); 146 // Check that unusual port is not stripped but ref is stripped 147 info.pexe_url = GURL("https://www.google.com:444/#foo"); 148 EXPECT_EQ("ABI:0;opt:0;URL:https://www.google.com:444/;" 149 "modified:1995:11:15:6:25:24:0:UTC;etag:", 150 PnaclTranslationCache::GetKey(info)); 151 // Check chrome-extesnsion scheme 152 info.pexe_url = GURL("chrome-extension://ljacajndfccfgnfohlgkdphmbnpkjflk/"); 153 EXPECT_EQ("ABI:0;opt:0;" 154 "URL:chrome-extension://ljacajndfccfgnfohlgkdphmbnpkjflk/;" 155 "modified:1995:11:15:6:25:24:0:UTC;etag:", 156 PnaclTranslationCache::GetKey(info)); 157 // Check that ABI version, opt level, and etag are in the key 158 info.pexe_url = GURL("http://www.google.com/"); 159 info.abi_version = 2; 160 EXPECT_EQ("ABI:2;opt:0;URL:http://www.google.com/;" 161 "modified:1995:11:15:6:25:24:0:UTC;etag:", 162 PnaclTranslationCache::GetKey(info)); 163 info.opt_level = 2; 164 EXPECT_EQ("ABI:2;opt:2;URL:http://www.google.com/;" 165 "modified:1995:11:15:6:25:24:0:UTC;etag:", 166 PnaclTranslationCache::GetKey(info)); 167 info.etag = std::string("etag"); 168 EXPECT_EQ("ABI:2;opt:2;URL:http://www.google.com/;" 169 "modified:1995:11:15:6:25:24:0:UTC;etag:etag", 170 PnaclTranslationCache::GetKey(info)); 171 172 // Check for all the time components, and null time 173 info.last_modified = base::Time(); 174 EXPECT_EQ("ABI:2;opt:2;URL:http://www.google.com/;" 175 "modified:0:0:0:0:0:0:0:UTC;etag:etag", 176 PnaclTranslationCache::GetKey(info)); 177 test_time.assign("Fri, 29 Feb 2008 13:04:12 GMT"); 178 base::Time::FromString(test_time.c_str(), &info.last_modified); 179 EXPECT_EQ("ABI:2;opt:2;URL:http://www.google.com/;" 180 "modified:2008:2:29:13:4:12:0:UTC;etag:etag", 181 PnaclTranslationCache::GetKey(info)); 182 } 183 184 TEST_F(PnaclTranslationCacheTest, StoreSmallInMem) { 185 // Test that a single store puts something in the mem backend 186 InitBackend(true); 187 StoreNexe(test_key, test_store_val); 188 EXPECT_EQ(1, cache_->Size()); 189 } 190 191 TEST_F(PnaclTranslationCacheTest, StoreSmallOnDisk) { 192 // Test that a single store puts something in the disk backend 193 InitBackend(false); 194 StoreNexe(test_key, test_store_val); 195 EXPECT_EQ(1, cache_->Size()); 196 } 197 198 TEST_F(PnaclTranslationCacheTest, StoreLargeOnDisk) { 199 // Test a value too large(?) for a single I/O operation 200 InitBackend(false); 201 const std::string large_buffer(kLargeNexeSize, 'a'); 202 StoreNexe(test_key, large_buffer); 203 EXPECT_EQ(1, cache_->Size()); 204 } 205 206 TEST_F(PnaclTranslationCacheTest, InMemSizeLimit) { 207 InitBackend(true); 208 scoped_refptr<net::DrainableIOBuffer> large_buffer(new net::DrainableIOBuffer( 209 new net::StringIOBuffer(std::string(kMaxMemCacheSize + 1, 'a')), 210 kMaxMemCacheSize + 1)); 211 net::TestCompletionCallback store_cb; 212 cache_->StoreNexe(test_key, large_buffer, store_cb.callback()); 213 EXPECT_EQ(net::ERR_FAILED, store_cb.GetResult(net::ERR_IO_PENDING)); 214 base::RunLoop().RunUntilIdle(); // Ensure the entry is closed. 215 EXPECT_EQ(0, cache_->Size()); 216 } 217 218 TEST_F(PnaclTranslationCacheTest, GetOneInMem) { 219 InitBackend(true); 220 StoreNexe(test_key, test_store_val); 221 EXPECT_EQ(1, cache_->Size()); 222 EXPECT_EQ(0, GetNexe(test_key).compare(test_store_val)); 223 } 224 225 TEST_F(PnaclTranslationCacheTest, GetOneOnDisk) { 226 InitBackend(false); 227 StoreNexe(test_key, test_store_val); 228 EXPECT_EQ(1, cache_->Size()); 229 EXPECT_EQ(0, GetNexe(test_key).compare(test_store_val)); 230 } 231 232 TEST_F(PnaclTranslationCacheTest, GetLargeOnDisk) { 233 InitBackend(false); 234 const std::string large_buffer(kLargeNexeSize, 'a'); 235 StoreNexe(test_key, large_buffer); 236 EXPECT_EQ(1, cache_->Size()); 237 EXPECT_EQ(0, GetNexe(test_key).compare(large_buffer)); 238 } 239 240 TEST_F(PnaclTranslationCacheTest, StoreTwice) { 241 // Test that storing twice with the same key overwrites 242 InitBackend(true); 243 StoreNexe(test_key, test_store_val); 244 StoreNexe(test_key, test_store_val + "aaa"); 245 EXPECT_EQ(1, cache_->Size()); 246 EXPECT_EQ(0, GetNexe(test_key).compare(test_store_val + "aaa")); 247 } 248 249 TEST_F(PnaclTranslationCacheTest, StoreTwo) { 250 InitBackend(true); 251 StoreNexe(test_key, test_store_val); 252 StoreNexe(test_key + "a", test_store_val + "aaa"); 253 EXPECT_EQ(2, cache_->Size()); 254 EXPECT_EQ(0, GetNexe(test_key).compare(test_store_val)); 255 EXPECT_EQ(0, GetNexe(test_key + "a").compare(test_store_val + "aaa")); 256 } 257 258 TEST_F(PnaclTranslationCacheTest, GetMiss) { 259 InitBackend(true); 260 StoreNexe(test_key, test_store_val); 261 TestNexeCallback load_cb; 262 std::string nexe; 263 cache_->GetNexe(test_key + "a", load_cb.callback()); 264 int rv; 265 scoped_refptr<net::DrainableIOBuffer> buf(load_cb.GetResult(&rv)); 266 EXPECT_EQ(net::ERR_FAILED, rv); 267 } 268 269 } // namespace pnacl 270