1// Copyright 2014 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/extensions/updater/local_extension_cache.h"
6
7#include "base/bind.h"
8#include "base/files/file_path.h"
9#include "base/files/file_util.h"
10#include "base/files/scoped_temp_dir.h"
11#include "base/run_loop.h"
12#include "base/test/sequenced_worker_pool_owner.h"
13#include "base/values.h"
14#include "chrome/common/extensions/extension_constants.h"
15#include "content/public/browser/browser_thread.h"
16#include "content/public/test/test_browser_thread_bundle.h"
17#include "content/public/test/test_utils.h"
18#include "testing/gtest/include/gtest/gtest.h"
19
20namespace {
21
22const char kTestExtensionId1[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
23const char kTestExtensionId2[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
24const char kTestExtensionId3[] = "cccccccccccccccccccccccccccccccc";
25
26}  // namespace
27
28namespace extensions {
29
30class LocalExtensionCacheTest : public testing::Test {
31 public:
32  LocalExtensionCacheTest() {}
33  virtual ~LocalExtensionCacheTest() {}
34
35  scoped_refptr<base::SequencedTaskRunner> background_task_runner() {
36    return background_task_runner_;
37  }
38
39  // testing::Test overrides:
40  virtual void SetUp() OVERRIDE {
41    pool_owner_.reset(
42        new base::SequencedWorkerPoolOwner(3, "Background Pool"));
43    background_task_runner_ = pool_owner_->pool()->GetSequencedTaskRunner(
44        pool_owner_->pool()->GetNamedSequenceToken("background"));
45  }
46
47  virtual void TearDown() OVERRIDE {
48    pool_owner_->pool()->Shutdown();
49    base::RunLoop().RunUntilIdle();
50  }
51
52  base::FilePath CreateCacheDir(bool initialized) {
53    EXPECT_TRUE(cache_dir_.CreateUniqueTempDir());
54    if (initialized)
55      CreateFlagFile(cache_dir_.path());
56    return cache_dir_.path();
57  }
58
59  base::FilePath CreateTempDir() {
60    EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
61    return temp_dir_.path();
62  }
63
64  void CreateFlagFile(const base::FilePath& dir) {
65    CreateFile(dir.Append(
66        extensions::LocalExtensionCache::kCacheReadyFlagFileName),
67        0, base::Time::Now());
68  }
69
70  void CreateExtensionFile(const base::FilePath& dir,
71                           const std::string& id,
72                           const std::string& version,
73                           size_t size,
74                           const base::Time& timestamp) {
75    CreateFile(GetExtensionFileName(dir, id, version), size, timestamp);
76  }
77
78  void CreateFile(const base::FilePath& file,
79                  size_t size,
80                  const base::Time& timestamp) {
81    std::string data(size, 0);
82    EXPECT_EQ(base::WriteFile(file, data.data(), data.size()), int(size));
83    EXPECT_TRUE(base::TouchFile(file, timestamp, timestamp));
84  }
85
86  base::FilePath GetExtensionFileName(const base::FilePath& dir,
87                                      const std::string& id,
88                                      const std::string& version) {
89    return dir.Append(id + "-" + version + ".crx");
90  }
91
92  void WaitForCompletion() {
93    // In the worst case you need to repeat this up to 3 times to make sure that
94    // all pending tasks we sent from UI thread to task runner and back to UI.
95    for (int i = 0; i < 3; i++) {
96      // Wait for background task completion that sends replay to UI thread.
97      pool_owner_->pool()->FlushForTesting();
98      // Wait for UI thread task completion.
99      base::RunLoop().RunUntilIdle();
100    }
101  }
102
103 private:
104  content::TestBrowserThreadBundle thread_bundle_;
105
106  scoped_ptr<base::SequencedWorkerPoolOwner> pool_owner_;
107  scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
108
109  base::ScopedTempDir cache_dir_;
110  base::ScopedTempDir temp_dir_;
111
112  DISALLOW_COPY_AND_ASSIGN(LocalExtensionCacheTest);
113};
114
115static void SimpleCallback(bool* ptr) {
116  *ptr = true;
117}
118
119TEST_F(LocalExtensionCacheTest, Basic) {
120  base::FilePath cache_dir(CreateCacheDir(false));
121
122  LocalExtensionCache cache(cache_dir,
123                            1000,
124                            base::TimeDelta::FromDays(30),
125                            background_task_runner());
126  cache.SetCacheStatusPollingDelayForTests(base::TimeDelta());
127
128  bool initialized = false;
129  cache.Init(true, base::Bind(&SimpleCallback, &initialized));
130
131  WaitForCompletion();
132  EXPECT_FALSE(initialized);
133
134  CreateExtensionFile(cache_dir, kTestExtensionId1, "1.0", 100,
135                      base::Time::Now() - base::TimeDelta::FromDays(1));
136  CreateExtensionFile(cache_dir, kTestExtensionId1, "0.1", 100,
137                      base::Time::Now() - base::TimeDelta::FromDays(10));
138  CreateExtensionFile(cache_dir, kTestExtensionId2, "2.0", 100,
139                      base::Time::Now() - base::TimeDelta::FromDays(40));
140  CreateExtensionFile(cache_dir, kTestExtensionId3, "3.0", 900,
141                      base::Time::Now() - base::TimeDelta::FromDays(41));
142
143  CreateFlagFile(cache_dir);
144
145  WaitForCompletion();
146  ASSERT_TRUE(initialized);
147
148  // Older version should be removed on cache initialization.
149  EXPECT_FALSE(base::PathExists(
150      GetExtensionFileName(cache_dir, kTestExtensionId1, "0.1")));
151
152  // All extensions should be there because cleanup happens on shutdown to
153  // support use case when device was not used to more than 30 days and cache
154  // shouldn't be cleaned before someone will have a chance to use it.
155  EXPECT_TRUE(cache.GetExtension(kTestExtensionId1, NULL, NULL));
156  EXPECT_TRUE(cache.GetExtension(kTestExtensionId2, NULL, NULL));
157  EXPECT_TRUE(cache.GetExtension(kTestExtensionId3, NULL, NULL));
158
159  bool did_shutdown = false;
160  cache.Shutdown(base::Bind(&SimpleCallback, &did_shutdown));
161  WaitForCompletion();
162  ASSERT_TRUE(did_shutdown);
163
164  EXPECT_TRUE(base::PathExists(
165      GetExtensionFileName(cache_dir, kTestExtensionId1, "1.0")));
166  EXPECT_FALSE(base::PathExists(
167      GetExtensionFileName(cache_dir, kTestExtensionId2, "2.0")));
168  EXPECT_FALSE(base::PathExists(
169      GetExtensionFileName(cache_dir, kTestExtensionId3, "3.0")));
170}
171
172}  // namespace extensions
173