device_local_account_external_policy_loader_unittest.cc revision f2477e01787aa58f445919b809d89e252beef54f
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/chromeos/extensions/device_local_account_external_policy_loader.h"
6
7#include <string>
8
9#include "base/callback.h"
10#include "base/file_util.h"
11#include "base/files/scoped_temp_dir.h"
12#include "base/message_loop/message_loop.h"
13#include "base/message_loop/message_loop_proxy.h"
14#include "base/path_service.h"
15#include "base/run_loop.h"
16#include "base/strings/stringprintf.h"
17#include "base/values.h"
18#include "base/version.h"
19#include "chrome/browser/chrome_notification_types.h"
20#include "chrome/browser/extensions/external_provider_impl.h"
21#include "chrome/browser/extensions/updater/extension_downloader.h"
22#include "chrome/browser/policy/cloud/mock_cloud_policy_store.h"
23#include "chrome/common/chrome_paths.h"
24#include "chrome/common/extensions/extension_constants.h"
25#include "chrome/test/base/testing_browser_process.h"
26#include "components/policy/core/common/policy_map.h"
27#include "components/policy/core/common/policy_types.h"
28#include "content/public/browser/notification_service.h"
29#include "content/public/browser/notification_source.h"
30#include "content/public/browser/render_process_host.h"
31#include "content/public/test/test_browser_thread_bundle.h"
32#include "content/public/test/test_utils.h"
33#include "extensions/browser/external_provider_interface.h"
34#include "extensions/common/extension.h"
35#include "extensions/common/manifest.h"
36#include "net/url_request/test_url_fetcher_factory.h"
37#include "net/url_request/url_fetcher_delegate.h"
38#include "net/url_request/url_request_context_getter.h"
39#include "net/url_request/url_request_test_util.h"
40#include "policy/policy_constants.h"
41#include "testing/gmock/include/gmock/gmock.h"
42#include "testing/gtest/include/gtest/gtest.h"
43#include "url/gurl.h"
44
45using ::testing::InvokeWithoutArgs;
46using ::testing::Mock;
47using ::testing::_;
48
49namespace chromeos {
50
51namespace {
52
53const char kCacheDir[] = "cache";
54const char kExtensionId[] = "ldnnhddmnhbkjipkidpdiheffobcpfmf";
55const char kExtensionUpdateManifest[] =
56    "extensions/good_v1_update_manifest.xml";
57const char kExtensionCRXSourceDir[] = "extensions";
58const char kExtensionCRXFile[] = "good.crx";
59const char kExtensionCRXVersion[] = "1.0.0.0";
60
61class MockExternalPolicyProviderVisitor
62    : public extensions::ExternalProviderInterface::VisitorInterface {
63 public:
64  MockExternalPolicyProviderVisitor();
65  virtual ~MockExternalPolicyProviderVisitor();
66
67  MOCK_METHOD6(OnExternalExtensionFileFound,
68               bool(const std::string&,
69                    const base::Version*,
70                    const base::FilePath&,
71                    extensions::Manifest::Location,
72                    int,
73                    bool));
74  MOCK_METHOD5(OnExternalExtensionUpdateUrlFound,
75               bool(const std::string&,
76                    const GURL&,
77                    extensions::Manifest::Location,
78                    int,
79                    bool));
80  MOCK_METHOD1(OnExternalProviderReady,
81               void(const extensions::ExternalProviderInterface* provider));
82
83 private:
84  DISALLOW_COPY_AND_ASSIGN(MockExternalPolicyProviderVisitor);
85};
86
87MockExternalPolicyProviderVisitor::MockExternalPolicyProviderVisitor() {
88}
89
90MockExternalPolicyProviderVisitor::~MockExternalPolicyProviderVisitor() {
91}
92
93}  // namespace
94
95class DeviceLocalAccountExternalPolicyLoaderTest : public testing::Test {
96 protected:
97  DeviceLocalAccountExternalPolicyLoaderTest();
98  virtual ~DeviceLocalAccountExternalPolicyLoaderTest();
99
100  virtual void SetUp() OVERRIDE;
101  virtual void TearDown() OVERRIDE;
102
103  void VerifyAndResetVisitorCallExpectations();
104  void SetForceInstallListPolicy();
105
106  content::TestBrowserThreadBundle thread_bundle_;
107  base::ScopedTempDir temp_dir_;
108  base::FilePath cache_dir_;
109  policy::MockCloudPolicyStore store_;
110  scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
111  base::FilePath test_dir_;
112
113  scoped_refptr<DeviceLocalAccountExternalPolicyLoader> loader_;
114  MockExternalPolicyProviderVisitor visitor_;
115  scoped_ptr<extensions::ExternalProviderImpl> provider_;
116};
117
118DeviceLocalAccountExternalPolicyLoaderTest::
119    DeviceLocalAccountExternalPolicyLoaderTest()
120    : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {
121}
122
123DeviceLocalAccountExternalPolicyLoaderTest::
124    ~DeviceLocalAccountExternalPolicyLoaderTest() {
125}
126
127void DeviceLocalAccountExternalPolicyLoaderTest::SetUp() {
128  ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
129  cache_dir_ = temp_dir_.path().Append(kCacheDir);
130  ASSERT_TRUE(file_util::CreateDirectoryAndGetError(cache_dir_, NULL));
131  request_context_getter_ =
132      new net::TestURLRequestContextGetter(base::MessageLoopProxy::current());
133  TestingBrowserProcess::GetGlobal()->SetSystemRequestContext(
134      request_context_getter_.get());
135  ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_dir_));
136
137  loader_ = new DeviceLocalAccountExternalPolicyLoader(&store_, cache_dir_);
138  provider_.reset(new extensions::ExternalProviderImpl(
139      &visitor_,
140      loader_,
141      NULL,
142      extensions::Manifest::EXTERNAL_POLICY,
143      extensions::Manifest::EXTERNAL_POLICY_DOWNLOAD,
144      extensions::Extension::NO_FLAGS));
145
146  content::RenderProcessHost::SetRunRendererInProcess(true);
147
148  VerifyAndResetVisitorCallExpectations();
149}
150
151void DeviceLocalAccountExternalPolicyLoaderTest::TearDown() {
152  content::RenderProcessHost::SetRunRendererInProcess(false);
153  TestingBrowserProcess::GetGlobal()->SetSystemRequestContext(NULL);
154}
155
156void DeviceLocalAccountExternalPolicyLoaderTest::
157    VerifyAndResetVisitorCallExpectations() {
158  Mock::VerifyAndClearExpectations(&visitor_);
159  EXPECT_CALL(visitor_, OnExternalExtensionFileFound(_, _, _, _, _, _))
160      .Times(0);
161  EXPECT_CALL(visitor_, OnExternalExtensionUpdateUrlFound(_, _, _, _, _))
162      .Times(0);
163  EXPECT_CALL(visitor_, OnExternalProviderReady(_))
164      .Times(0);
165}
166
167void DeviceLocalAccountExternalPolicyLoaderTest::SetForceInstallListPolicy() {
168  scoped_ptr<base::ListValue> forcelist(new base::ListValue);
169  forcelist->AppendString("invalid");
170  forcelist->AppendString(base::StringPrintf(
171      "%s;%s",
172      kExtensionId,
173      extension_urls::GetWebstoreUpdateUrl().spec().c_str()));
174  store_.policy_map_.Set(policy::key::kExtensionInstallForcelist,
175                         policy::POLICY_LEVEL_MANDATORY,
176                         policy::POLICY_SCOPE_USER,
177                         forcelist.release(),
178                         NULL);
179  store_.NotifyStoreLoaded();
180}
181
182// Verifies that when the cache is not explicitly started, the loader does not
183// serve any extensions, even if the force-install list policy is set or a load
184// is manually requested.
185TEST_F(DeviceLocalAccountExternalPolicyLoaderTest, CacheNotStarted) {
186  // Set the force-install list policy.
187  SetForceInstallListPolicy();
188
189  // Manually request a load.
190  loader_->StartLoading();
191
192  EXPECT_FALSE(loader_->IsCacheRunning());
193  EXPECT_TRUE(base::MessageLoop::current()->IsIdleForTesting());
194}
195
196// Verifies that the cache can be started and stopped correctly.
197TEST_F(DeviceLocalAccountExternalPolicyLoaderTest, ForceInstallListEmpty) {
198  // Set an empty force-install list policy.
199  store_.NotifyStoreLoaded();
200
201  // Start the cache. Verify that the loader announces an empty extension list.
202  EXPECT_CALL(visitor_, OnExternalProviderReady(provider_.get()))
203      .Times(1);
204  loader_->StartCache(base::MessageLoopProxy::current());
205  VerifyAndResetVisitorCallExpectations();
206
207  // Stop the cache. Verify that the loader announces an empty extension list.
208  EXPECT_CALL(visitor_, OnExternalProviderReady(provider_.get()))
209      .Times(1);
210  base::RunLoop run_loop;
211  loader_->StopCache(run_loop.QuitClosure());
212  VerifyAndResetVisitorCallExpectations();
213
214  // Spin the loop until the cache shutdown callback is invoked. Verify that at
215  // that point, no further file I/O tasks are pending.
216  run_loop.Run();
217  EXPECT_TRUE(base::MessageLoop::current()->IsIdleForTesting());
218}
219
220// Verifies that when a force-install list policy referencing an extension is
221// set and the cache is started, the loader downloads, caches and serves the
222// extension.
223TEST_F(DeviceLocalAccountExternalPolicyLoaderTest, ForceInstallListSet) {
224  // Set a force-install list policy that contains an invalid entry (which
225  // should be ignored) and a valid reference to an extension.
226  SetForceInstallListPolicy();
227
228  // Start the cache.
229  loader_->StartCache(base::MessageLoopProxy::current());
230
231  // Spin the loop, allowing the loader to process the force-install list.
232  // Verify that the loader announces an empty extension list.
233  net::TestURLFetcherFactory factory;
234  EXPECT_CALL(visitor_, OnExternalProviderReady(provider_.get()))
235      .Times(1);
236  base::MessageLoop::current()->RunUntilIdle();
237
238  // Verify that a downloader has started and is attempting to download an
239  // update manifest.
240  net::TestURLFetcher* fetcher = factory.GetFetcherByID(
241      extensions::ExtensionDownloader::kManifestFetcherId);
242  ASSERT_TRUE(fetcher);
243  ASSERT_TRUE(fetcher->delegate());
244
245  // Return a manifest to the downloader.
246  std::string manifest;
247  EXPECT_TRUE(base::ReadFileToString(test_dir_.Append(kExtensionUpdateManifest),
248                                     &manifest));
249  fetcher->set_response_code(200);
250  fetcher->SetResponseString(manifest);
251  fetcher->delegate()->OnURLFetchComplete(fetcher);
252
253  // Wait for the manifest to be parsed.
254  content::WindowedNotificationObserver(
255      chrome::NOTIFICATION_EXTENSION_UPDATE_FOUND,
256      content::NotificationService::AllSources()).Wait();
257
258  // Verify that the downloader is attempting to download a CRX file.
259  fetcher = factory.GetFetcherByID(
260      extensions::ExtensionDownloader::kExtensionFetcherId);
261  ASSERT_TRUE(fetcher);
262  ASSERT_TRUE(fetcher->delegate());
263
264  // Create a temporary CRX file and return its path to the downloader.
265  EXPECT_TRUE(base::CopyFile(
266      test_dir_.Append(kExtensionCRXSourceDir).Append(kExtensionCRXFile),
267      temp_dir_.path().Append(kExtensionCRXFile)));
268  fetcher->set_response_code(200);
269  fetcher->SetResponseFilePath(temp_dir_.path().Append(kExtensionCRXFile));
270  fetcher->delegate()->OnURLFetchComplete(fetcher);
271
272  // Spin the loop. Verify that the loader announces the presence of a new CRX
273  // file, served from the cache directory.
274  const base::FilePath cached_crx_path = cache_dir_.Append(base::StringPrintf(
275      "%s-%s.crx", kExtensionId, kExtensionCRXVersion));
276  base::RunLoop cache_run_loop;
277  EXPECT_CALL(visitor_, OnExternalExtensionFileFound(
278      kExtensionId,
279      _,
280      cached_crx_path,
281      extensions::Manifest::EXTERNAL_POLICY,
282      _,
283      _));
284  EXPECT_CALL(visitor_, OnExternalProviderReady(provider_.get()))
285      .Times(1)
286      .WillOnce(InvokeWithoutArgs(&cache_run_loop, &base::RunLoop::Quit));
287  cache_run_loop.Run();
288  VerifyAndResetVisitorCallExpectations();
289
290  // Verify that the CRX file actually exists in the cache directory and its
291  // contents matches the file returned to the downloader.
292  EXPECT_TRUE(base::ContentsEqual(
293      test_dir_.Append(kExtensionCRXSourceDir).Append(kExtensionCRXFile),
294      cached_crx_path));
295
296  // Stop the cache. Verify that the loader announces an empty extension list.
297  EXPECT_CALL(visitor_, OnExternalProviderReady(provider_.get()))
298      .Times(1);
299  base::RunLoop shutdown_run_loop;
300  loader_->StopCache(shutdown_run_loop.QuitClosure());
301  VerifyAndResetVisitorCallExpectations();
302
303  // Spin the loop until the cache shutdown callback is invoked. Verify that at
304  // that point, no further file I/O tasks are pending.
305  shutdown_run_loop.Run();
306  EXPECT_TRUE(base::MessageLoop::current()->IsIdleForTesting());
307}
308
309}  // namespace chromeos
310