普通文本  |  242行  |  9.49 KB

// Copyright (c) 2011 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.

// TODO(rickcam): Bug 73183: Add unit tests for image loading

#include <cstdlib>
#include <set>

#include "chrome/browser/background_application_list_model.h"

#include "base/command_line.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
#include "base/stl_util-inl.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/test/testing_profile.h"
#include "content/browser/browser_thread.h"
#include "content/common/notification_registrar.h"
#include "content/common/notification_service.h"
#include "content/common/notification_type.h"
#include "testing/gtest/include/gtest/gtest.h"

// This value is used to seed the PRNG at the beginning of a sequence of
// operations to produce a repeatable sequence.
#define RANDOM_SEED (0x33F7A7A7)

// For ExtensionService interface when it requires a path that is not used.
FilePath bogus_file_path() {
  return FilePath(FILE_PATH_LITERAL("//foobar_nonexistent"));
}

class BackgroundApplicationListModelTest : public testing::Test {
 public:
  BackgroundApplicationListModelTest();
  ~BackgroundApplicationListModelTest();

  virtual void InitializeEmptyExtensionService();

 protected:
  scoped_ptr<Profile> profile_;
  scoped_refptr<ExtensionService> service_;
  MessageLoop loop_;
  BrowserThread ui_thread_;
};

// The message loop may be used in tests which require it to be an IO loop.
BackgroundApplicationListModelTest::BackgroundApplicationListModelTest()
    : loop_(MessageLoop::TYPE_IO),
      ui_thread_(BrowserThread::UI, &loop_) {
}

BackgroundApplicationListModelTest::~BackgroundApplicationListModelTest() {
  // Drop reference to ExtensionService and TestingProfile, so that they can be
  // destroyed while BrowserThreads and MessageLoop are still around.  They
  // are used in the destruction process.
  service_ = NULL;
  profile_.reset(NULL);
  MessageLoop::current()->RunAllPending();
}

// This is modeled on a similar routine in ExtensionServiceTestBase.
void BackgroundApplicationListModelTest::InitializeEmptyExtensionService() {
  TestingProfile* profile = new TestingProfile();
  profile_.reset(profile);
  service_ = profile->CreateExtensionService(
      CommandLine::ForCurrentProcess(),
      bogus_file_path(), false);
  service_->set_extensions_enabled(true);
  service_->set_show_extensions_prompts(false);
  service_->OnLoadedInstalledExtensions(); /* Sends EXTENSIONS_READY */
}

// Returns a barebones test Extension object with the specified |name|.  The
// returned extension will include background permission iff
// |background_permission| is true.
static scoped_refptr<Extension> CreateExtension(const std::string& name,
                                                bool background_permission) {
  DictionaryValue manifest;
  manifest.SetString(extension_manifest_keys::kVersion, "1.0.0.0");
  manifest.SetString(extension_manifest_keys::kName, name);
  if (background_permission) {
    ListValue* permissions = new ListValue();
    manifest.Set(extension_manifest_keys::kPermissions, permissions);
    permissions->Append(Value::CreateStringValue("background"));
  }
  std::string error;
  scoped_refptr<Extension> extension = Extension::Create(
      bogus_file_path().AppendASCII(name), Extension::INVALID, manifest,
      Extension::STRICT_ERROR_CHECKS, &error);
  // Cannot ASSERT_* here because that attempts an illegitimate return.
  // Cannot EXPECT_NE here because that assumes non-pointers unlike EXPECT_EQ
  EXPECT_TRUE(extension.get() != NULL) << error;
  return extension;
}

// With minimal test logic, verifies behavior over an explicit set of
// extensions, of which some are Background Apps and others are not.
TEST_F(BackgroundApplicationListModelTest, LoadExplicitExtensions) {
  InitializeEmptyExtensionService();
  ExtensionService* service = profile_->GetExtensionService();
  ASSERT_TRUE(service);
  ASSERT_TRUE(service->is_ready());
  ASSERT_TRUE(service->extensions());
  ASSERT_TRUE(service->extensions()->empty());
  scoped_ptr<BackgroundApplicationListModel> model(
      new BackgroundApplicationListModel(profile_.get()));
  ASSERT_EQ(0U, model->size());

  scoped_refptr<Extension> ext1 = CreateExtension("alpha", false);
  scoped_refptr<Extension> ext2 = CreateExtension("bravo", false);
  scoped_refptr<Extension> ext3 = CreateExtension("charlie", false);
  scoped_refptr<Extension> bgapp1 = CreateExtension("delta", true);
  scoped_refptr<Extension> bgapp2 = CreateExtension("echo", true);
  ASSERT_TRUE(service->extensions() != NULL);
  ASSERT_EQ(0U, service->extensions()->size());
  ASSERT_EQ(0U, model->size());
  // Add alternating Extensions and Background Apps
  ASSERT_FALSE(BackgroundApplicationListModel::IsBackgroundApp(*ext1));
  service->AddExtension(ext1);
  ASSERT_EQ(1U, service->extensions()->size());
  ASSERT_EQ(0U, model->size());
  ASSERT_TRUE(BackgroundApplicationListModel::IsBackgroundApp(*bgapp1));
  service->AddExtension(bgapp1);
  ASSERT_EQ(2U, service->extensions()->size());
  ASSERT_EQ(1U, model->size());
  ASSERT_FALSE(BackgroundApplicationListModel::IsBackgroundApp(*ext2));
  service->AddExtension(ext2);
  ASSERT_EQ(3U, service->extensions()->size());
  ASSERT_EQ(1U, model->size());
  ASSERT_TRUE(BackgroundApplicationListModel::IsBackgroundApp(*bgapp2));
  service->AddExtension(bgapp2);
  ASSERT_EQ(4U, service->extensions()->size());
  ASSERT_EQ(2U, model->size());
  ASSERT_FALSE(BackgroundApplicationListModel::IsBackgroundApp(*ext3));
  service->AddExtension(ext3);
  ASSERT_EQ(5U, service->extensions()->size());
  ASSERT_EQ(2U, model->size());
  // Remove in FIFO order.
  ASSERT_FALSE(BackgroundApplicationListModel::IsBackgroundApp(*ext1));
  service->UninstallExtension(ext1->id(), false, NULL);
  ASSERT_EQ(4U, service->extensions()->size());
  ASSERT_EQ(2U, model->size());
  ASSERT_TRUE(BackgroundApplicationListModel::IsBackgroundApp(*bgapp1));
  service->UninstallExtension(bgapp1->id(), false, NULL);
  ASSERT_EQ(3U, service->extensions()->size());
  ASSERT_EQ(1U, model->size());
  ASSERT_FALSE(BackgroundApplicationListModel::IsBackgroundApp(*ext2));
  service->UninstallExtension(ext2->id(), false, NULL);
  ASSERT_EQ(2U, service->extensions()->size());
  ASSERT_EQ(1U, model->size());
  ASSERT_TRUE(BackgroundApplicationListModel::IsBackgroundApp(*bgapp2));
  service->UninstallExtension(bgapp2->id(), false, NULL);
  ASSERT_EQ(1U, service->extensions()->size());
  ASSERT_EQ(0U, model->size());
  ASSERT_FALSE(BackgroundApplicationListModel::IsBackgroundApp(*ext3));
  service->UninstallExtension(ext3->id(), false, NULL);
  ASSERT_EQ(0U, service->extensions()->size());
  ASSERT_EQ(0U, model->size());
}

typedef std::set<scoped_refptr<Extension> > ExtensionSet;

namespace {
std::string GenerateUniqueExtensionName() {
  static int uniqueness = 0;
  std::ostringstream output;
  output << "Unique Named Extension " << uniqueness;
  ++uniqueness;
  return output.str();
}
}

// Verifies behavior with a pseudo-randomly generated set of actions: Adding and
// removing extensions, of which some are Background Apps and others are not.
TEST_F(BackgroundApplicationListModelTest, LoadRandomExtension) {
  InitializeEmptyExtensionService();
  ExtensionService* service = profile_->GetExtensionService();
  ASSERT_TRUE(service);
  ASSERT_TRUE(service->is_ready());
  ASSERT_TRUE(service->extensions());
  ASSERT_TRUE(service->extensions()->empty());
  scoped_ptr<BackgroundApplicationListModel> model(
      new BackgroundApplicationListModel(profile_.get()));
  ASSERT_EQ(0U, model->size());

  static const int kIterations = 500;
  ExtensionSet extensions;
  size_t count = 0;
  size_t expected = 0;
  srand(RANDOM_SEED);
  for (int index = 0; index < kIterations; ++index) {
    if (rand() % 2) {  // Add an extension
      std::string name = GenerateUniqueExtensionName();
      bool create_background = false;
      if (rand() % 2) {
        create_background = true;
        ++expected;
      }
      scoped_refptr<Extension> extension =
          CreateExtension(name, create_background);
      ASSERT_EQ(BackgroundApplicationListModel::IsBackgroundApp(*extension),
                create_background);
      extensions.insert(extension);
      ++count;
      ASSERT_EQ(count, extensions.size());
      service->AddExtension(extension);
      ASSERT_EQ(count, service->extensions()->size());
      ASSERT_EQ(expected, model->size());
    } else {  // Maybe remove an extension.
      ExtensionSet::iterator cursor = extensions.begin();
      if (cursor == extensions.end()) {
        // Nothing to remove.  Just verify accounting.
        ASSERT_EQ(0U, count);
        ASSERT_EQ(0U, expected);
        ASSERT_EQ(0U, service->extensions()->size());
        ASSERT_EQ(0U, model->size());
      } else {
        // Randomly select which extension to remove
        if (extensions.size() > 1) {
          int offset = rand() % (extensions.size() - 1);
          for (int index = 0; index < offset; ++index)
            ++cursor;
        }
        scoped_refptr<Extension> extension = cursor->get();
        std::string id = extension->id();
        if (BackgroundApplicationListModel::IsBackgroundApp(*extension))
          --expected;
        extensions.erase(cursor);
        --count;
        ASSERT_EQ(count, extensions.size());
        service->UninstallExtension(extension->id(), false, NULL);
        ASSERT_EQ(count, service->extensions()->size());
        ASSERT_EQ(expected, model->size());
      }
    }
  }
}