root/chrome/browser/chromeos/extensions/external_cache_unittest.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. background_task_runner
  2. request_context_getter
  3. provided_prefs
  4. SetUp
  5. TearDown
  6. OnExtensionListsUpdated
  7. GetInstalledExtensionVersion
  8. CreateCacheDir
  9. CreateTempDir
  10. CreateFlagFile
  11. CreateExtensionFile
  12. CreateFile
  13. GetExtensionFile
  14. CreateEntryWithUpdateUrl
  15. WaitForCompletion
  16. AddInstalledExtension
  17. TEST_F
  18. TEST_F

// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/chromeos/extensions/external_cache.h"

#include <map>
#include <set>
#include <string>

#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
#include "base/run_loop.h"
#include "base/test/sequenced_worker_pool_owner.h"
#include "base/values.h"
#include "chrome/browser/extensions/external_provider_impl.h"
#include "chrome/common/extensions/extension_constants.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "net/url_request/test_url_fetcher_factory.h"
#include "net/url_request/url_fetcher_impl.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace {

const char kTestExtensionId1[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
const char kTestExtensionId2[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
const char kTestExtensionId3[] = "cccccccccccccccccccccccccccccccc";
const char kTestExtensionId4[] = "dddddddddddddddddddddddddddddddd";
const char kNonWebstoreUpdateUrl[] = "https://localhost/service/update2/crx";

}  // namespace

namespace chromeos {

class ExternalCacheTest : public testing::Test,
                          public ExternalCache::Delegate {
 public:
  ExternalCacheTest()
    : thread_bundle_(content::TestBrowserThreadBundle::REAL_IO_THREAD) {
  }
  virtual ~ExternalCacheTest() {}

  scoped_refptr<base::SequencedTaskRunner> background_task_runner() {
    return background_task_runner_;
  }

  net::URLRequestContextGetter* request_context_getter() {
    return request_context_getter_.get();
  }

  const base::DictionaryValue* provided_prefs() {
    return prefs_.get();
  }

  // testing::Test overrides:
  virtual void SetUp() OVERRIDE {
    request_context_getter_ = new net::TestURLRequestContextGetter(
        content::BrowserThread::GetMessageLoopProxyForThread(
            content::BrowserThread::IO));
    fetcher_factory_.reset(new net::TestURLFetcherFactory());

    pool_owner_.reset(
        new base::SequencedWorkerPoolOwner(3, "Background Pool"));
    background_task_runner_ = pool_owner_->pool()->GetSequencedTaskRunner(
        pool_owner_->pool()->GetNamedSequenceToken("background"));
  }

  virtual void TearDown() OVERRIDE {
    pool_owner_->pool()->Shutdown();
    base::RunLoop().RunUntilIdle();
  }

  // ExternalCache::Delegate:
  virtual void OnExtensionListsUpdated(
      const base::DictionaryValue* prefs) OVERRIDE {
    prefs_.reset(prefs->DeepCopy());
  }

  virtual std::string GetInstalledExtensionVersion(
      const std::string& id) OVERRIDE {
    std::map<std::string, std::string>::iterator it =
        installed_extensions_.find(id);
    return it != installed_extensions_.end() ? it->second : std::string();
  }

  base::FilePath CreateCacheDir(bool initialized) {
    EXPECT_TRUE(cache_dir_.CreateUniqueTempDir());
    if (initialized)
      CreateFlagFile(cache_dir_.path());
    return cache_dir_.path();
  }

  base::FilePath CreateTempDir() {
    EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
    return temp_dir_.path();
  }

  void CreateFlagFile(const base::FilePath& dir) {
    CreateFile(dir.Append(
        extensions::LocalExtensionCache::kCacheReadyFlagFileName));
  }

  void CreateExtensionFile(const base::FilePath& dir,
                           const std::string& id,
                           const std::string& version) {
    CreateFile(GetExtensionFile(dir, id, version));
  }

  void CreateFile(const base::FilePath& file) {
    EXPECT_EQ(base::WriteFile(file, NULL, 0), 0);
  }

  base::FilePath GetExtensionFile(const base::FilePath& dir,
                                  const std::string& id,
                                  const std::string& version) {
    return dir.Append(id + "-" + version + ".crx");
  }

  base::DictionaryValue* CreateEntryWithUpdateUrl(bool from_webstore) {
    base::DictionaryValue* entry = new base::DictionaryValue;
    entry->SetString(extensions::ExternalProviderImpl::kExternalUpdateUrl,
        from_webstore ? extension_urls::GetWebstoreUpdateUrl().spec()
                      : kNonWebstoreUpdateUrl);
    return entry;
  }

  void WaitForCompletion() {
    // Wait for background task completion that sends replay to UI thread.
    pool_owner_->pool()->FlushForTesting();
    // Wait for UI thread task completion.
    base::RunLoop().RunUntilIdle();
  }

  void AddInstalledExtension(const std::string& id,
                             const std::string& version) {
    installed_extensions_[id] = version;
  }

 private:
  content::TestBrowserThreadBundle thread_bundle_;

  scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
  scoped_ptr<net::TestURLFetcherFactory> fetcher_factory_;

  scoped_ptr<base::SequencedWorkerPoolOwner> pool_owner_;
  scoped_refptr<base::SequencedTaskRunner> background_task_runner_;

  base::ScopedTempDir cache_dir_;
  base::ScopedTempDir temp_dir_;
  scoped_ptr<base::DictionaryValue> prefs_;
  std::map<std::string, std::string> installed_extensions_;

  DISALLOW_COPY_AND_ASSIGN(ExternalCacheTest);
};

TEST_F(ExternalCacheTest, Basic) {
  base::FilePath cache_dir(CreateCacheDir(false));
  ExternalCache external_cache(cache_dir, request_context_getter(),
      background_task_runner(), this, true, false);

  scoped_ptr<base::DictionaryValue> prefs(new base::DictionaryValue);
  base::DictionaryValue* dict = CreateEntryWithUpdateUrl(true);
  prefs->Set(kTestExtensionId1, dict);
  CreateExtensionFile(cache_dir, kTestExtensionId1, "1");
  dict = CreateEntryWithUpdateUrl(true);
  prefs->Set(kTestExtensionId2, dict);
  prefs->Set(kTestExtensionId3, CreateEntryWithUpdateUrl(false));
  CreateExtensionFile(cache_dir, kTestExtensionId3, "3");
  prefs->Set(kTestExtensionId4, CreateEntryWithUpdateUrl(false));

  external_cache.UpdateExtensionsList(prefs.Pass());
  WaitForCompletion();

  ASSERT_TRUE(provided_prefs());
  EXPECT_EQ(provided_prefs()->size(), 2ul);

  // File in cache from Webstore.
  const base::DictionaryValue* entry1 = NULL;
  ASSERT_TRUE(provided_prefs()->GetDictionary(kTestExtensionId1, &entry1));
  EXPECT_FALSE(entry1->HasKey(
      extensions::ExternalProviderImpl::kExternalUpdateUrl));
  EXPECT_TRUE(entry1->HasKey(
      extensions::ExternalProviderImpl::kExternalCrx));
  EXPECT_TRUE(entry1->HasKey(
      extensions::ExternalProviderImpl::kExternalVersion));
  bool from_webstore = false;
  EXPECT_TRUE(entry1->GetBoolean(
      extensions::ExternalProviderImpl::kIsFromWebstore, &from_webstore));
  EXPECT_TRUE(from_webstore);

  // File in cache not from Webstore.
  const base::DictionaryValue* entry3 = NULL;
  ASSERT_TRUE(provided_prefs()->GetDictionary(kTestExtensionId3, &entry3));
  EXPECT_FALSE(entry3->HasKey(
      extensions::ExternalProviderImpl::kExternalUpdateUrl));
  EXPECT_TRUE(entry3->HasKey(
      extensions::ExternalProviderImpl::kExternalCrx));
  EXPECT_TRUE(entry3->HasKey(
      extensions::ExternalProviderImpl::kExternalVersion));
  EXPECT_FALSE(entry3->HasKey(
      extensions::ExternalProviderImpl::kIsFromWebstore));

  // Update from Webstore.
  base::FilePath temp_dir(CreateTempDir());
  base::FilePath temp_file2 = temp_dir.Append("b.crx");
  CreateFile(temp_file2);
  external_cache.OnExtensionDownloadFinished(kTestExtensionId2,
      temp_file2,
      true,
      GURL(),
      "2",
      extensions::ExtensionDownloaderDelegate::PingResult(),
      std::set<int>());

  WaitForCompletion();
  EXPECT_EQ(provided_prefs()->size(), 3ul);

  const base::DictionaryValue* entry2 = NULL;
  ASSERT_TRUE(provided_prefs()->GetDictionary(kTestExtensionId2, &entry2));
  EXPECT_FALSE(entry2->HasKey(
      extensions::ExternalProviderImpl::kExternalUpdateUrl));
  EXPECT_TRUE(entry2->HasKey(
      extensions::ExternalProviderImpl::kExternalCrx));
  EXPECT_TRUE(entry2->HasKey(
      extensions::ExternalProviderImpl::kExternalVersion));
  from_webstore = false;
  EXPECT_TRUE(entry2->GetBoolean(
      extensions::ExternalProviderImpl::kIsFromWebstore, &from_webstore));
  EXPECT_TRUE(from_webstore);
  EXPECT_TRUE(base::PathExists(
      GetExtensionFile(cache_dir, kTestExtensionId2, "2")));

  // Update not from Webstore.
  base::FilePath temp_file4 = temp_dir.Append("d.crx");
  CreateFile(temp_file4);
  external_cache.OnExtensionDownloadFinished(kTestExtensionId4,
      temp_file4,
      true,
      GURL(),
      "4",
      extensions::ExtensionDownloaderDelegate::PingResult(),
      std::set<int>());

  WaitForCompletion();
  EXPECT_EQ(provided_prefs()->size(), 4ul);

  const base::DictionaryValue* entry4 = NULL;
  ASSERT_TRUE(provided_prefs()->GetDictionary(kTestExtensionId4, &entry4));
  EXPECT_FALSE(entry4->HasKey(
      extensions::ExternalProviderImpl::kExternalUpdateUrl));
  EXPECT_TRUE(entry4->HasKey(
      extensions::ExternalProviderImpl::kExternalCrx));
  EXPECT_TRUE(entry4->HasKey(
      extensions::ExternalProviderImpl::kExternalVersion));
  EXPECT_FALSE(entry4->HasKey(
      extensions::ExternalProviderImpl::kIsFromWebstore));
  EXPECT_TRUE(base::PathExists(
      GetExtensionFile(cache_dir, kTestExtensionId4, "4")));

  // Damaged file should be removed from disk.
  external_cache.OnDamagedFileDetected(
      GetExtensionFile(cache_dir, kTestExtensionId2, "2"));
  WaitForCompletion();
  EXPECT_EQ(provided_prefs()->size(), 3ul);
  EXPECT_FALSE(base::PathExists(
      GetExtensionFile(cache_dir, kTestExtensionId2, "2")));

  // Shutdown with callback OnExtensionListsUpdated that clears prefs.
  scoped_ptr<base::DictionaryValue> empty(new base::DictionaryValue);
  external_cache.Shutdown(
        base::Bind(&ExternalCacheTest::OnExtensionListsUpdated,
                   base::Unretained(this),
                   base::Unretained(empty.get())));
  WaitForCompletion();
  EXPECT_EQ(provided_prefs()->size(), 0ul);

  // After Shutdown directory shouldn't be touched.
  external_cache.OnDamagedFileDetected(
      GetExtensionFile(cache_dir, kTestExtensionId4, "4"));
  WaitForCompletion();
  EXPECT_TRUE(base::PathExists(
      GetExtensionFile(cache_dir, kTestExtensionId4, "4")));
}

TEST_F(ExternalCacheTest, PreserveInstalled) {
  base::FilePath cache_dir(CreateCacheDir(false));
  ExternalCache external_cache(cache_dir, request_context_getter(),
      background_task_runner(), this, true, false);

  scoped_ptr<base::DictionaryValue> prefs(new base::DictionaryValue);
  prefs->Set(kTestExtensionId1, CreateEntryWithUpdateUrl(true));
  prefs->Set(kTestExtensionId2, CreateEntryWithUpdateUrl(true));

  AddInstalledExtension(kTestExtensionId1, "1");

  external_cache.UpdateExtensionsList(prefs.Pass());
  WaitForCompletion();

  ASSERT_TRUE(provided_prefs());
  EXPECT_EQ(provided_prefs()->size(), 1ul);

  // File not in cache but extension installed.
  const base::DictionaryValue* entry1 = NULL;
  ASSERT_TRUE(provided_prefs()->GetDictionary(kTestExtensionId1, &entry1));
  EXPECT_TRUE(entry1->HasKey(
      extensions::ExternalProviderImpl::kExternalUpdateUrl));
  EXPECT_FALSE(entry1->HasKey(
      extensions::ExternalProviderImpl::kExternalCrx));
  EXPECT_FALSE(entry1->HasKey(
      extensions::ExternalProviderImpl::kExternalVersion));
}

}  // namespace chromeos

/* [<][>][^][v][top][bottom][index][help] */