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