device_local_account_external_policy_loader_unittest.cc revision effb81e5f8246d0db0270817048dc992db66e9fb
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/common/chrome_paths.h"
23#include "chrome/common/extensions/extension_constants.h"
24#include "chrome/test/base/testing_browser_process.h"
25#include "components/policy/core/common/cloud/mock_cloud_policy_store.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/test/test_browser_thread_bundle.h"
31#include "content/public/test/test_utils.h"
32#include "extensions/browser/external_provider_interface.h"
33#include "extensions/common/extension.h"
34#include "extensions/common/manifest.h"
35#include "net/url_request/test_url_fetcher_factory.h"
36#include "net/url_request/url_fetcher_delegate.h"
37#include "net/url_request/url_request_context_getter.h"
38#include "net/url_request/url_request_test_util.h"
39#include "policy/policy_constants.h"
40#include "testing/gmock/include/gmock/gmock.h"
41#include "testing/gtest/include/gtest/gtest.h"
42#include "url/gurl.h"
43
44using ::testing::InvokeWithoutArgs;
45using ::testing::Mock;
46using ::testing::_;
47
48namespace chromeos {
49
50namespace {
51
52const char kCacheDir[] = "cache";
53const char kExtensionId[] = "ldnnhddmnhbkjipkidpdiheffobcpfmf";
54const char kExtensionUpdateManifest[] =
55    "extensions/good_v1_update_manifest.xml";
56const char kExtensionCRXSourceDir[] = "extensions";
57const char kExtensionCRXFile[] = "good.crx";
58const char kExtensionCRXVersion[] = "1.0.0.0";
59
60class MockExternalPolicyProviderVisitor
61    : public extensions::ExternalProviderInterface::VisitorInterface {
62 public:
63  MockExternalPolicyProviderVisitor();
64  virtual ~MockExternalPolicyProviderVisitor();
65
66  MOCK_METHOD6(OnExternalExtensionFileFound,
67               bool(const std::string&,
68                    const base::Version*,
69                    const base::FilePath&,
70                    extensions::Manifest::Location,
71                    int,
72                    bool));
73  MOCK_METHOD6(OnExternalExtensionUpdateUrlFound,
74               bool(const std::string&,
75                    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  content::InProcessUtilityThreadHelper in_process_utility_thread_helper_;
118};
119
120DeviceLocalAccountExternalPolicyLoaderTest::
121    DeviceLocalAccountExternalPolicyLoaderTest()
122    : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {
123}
124
125DeviceLocalAccountExternalPolicyLoaderTest::
126    ~DeviceLocalAccountExternalPolicyLoaderTest() {
127}
128
129void DeviceLocalAccountExternalPolicyLoaderTest::SetUp() {
130  ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
131  cache_dir_ = temp_dir_.path().Append(kCacheDir);
132  ASSERT_TRUE(base::CreateDirectoryAndGetError(cache_dir_, NULL));
133  request_context_getter_ =
134      new net::TestURLRequestContextGetter(base::MessageLoopProxy::current());
135  TestingBrowserProcess::GetGlobal()->SetSystemRequestContext(
136      request_context_getter_.get());
137  ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_dir_));
138
139  loader_ = new DeviceLocalAccountExternalPolicyLoader(&store_, cache_dir_);
140  provider_.reset(new extensions::ExternalProviderImpl(
141      &visitor_,
142      loader_,
143      NULL,
144      extensions::Manifest::EXTERNAL_POLICY,
145      extensions::Manifest::EXTERNAL_POLICY_DOWNLOAD,
146      extensions::Extension::NO_FLAGS));
147
148  VerifyAndResetVisitorCallExpectations();
149}
150
151void DeviceLocalAccountExternalPolicyLoaderTest::TearDown() {
152  TestingBrowserProcess::GetGlobal()->SetSystemRequestContext(NULL);
153}
154
155void DeviceLocalAccountExternalPolicyLoaderTest::
156    VerifyAndResetVisitorCallExpectations() {
157  Mock::VerifyAndClearExpectations(&visitor_);
158  EXPECT_CALL(visitor_, OnExternalExtensionFileFound(_, _, _, _, _, _))
159      .Times(0);
160  EXPECT_CALL(visitor_, OnExternalExtensionUpdateUrlFound(_, _, _, _, _, _))
161      .Times(0);
162  EXPECT_CALL(visitor_, OnExternalProviderReady(_))
163      .Times(0);
164}
165
166void DeviceLocalAccountExternalPolicyLoaderTest::SetForceInstallListPolicy() {
167  scoped_ptr<base::ListValue> forcelist(new base::ListValue);
168  forcelist->AppendString("invalid");
169  forcelist->AppendString(base::StringPrintf(
170      "%s;%s",
171      kExtensionId,
172      extension_urls::GetWebstoreUpdateUrl().spec().c_str()));
173  store_.policy_map_.Set(policy::key::kExtensionInstallForcelist,
174                         policy::POLICY_LEVEL_MANDATORY,
175                         policy::POLICY_SCOPE_USER,
176                         forcelist.release(),
177                         NULL);
178  store_.NotifyStoreLoaded();
179}
180
181// Verifies that when the cache is not explicitly started, the loader does not
182// serve any extensions, even if the force-install list policy is set or a load
183// is manually requested.
184TEST_F(DeviceLocalAccountExternalPolicyLoaderTest, CacheNotStarted) {
185  // Set the force-install list policy.
186  SetForceInstallListPolicy();
187
188  // Manually request a load.
189  loader_->StartLoading();
190
191  EXPECT_FALSE(loader_->IsCacheRunning());
192  EXPECT_TRUE(base::MessageLoop::current()->IsIdleForTesting());
193}
194
195// Verifies that the cache can be started and stopped correctly.
196TEST_F(DeviceLocalAccountExternalPolicyLoaderTest, ForceInstallListEmpty) {
197  // Set an empty force-install list policy.
198  store_.NotifyStoreLoaded();
199
200  // Start the cache. Verify that the loader announces an empty extension list.
201  EXPECT_CALL(visitor_, OnExternalProviderReady(provider_.get()))
202      .Times(1);
203  loader_->StartCache(base::MessageLoopProxy::current());
204  base::RunLoop().RunUntilIdle();
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