pnacl_translation_cache_unittest.cc 11.5 KB
Newer Older
1 2 3 4
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
#include "components/nacl/browser/pnacl_translation_cache.h"
6 7 8

#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
9
#include "base/message_loop/message_loop.h"
10
#include "base/run_loop.h"
11
#include "build/build_config.h"
12
#include "components/nacl/common/pnacl_types.h"
13
#include "content/public/browser/browser_thread.h"
14
#include "content/public/test/test_browser_thread_bundle.h"
15
#include "net/base/io_buffer.h"
16 17 18
#include "net/base/test_completion_callback.h"
#include "testing/gtest/include/gtest/gtest.h"

19 20 21 22 23
// For fine-grained suppression on flaky tests.
#if defined(OS_WIN)
#include "base/win/windows_version.h"
#endif

24
using content::BrowserThread;
25
using base::FilePath;
26

27
namespace pnacl {
28

29 30
const int kTestDiskCacheSize = 16 * 1024 * 1024;

31
class PnaclTranslationCacheTest : public testing::Test {
32
 protected:
33
  PnaclTranslationCacheTest()
34
      : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {}
35 36 37
  ~PnaclTranslationCacheTest() override {}
  void SetUp() override { cache_.reset(new PnaclTranslationCache()); }
  void TearDown() override {
38
    // The destructor of PnaclTranslationCacheWriteEntry posts a task to the IO
39 40 41 42 43
    // thread to close the backend cache entry. We want to make sure the entries
    // are closed before we delete the backend (and in particular the destructor
    // for the memory backend has a DCHECK to verify this), so we run the loop
    // here to ensure the task gets processed.
    base::RunLoop().RunUntilIdle();
44
    cache_.reset();
45
  }
46

47 48 49 50
  void InitBackend(bool in_mem);
  void StoreNexe(const std::string& key, const std::string& nexe);
  std::string GetNexe(const std::string& key);

51
  std::unique_ptr<PnaclTranslationCache> cache_;
52
  content::TestBrowserThreadBundle thread_bundle_;
53
  base::ScopedTempDir temp_dir_;
54 55
};

56
void PnaclTranslationCacheTest::InitBackend(bool in_mem) {
57
  net::TestCompletionCallback init_cb;
58 59 60
  if (!in_mem) {
    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
  }
61
  // Use the private init method so we can control the size
62
  int rv = cache_->Init(in_mem ? net::MEMORY_CACHE : net::PNACL_CACHE,
63
                        in_mem ? base::FilePath() : temp_dir_.GetPath(),
64 65
                        in_mem ? kMaxMemCacheSize : kTestDiskCacheSize,
                        init_cb.callback());
66 67
  if (in_mem)
    ASSERT_EQ(net::OK, rv);
68
  ASSERT_EQ(net::OK, init_cb.GetResult(rv));
69 70 71
  ASSERT_EQ(0, cache_->Size());
}

72
void PnaclTranslationCacheTest::StoreNexe(const std::string& key,
73
                                          const std::string& nexe) {
74
  net::TestCompletionCallback store_cb;
75 76
  scoped_refptr<net::DrainableIOBuffer> nexe_buf(
      new net::DrainableIOBuffer(new net::StringIOBuffer(nexe), nexe.size()));
77
  cache_->StoreNexe(key, nexe_buf.get(), store_cb.callback());
78 79 80 81
  // Using ERR_IO_PENDING here causes the callback to wait for the result
  // which should be harmless even if it returns OK immediately. This is because
  // we don't plumb the intermediate writing stages all the way out.
  EXPECT_EQ(net::OK, store_cb.GetResult(net::ERR_IO_PENDING));
82 83
}

84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
// Inspired by net::TestCompletionCallback. Instantiate a TestNexeCallback and
// pass the GetNexeCallback returned by the callback() method to GetNexe.
// Then call GetResult, which will pump the message loop until it gets a result,
// return the resulting IOBuffer and fill in the return value
class TestNexeCallback {
 public:
  TestNexeCallback()
      : have_result_(false),
        result_(-1),
        cb_(base::Bind(&TestNexeCallback::SetResult, base::Unretained(this))) {}
  GetNexeCallback callback() { return cb_; }
  net::DrainableIOBuffer* GetResult(int* result) {
    while (!have_result_)
      base::RunLoop().RunUntilIdle();
    have_result_ = false;
    *result = result_;
    return buf_.get();
  }

 private:
  void SetResult(int rv, scoped_refptr<net::DrainableIOBuffer> buf) {
    have_result_ = true;
    result_ = rv;
    buf_ = buf;
  }
  bool have_result_;
  int result_;
  scoped_refptr<net::DrainableIOBuffer> buf_;
  const GetNexeCallback cb_;
};

115
std::string PnaclTranslationCacheTest::GetNexe(const std::string& key) {
116 117 118 119 120
  TestNexeCallback load_cb;
  cache_->GetNexe(key, load_cb.callback());
  int rv;
  scoped_refptr<net::DrainableIOBuffer> buf(load_cb.GetResult(&rv));
  EXPECT_EQ(net::OK, rv);
121 122
  if (buf.get() == NULL) // for some reason ASSERT macros don't work here.
    return std::string();
123
  std::string nexe(buf->data(), buf->size());
124 125 126 127 128
  return nexe;
}

static const std::string test_key("1");
static const std::string test_store_val("testnexe");
129
static const int kLargeNexeSize = 8 * 1024 * 1024;
130 131 132 133 134 135

TEST(PnaclTranslationCacheKeyTest, CacheKeyTest) {
  nacl::PnaclCacheInfo info;
  info.pexe_url = GURL("http://www.google.com");
  info.abi_version = 0;
  info.opt_level = 0;
136
  info.sandbox_isa = "x86-32";
137
  std::string test_time("Wed, 15 Nov 1995 06:25:24 GMT");
138
  EXPECT_TRUE(base::Time::FromString(test_time.c_str(), &info.last_modified));
139 140
  // Basic check for URL and time components
  EXPECT_EQ("ABI:0;opt:0;URL:http://www.google.com/;"
141 142
            "modified:1995:11:15:6:25:24:0:UTC;etag:;"
            "sandbox:x86-32;extra_flags:;",
143 144 145 146
            PnaclTranslationCache::GetKey(info));
  // Check that query portion of URL is not stripped
  info.pexe_url = GURL("http://www.google.com/?foo=bar");
  EXPECT_EQ("ABI:0;opt:0;URL:http://www.google.com/?foo=bar;"
147 148
            "modified:1995:11:15:6:25:24:0:UTC;etag:;"
            "sandbox:x86-32;extra_flags:;",
149 150 151 152
            PnaclTranslationCache::GetKey(info));
  // Check that username, password, and normal port are stripped
  info.pexe_url = GURL("https://user:host@www.google.com:443/");
  EXPECT_EQ("ABI:0;opt:0;URL:https://www.google.com/;"
153 154
            "modified:1995:11:15:6:25:24:0:UTC;etag:;"
            "sandbox:x86-32;extra_flags:;",
155 156 157 158
            PnaclTranslationCache::GetKey(info));
  // Check that unusual port is not stripped but ref is stripped
  info.pexe_url = GURL("https://www.google.com:444/#foo");
  EXPECT_EQ("ABI:0;opt:0;URL:https://www.google.com:444/;"
159 160
            "modified:1995:11:15:6:25:24:0:UTC;etag:;"
            "sandbox:x86-32;extra_flags:;",
161 162 163 164 165
            PnaclTranslationCache::GetKey(info));
  // Check chrome-extesnsion scheme
  info.pexe_url = GURL("chrome-extension://ljacajndfccfgnfohlgkdphmbnpkjflk/");
  EXPECT_EQ("ABI:0;opt:0;"
            "URL:chrome-extension://ljacajndfccfgnfohlgkdphmbnpkjflk/;"
166 167
            "modified:1995:11:15:6:25:24:0:UTC;etag:;"
            "sandbox:x86-32;extra_flags:;",
168 169 170 171 172
            PnaclTranslationCache::GetKey(info));
  // Check that ABI version, opt level, and etag are in the key
  info.pexe_url = GURL("http://www.google.com/");
  info.abi_version = 2;
  EXPECT_EQ("ABI:2;opt:0;URL:http://www.google.com/;"
173 174
            "modified:1995:11:15:6:25:24:0:UTC;etag:;"
            "sandbox:x86-32;extra_flags:;",
175 176 177
            PnaclTranslationCache::GetKey(info));
  info.opt_level = 2;
  EXPECT_EQ("ABI:2;opt:2;URL:http://www.google.com/;"
178 179
            "modified:1995:11:15:6:25:24:0:UTC;etag:;"
            "sandbox:x86-32;extra_flags:;",
180
            PnaclTranslationCache::GetKey(info));
181 182 183 184 185 186 187
  // Check that Subzero gets a different cache key.
  info.use_subzero = true;
  EXPECT_EQ("ABI:2;opt:2subzero;URL:http://www.google.com/;"
            "modified:1995:11:15:6:25:24:0:UTC;etag:;"
            "sandbox:x86-32;extra_flags:;",
            PnaclTranslationCache::GetKey(info));
  info.use_subzero = false;
188 189
  info.etag = std::string("etag");
  EXPECT_EQ("ABI:2;opt:2;URL:http://www.google.com/;"
190 191 192 193 194 195 196 197
            "modified:1995:11:15:6:25:24:0:UTC;etag:etag;"
            "sandbox:x86-32;extra_flags:;",
            PnaclTranslationCache::GetKey(info));

  info.extra_flags = "-mavx-neon";
  EXPECT_EQ("ABI:2;opt:2;URL:http://www.google.com/;"
            "modified:1995:11:15:6:25:24:0:UTC;etag:etag;"
            "sandbox:x86-32;extra_flags:-mavx-neon;",
198 199 200 201 202
            PnaclTranslationCache::GetKey(info));

  // Check for all the time components, and null time
  info.last_modified = base::Time();
  EXPECT_EQ("ABI:2;opt:2;URL:http://www.google.com/;"
203 204
            "modified:0:0:0:0:0:0:0:UTC;etag:etag;"
            "sandbox:x86-32;extra_flags:-mavx-neon;",
205 206
            PnaclTranslationCache::GetKey(info));
  test_time.assign("Fri, 29 Feb 2008 13:04:12 GMT");
207
  EXPECT_TRUE(base::Time::FromString(test_time.c_str(), &info.last_modified));
208
  EXPECT_EQ("ABI:2;opt:2;URL:http://www.google.com/;"
209 210
            "modified:2008:2:29:13:4:12:0:UTC;etag:etag;"
            "sandbox:x86-32;extra_flags:-mavx-neon;",
211 212
            PnaclTranslationCache::GetKey(info));
}
213

214
TEST_F(PnaclTranslationCacheTest, StoreSmallInMem) {
215 216 217
  // Test that a single store puts something in the mem backend
  InitBackend(true);
  StoreNexe(test_key, test_store_val);
218 219 220
  EXPECT_EQ(1, cache_->Size());
}

221
TEST_F(PnaclTranslationCacheTest, StoreSmallOnDisk) {
222 223 224 225 226 227
  // Test that a single store puts something in the disk backend
  InitBackend(false);
  StoreNexe(test_key, test_store_val);
  EXPECT_EQ(1, cache_->Size());
}

228
TEST_F(PnaclTranslationCacheTest, StoreLargeOnDisk) {
229 230 231 232
  // Test a value too large(?) for a single I/O operation
  InitBackend(false);
  const std::string large_buffer(kLargeNexeSize, 'a');
  StoreNexe(test_key, large_buffer);
233 234 235
  EXPECT_EQ(1, cache_->Size());
}

236
TEST_F(PnaclTranslationCacheTest, InMemSizeLimit) {
237
  InitBackend(true);
238 239 240
  scoped_refptr<net::DrainableIOBuffer> large_buffer(new net::DrainableIOBuffer(
      new net::StringIOBuffer(std::string(kMaxMemCacheSize + 1, 'a')),
      kMaxMemCacheSize + 1));
241
  net::TestCompletionCallback store_cb;
242
  cache_->StoreNexe(test_key, large_buffer.get(), store_cb.callback());
243
  EXPECT_EQ(net::ERR_FAILED, store_cb.GetResult(net::ERR_IO_PENDING));
244
  base::RunLoop().RunUntilIdle();  // Ensure the entry is closed.
245
  EXPECT_EQ(0, cache_->Size());
246 247
}

248
TEST_F(PnaclTranslationCacheTest, GetOneInMem) {
249 250 251 252 253 254
  InitBackend(true);
  StoreNexe(test_key, test_store_val);
  EXPECT_EQ(1, cache_->Size());
  EXPECT_EQ(0, GetNexe(test_key).compare(test_store_val));
}

255 256 257 258 259 260 261
TEST_F(PnaclTranslationCacheTest, GetOneOnDisk) {
  InitBackend(false);
  StoreNexe(test_key, test_store_val);
  EXPECT_EQ(1, cache_->Size());
  EXPECT_EQ(0, GetNexe(test_key).compare(test_store_val));
}

262
TEST_F(PnaclTranslationCacheTest, GetLargeOnDisk) {
263 264 265 266 267
#if defined(OS_WIN)
  // Flaky on XP bot http://crbug.com/468741
  if (base::win::GetVersion() <= base::win::VERSION_XP)
    return;
#endif
268 269 270 271 272 273 274
  InitBackend(false);
  const std::string large_buffer(kLargeNexeSize, 'a');
  StoreNexe(test_key, large_buffer);
  EXPECT_EQ(1, cache_->Size());
  EXPECT_EQ(0, GetNexe(test_key).compare(large_buffer));
}

275
TEST_F(PnaclTranslationCacheTest, StoreTwice) {
276 277 278 279 280 281 282 283
  // Test that storing twice with the same key overwrites
  InitBackend(true);
  StoreNexe(test_key, test_store_val);
  StoreNexe(test_key, test_store_val + "aaa");
  EXPECT_EQ(1, cache_->Size());
  EXPECT_EQ(0, GetNexe(test_key).compare(test_store_val + "aaa"));
}

284
TEST_F(PnaclTranslationCacheTest, StoreTwo) {
285 286 287 288 289 290 291 292
  InitBackend(true);
  StoreNexe(test_key, test_store_val);
  StoreNexe(test_key + "a", test_store_val + "aaa");
  EXPECT_EQ(2, cache_->Size());
  EXPECT_EQ(0, GetNexe(test_key).compare(test_store_val));
  EXPECT_EQ(0, GetNexe(test_key + "a").compare(test_store_val + "aaa"));
}

293
TEST_F(PnaclTranslationCacheTest, GetMiss) {
294 295
  InitBackend(true);
  StoreNexe(test_key, test_store_val);
296
  TestNexeCallback load_cb;
297
  std::string nexe;
298 299 300 301
  cache_->GetNexe(test_key + "a", load_cb.callback());
  int rv;
  scoped_refptr<net::DrainableIOBuffer> buf(load_cb.GetResult(&rv));
  EXPECT_EQ(net::ERR_FAILED, rv);
302 303
}

304
}  // namespace pnacl