external_cache_unittest.cc revision 4e180b6a0b4720a9b8e9e959a882386f690f08ff
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/external_cache.h" 6 7#include <set> 8 9#include "base/file_util.h" 10#include "base/files/file_path.h" 11#include "base/files/scoped_temp_dir.h" 12#include "base/run_loop.h" 13#include "base/test/sequenced_worker_pool_owner.h" 14#include "base/values.h" 15#include "chrome/browser/extensions/external_provider_impl.h" 16#include "chrome/common/extensions/extension_constants.h" 17#include "content/public/browser/browser_thread.h" 18#include "content/public/test/test_browser_thread_bundle.h" 19#include "net/url_request/test_url_fetcher_factory.h" 20#include "net/url_request/url_fetcher_impl.h" 21#include "net/url_request/url_request_test_util.h" 22#include "testing/gtest/include/gtest/gtest.h" 23 24namespace { 25 26const char kTestExtensionId1[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; 27const char kTestExtensionId2[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"; 28const char kTestExtensionId3[] = "cccccccccccccccccccccccccccccccc"; 29const char kTestExtensionId4[] = "dddddddddddddddddddddddddddddddd"; 30const char kNonWebstoreUpdateUrl[] = "https://localhost/service/update2/crx"; 31 32} // namespace 33 34namespace chromeos { 35 36class ExternalCacheTest : public testing::Test, 37 public ExternalCache::Delegate { 38 public: 39 ExternalCacheTest() 40 : thread_bundle_(content::TestBrowserThreadBundle::REAL_IO_THREAD) { 41 } 42 virtual ~ExternalCacheTest() {} 43 44 scoped_refptr<base::SequencedTaskRunner> background_task_runner() { 45 return background_task_runner_; 46 } 47 48 net::URLRequestContextGetter* request_context_getter() { 49 return request_context_getter_.get(); 50 } 51 52 const base::DictionaryValue* provided_prefs() { 53 return prefs_.get(); 54 } 55 56 // testing::Test overrides: 57 virtual void SetUp() OVERRIDE { 58 request_context_getter_ = new net::TestURLRequestContextGetter( 59 content::BrowserThread::GetMessageLoopProxyForThread( 60 content::BrowserThread::IO)); 61 fetcher_factory_.reset(new net::TestURLFetcherFactory()); 62 63 pool_owner_.reset( 64 new base::SequencedWorkerPoolOwner(3, "Background Pool")); 65 background_task_runner_ = pool_owner_->pool()->GetSequencedTaskRunner( 66 pool_owner_->pool()->GetNamedSequenceToken("background")); 67 } 68 69 virtual void TearDown() OVERRIDE { 70 pool_owner_->pool()->Shutdown(); 71 base::RunLoop().RunUntilIdle(); 72 } 73 74 // ExternalCache::Delegate: 75 virtual void OnExtensionListsUpdated( 76 const base::DictionaryValue* prefs) OVERRIDE { 77 prefs_.reset(prefs->DeepCopy()); 78 } 79 80 base::FilePath CreateCacheDir(bool initialized) { 81 EXPECT_TRUE(cache_dir_.CreateUniqueTempDir()); 82 if (initialized) 83 CreateFlagFile(cache_dir_.path()); 84 return cache_dir_.path(); 85 } 86 87 base::FilePath CreateTempDir() { 88 EXPECT_TRUE(temp_dir_.CreateUniqueTempDir()); 89 return temp_dir_.path(); 90 } 91 92 void CreateFlagFile(const base::FilePath& dir) { 93 CreateFile(dir.Append(ExternalCache::kCacheReadyFlagFileName)); 94 } 95 96 void CreateExtensionFile(const base::FilePath& dir, 97 const std::string& id, 98 const std::string& version) { 99 CreateFile(GetExtensionFile(dir, id, version)); 100 } 101 102 void CreateFile(const base::FilePath& file) { 103 EXPECT_EQ(file_util::WriteFile(file, NULL, 0), 0); 104 } 105 106 base::FilePath GetExtensionFile(const base::FilePath& dir, 107 const std::string& id, 108 const std::string& version) { 109 return dir.Append(id + "-" + version + ".crx"); 110 } 111 112 base::DictionaryValue* CreateEntryWithUpdateUrl(bool from_webstore) { 113 base::DictionaryValue* entry = new base::DictionaryValue; 114 entry->SetString(extensions::ExternalProviderImpl::kExternalUpdateUrl, 115 from_webstore ? extension_urls::GetWebstoreUpdateUrl().spec() 116 : kNonWebstoreUpdateUrl); 117 return entry; 118 } 119 120 void WaitForCompletion() { 121 // Wait for background task completion that sends replay to UI thread. 122 pool_owner_->pool()->FlushForTesting(); 123 // Wait for UI thread task completion. 124 base::RunLoop().RunUntilIdle(); 125 } 126 127 private: 128 content::TestBrowserThreadBundle thread_bundle_; 129 130 scoped_refptr<net::URLRequestContextGetter> request_context_getter_; 131 scoped_ptr<net::TestURLFetcherFactory> fetcher_factory_; 132 133 scoped_ptr<base::SequencedWorkerPoolOwner> pool_owner_; 134 scoped_refptr<base::SequencedTaskRunner> background_task_runner_; 135 136 base::ScopedTempDir cache_dir_; 137 base::ScopedTempDir temp_dir_; 138 scoped_ptr<base::DictionaryValue> prefs_; 139 140 DISALLOW_COPY_AND_ASSIGN(ExternalCacheTest); 141}; 142 143TEST_F(ExternalCacheTest, Basic) { 144 base::FilePath cache_dir(CreateCacheDir(false)); 145 ExternalCache external_cache(cache_dir, request_context_getter(), 146 background_task_runner(), this, true, false); 147 148 scoped_ptr<base::DictionaryValue> prefs(new base::DictionaryValue); 149 base::DictionaryValue* dict = CreateEntryWithUpdateUrl(true); 150 dict->SetBoolean( 151 extensions::ExternalProviderImpl::kRequirePermissionsConsent, true); 152 prefs->Set(kTestExtensionId1, dict); 153 CreateExtensionFile(cache_dir, kTestExtensionId1, "1"); 154 dict = CreateEntryWithUpdateUrl(true); 155 dict->SetBoolean( 156 extensions::ExternalProviderImpl::kRequirePermissionsConsent, true); 157 prefs->Set(kTestExtensionId2, dict); 158 prefs->Set(kTestExtensionId3, CreateEntryWithUpdateUrl(false)); 159 CreateExtensionFile(cache_dir, kTestExtensionId3, "3"); 160 prefs->Set(kTestExtensionId4, CreateEntryWithUpdateUrl(false)); 161 162 external_cache.UpdateExtensionsList(prefs.Pass()); 163 WaitForCompletion(); 164 165 ASSERT_TRUE(provided_prefs()); 166 EXPECT_EQ(provided_prefs()->size(), 2ul); 167 168 // File in cache from Webstore. 169 const base::DictionaryValue* entry1 = NULL; 170 ASSERT_TRUE(provided_prefs()->GetDictionary(kTestExtensionId1, &entry1)); 171 EXPECT_FALSE(entry1->HasKey( 172 extensions::ExternalProviderImpl::kExternalUpdateUrl)); 173 EXPECT_TRUE(entry1->HasKey( 174 extensions::ExternalProviderImpl::kExternalCrx)); 175 EXPECT_TRUE(entry1->HasKey( 176 extensions::ExternalProviderImpl::kExternalVersion)); 177 bool from_webstore = false; 178 EXPECT_TRUE(entry1->GetBoolean( 179 extensions::ExternalProviderImpl::kIsFromWebstore, &from_webstore)); 180 EXPECT_TRUE(from_webstore); 181 bool require_permissions_consent = false; 182 EXPECT_TRUE(entry1->GetBoolean( 183 extensions::ExternalProviderImpl::kRequirePermissionsConsent, 184 &require_permissions_consent)); 185 EXPECT_TRUE(require_permissions_consent); 186 187 // File in cache not from Webstore. 188 const base::DictionaryValue* entry3 = NULL; 189 ASSERT_TRUE(provided_prefs()->GetDictionary(kTestExtensionId3, &entry3)); 190 EXPECT_FALSE(entry3->HasKey( 191 extensions::ExternalProviderImpl::kExternalUpdateUrl)); 192 EXPECT_TRUE(entry3->HasKey( 193 extensions::ExternalProviderImpl::kExternalCrx)); 194 EXPECT_TRUE(entry3->HasKey( 195 extensions::ExternalProviderImpl::kExternalVersion)); 196 EXPECT_FALSE(entry3->HasKey( 197 extensions::ExternalProviderImpl::kIsFromWebstore)); 198 199 // Update from Webstore. 200 base::FilePath temp_dir(CreateTempDir()); 201 base::FilePath temp_file2 = temp_dir.Append("b.crx"); 202 CreateFile(temp_file2); 203 external_cache.OnExtensionDownloadFinished(kTestExtensionId2, 204 temp_file2, 205 GURL(), 206 "2", 207 extensions::ExtensionDownloaderDelegate::PingResult(), 208 std::set<int>()); 209 210 WaitForCompletion(); 211 EXPECT_EQ(provided_prefs()->size(), 3ul); 212 213 const base::DictionaryValue* entry2 = NULL; 214 ASSERT_TRUE(provided_prefs()->GetDictionary(kTestExtensionId2, &entry2)); 215 EXPECT_FALSE(entry2->HasKey( 216 extensions::ExternalProviderImpl::kExternalUpdateUrl)); 217 EXPECT_TRUE(entry2->HasKey( 218 extensions::ExternalProviderImpl::kExternalCrx)); 219 EXPECT_TRUE(entry2->HasKey( 220 extensions::ExternalProviderImpl::kExternalVersion)); 221 from_webstore = false; 222 EXPECT_TRUE(entry2->GetBoolean( 223 extensions::ExternalProviderImpl::kIsFromWebstore, &from_webstore)); 224 EXPECT_TRUE(from_webstore); 225 require_permissions_consent = false; 226 EXPECT_TRUE(entry2->GetBoolean( 227 extensions::ExternalProviderImpl::kRequirePermissionsConsent, 228 &require_permissions_consent)); 229 EXPECT_TRUE(require_permissions_consent); 230 EXPECT_TRUE(base::PathExists( 231 GetExtensionFile(cache_dir, kTestExtensionId2, "2"))); 232 233 // Update not from Webstore. 234 base::FilePath temp_file4 = temp_dir.Append("d.crx"); 235 CreateFile(temp_file4); 236 external_cache.OnExtensionDownloadFinished(kTestExtensionId4, 237 temp_file4, 238 GURL(), 239 "4", 240 extensions::ExtensionDownloaderDelegate::PingResult(), 241 std::set<int>()); 242 243 WaitForCompletion(); 244 EXPECT_EQ(provided_prefs()->size(), 4ul); 245 246 const base::DictionaryValue* entry4 = NULL; 247 ASSERT_TRUE(provided_prefs()->GetDictionary(kTestExtensionId4, &entry4)); 248 EXPECT_FALSE(entry4->HasKey( 249 extensions::ExternalProviderImpl::kExternalUpdateUrl)); 250 EXPECT_TRUE(entry4->HasKey( 251 extensions::ExternalProviderImpl::kExternalCrx)); 252 EXPECT_TRUE(entry4->HasKey( 253 extensions::ExternalProviderImpl::kExternalVersion)); 254 EXPECT_FALSE(entry4->HasKey( 255 extensions::ExternalProviderImpl::kIsFromWebstore)); 256 EXPECT_TRUE(base::PathExists( 257 GetExtensionFile(cache_dir, kTestExtensionId4, "4"))); 258 259 // Damaged file should be removed from disk. 260 external_cache.OnDamagedFileDetected( 261 GetExtensionFile(cache_dir, kTestExtensionId2, "2")); 262 WaitForCompletion(); 263 EXPECT_EQ(provided_prefs()->size(), 3ul); 264 EXPECT_FALSE(base::PathExists( 265 GetExtensionFile(cache_dir, kTestExtensionId2, "2"))); 266 267 // Shutdown with callback OnExtensionListsUpdated that clears prefs. 268 scoped_ptr<base::DictionaryValue> empty(new base::DictionaryValue); 269 external_cache.Shutdown( 270 base::Bind(&ExternalCacheTest::OnExtensionListsUpdated, 271 base::Unretained(this), 272 base::Unretained(empty.get()))); 273 WaitForCompletion(); 274 EXPECT_EQ(provided_prefs()->size(), 0ul); 275 276 // After Shutdown directory shouldn't be touched. 277 external_cache.OnDamagedFileDetected( 278 GetExtensionFile(cache_dir, kTestExtensionId4, "4")); 279 WaitForCompletion(); 280 EXPECT_TRUE(base::PathExists( 281 GetExtensionFile(cache_dir, kTestExtensionId4, "4"))); 282} 283 284} // namespace chromeos 285