root/chrome/browser/background/background_application_list_model_unittest.cc

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

DEFINITIONS

This source file includes following definitions.
  1. bogus_file_pathname
  2. InitializeAndLoadEmptyExtensionService
  3. IsBackgroundApp
  4. CreateExtensionBase
  5. CreateExtension
  6. GenerateUniqueExtensionName
  7. AddBackgroundPermission
  8. RemoveBackgroundPermission
  9. TEST_F
  10. TEST_F
  11. TEST_F
  12. AddExtension
  13. RemoveExtension
  14. TogglePermission
  15. TEST_F

// Copyright (c) 2012 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/background_application_list_model.h"

#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/stl_util.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_service_unittest.h"
#include "chrome/browser/extensions/permissions_updater.h"
#include "chrome/test/base/testing_profile.h"
#include "content/public/browser/notification_registrar.h"
#include "content/public/browser/notification_types.h"
#include "extensions/browser/extension_system.h"
#include "extensions/common/extension.h"
#include "extensions/common/manifest_constants.h"
#include "extensions/common/permissions/api_permission.h"
#include "extensions/common/permissions/permission_set.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)

using extensions::APIPermission;
using extensions::Extension;

// For ExtensionService interface when it requires a path that is not used.
base::FilePath bogus_file_pathname(const std::string& name) {
  return base::FilePath(FILE_PATH_LITERAL("//foobar_nonexistent"))
      .AppendASCII(name);
}

class BackgroundApplicationListModelTest : public ExtensionServiceTestBase {
 public:
  BackgroundApplicationListModelTest() {}
  virtual ~BackgroundApplicationListModelTest() {}

 protected:
  void InitializeAndLoadEmptyExtensionService() {
    InitializeEmptyExtensionService();
    service_->Init(); /* Sends EXTENSIONS_READY */
  }

  bool IsBackgroundApp(const Extension& app) {
    return BackgroundApplicationListModel::IsBackgroundApp(app,
                                                           profile_.get());
  }
};

enum PushMessagingOption {
  NO_PUSH_MESSAGING,
  PUSH_MESSAGING_PERMISSION,
  PUSH_MESSAGING_BUT_NOT_BACKGROUND
};

// Returns a barebones test Extension object with the specified |name|.  The
// returned extension will include background permission iff
// |background_permission| is true and pushMessaging permission if requested
// by |push_messaging| value. Also the extension may have a specific id set
// to test the case when it has a pushMessaging permission but is not
// considered a background app based on a whitelist.
static scoped_refptr<Extension> CreateExtensionBase(
    const std::string& name,
    bool background_permission,
    PushMessagingOption push_messaging) {
  base::DictionaryValue manifest;
  manifest.SetString(extensions::manifest_keys::kVersion, "1.0.0.0");
  manifest.SetString(extensions::manifest_keys::kName, name);
  base::ListValue* permissions = new base::ListValue();
  manifest.Set(extensions::manifest_keys::kPermissions, permissions);
  if (background_permission) {
    permissions->Append(base::Value::CreateStringValue("background"));
  }
  if (push_messaging == PUSH_MESSAGING_PERMISSION ||
      push_messaging == PUSH_MESSAGING_BUT_NOT_BACKGROUND) {
    permissions->Append(base::Value::CreateStringValue("pushMessaging"));
  }

  std::string error;
  scoped_refptr<Extension> extension;

  // There is a whitelist for extensions that have pushMessaging permission but
  // are not considered a background app. Create a test extension with a known
  // test id if needed.
  if (push_messaging == PUSH_MESSAGING_BUT_NOT_BACKGROUND) {
    extension = Extension::Create(
        bogus_file_pathname(name),
        extensions::Manifest::INVALID_LOCATION,
        manifest,
        Extension::NO_FLAGS,
        "aaaabbbbccccddddeeeeffffgggghhhh",
        &error);
  } else {
    extension = Extension::Create(
        bogus_file_pathname(name),
        extensions::Manifest::INVALID_LOCATION,
        manifest,
        Extension::NO_FLAGS,
        &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;
}

static scoped_refptr<Extension> CreateExtension(const std::string& name,
                                                bool background_permission) {
  return CreateExtensionBase(name, background_permission, NO_PUSH_MESSAGING);
}

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

void AddBackgroundPermission(ExtensionService* service,
                             Extension* extension) {
  if (BackgroundApplicationListModel::IsBackgroundApp(*extension,
                                                      service->profile())) {
    return;
  }

  scoped_refptr<Extension> temporary =
      CreateExtension(GenerateUniqueExtensionName(), true);
  scoped_refptr<const extensions::PermissionSet> permissions =
      temporary->GetActivePermissions();
  extensions::PermissionsUpdater(service->profile()).AddPermissions(
      extension, permissions.get());
}

void RemoveBackgroundPermission(ExtensionService* service,
                                Extension* extension) {
  if (!BackgroundApplicationListModel::IsBackgroundApp(*extension,
                                                       service->profile())) {
    return;
  }
  extensions::PermissionsUpdater(service->profile())
      .RemovePermissions(extension, extension->GetActivePermissions().get());
}
}  // namespace

// Crashes on Mac tryslaves.
// http://crbug.com/165458
#if defined(OS_MACOSX) || defined(OS_LINUX)
#define MAYBE_ExplicitTest DISABLED_ExplicitTest
#else
#define MAYBE_ExplicitTest ExplicitTest
#endif
// 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, MAYBE_ExplicitTest) {
  InitializeAndLoadEmptyExtensionService();
  ExtensionService* service = extensions::ExtensionSystem::Get(profile_.get())->
      extension_service();
  ASSERT_TRUE(service);
  ASSERT_TRUE(service->is_ready());
  ASSERT_TRUE(service->extensions());
  ASSERT_TRUE(service->extensions()->is_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(IsBackgroundApp(*ext1.get()));
  service->AddExtension(ext1.get());
  ASSERT_EQ(1U, service->extensions()->size());
  ASSERT_EQ(0U, model->size());
  ASSERT_TRUE(IsBackgroundApp(*bgapp1.get()));
  service->AddExtension(bgapp1.get());
  ASSERT_EQ(2U, service->extensions()->size());
  ASSERT_EQ(1U, model->size());
  ASSERT_FALSE(IsBackgroundApp(*ext2.get()));
  service->AddExtension(ext2.get());
  ASSERT_EQ(3U, service->extensions()->size());
  ASSERT_EQ(1U, model->size());
  ASSERT_TRUE(IsBackgroundApp(*bgapp2.get()));
  service->AddExtension(bgapp2.get());
  ASSERT_EQ(4U, service->extensions()->size());
  ASSERT_EQ(2U, model->size());
  ASSERT_FALSE(IsBackgroundApp(*ext3.get()));
  service->AddExtension(ext3.get());
  ASSERT_EQ(5U, service->extensions()->size());
  ASSERT_EQ(2U, model->size());

  // Remove in FIFO order.
  ASSERT_FALSE(IsBackgroundApp(*ext1.get()));
  service->UninstallExtension(ext1->id(), false, NULL);
  ASSERT_EQ(4U, service->extensions()->size());
  ASSERT_EQ(2U, model->size());
  ASSERT_TRUE(IsBackgroundApp(*bgapp1.get()));
  service->UninstallExtension(bgapp1->id(), false, NULL);
  ASSERT_EQ(3U, service->extensions()->size());
  ASSERT_EQ(1U, model->size());
  ASSERT_FALSE(IsBackgroundApp(*ext2.get()));
  service->UninstallExtension(ext2->id(), false, NULL);
  ASSERT_EQ(2U, service->extensions()->size());
  ASSERT_EQ(1U, model->size());
  ASSERT_TRUE(IsBackgroundApp(*bgapp2.get()));
  service->UninstallExtension(bgapp2->id(), false, NULL);
  ASSERT_EQ(1U, service->extensions()->size());
  ASSERT_EQ(0U, model->size());
  ASSERT_FALSE(IsBackgroundApp(*ext3.get()));
  service->UninstallExtension(ext3->id(), false, NULL);
  ASSERT_EQ(0U, service->extensions()->size());
  ASSERT_EQ(0U, model->size());
}

// Verifies that pushMessaging also triggers background detection, except
// when extension is in a whitelist.
TEST_F(BackgroundApplicationListModelTest, PushMessagingTest) {
  InitializeAndLoadEmptyExtensionService();
  ExtensionService* service = extensions::ExtensionSystem::Get(profile_.get())->
      extension_service();
  ASSERT_TRUE(service);
  ASSERT_TRUE(service->is_ready());
  ASSERT_TRUE(service->extensions());
  ASSERT_TRUE(service->extensions()->is_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 =
      CreateExtensionBase("charlie", false, PUSH_MESSAGING_BUT_NOT_BACKGROUND);
  scoped_refptr<Extension> bgapp1 =
      CreateExtensionBase("bravo", false, PUSH_MESSAGING_PERMISSION);
  scoped_refptr<Extension> bgapp2 =
      CreateExtensionBase("delta", true, PUSH_MESSAGING_PERMISSION);
  scoped_refptr<Extension> bgapp3 =
      CreateExtensionBase("echo", true, PUSH_MESSAGING_BUT_NOT_BACKGROUND);
  ASSERT_TRUE(service->extensions() != NULL);
  ASSERT_EQ(0U, service->extensions()->size());
  ASSERT_EQ(0U, model->size());

  // Add alternating Extensions and Background Apps
  ASSERT_FALSE(IsBackgroundApp(*ext1.get()));
  service->AddExtension(ext1.get());
  ASSERT_EQ(1U, service->extensions()->size());
  ASSERT_EQ(0U, model->size());
  ASSERT_TRUE(IsBackgroundApp(*bgapp1.get()));
  service->AddExtension(bgapp1.get());
  ASSERT_EQ(2U, service->extensions()->size());
  ASSERT_EQ(1U, model->size());
  ASSERT_FALSE(IsBackgroundApp(*ext2.get()));
  service->AddExtension(ext2.get());
  ASSERT_EQ(3U, service->extensions()->size());
  ASSERT_EQ(1U, model->size());
  ASSERT_TRUE(IsBackgroundApp(*bgapp2.get()));
  service->AddExtension(bgapp2.get());
  ASSERT_EQ(4U, service->extensions()->size());
  ASSERT_EQ(2U, model->size());
  // Need to remove ext2 because it uses same id as bgapp3.
  ASSERT_FALSE(IsBackgroundApp(*ext2.get()));
  service->UninstallExtension(ext2->id(), false, NULL);
  ASSERT_EQ(3U, service->extensions()->size());
  ASSERT_EQ(2U, model->size());
  ASSERT_TRUE(IsBackgroundApp(*bgapp3.get()));
  service->AddExtension(bgapp3.get());
  ASSERT_EQ(4U, service->extensions()->size());
  ASSERT_EQ(3U, model->size());

  // Remove in FIFO order.
  ASSERT_FALSE(IsBackgroundApp(*ext1.get()));
  service->UninstallExtension(ext1->id(), false, NULL);
  ASSERT_EQ(3U, service->extensions()->size());
  ASSERT_EQ(3U, model->size());
  ASSERT_TRUE(IsBackgroundApp(*bgapp1.get()));
  service->UninstallExtension(bgapp1->id(), false, NULL);
  ASSERT_EQ(2U, service->extensions()->size());
  ASSERT_EQ(2U, model->size());
  ASSERT_TRUE(IsBackgroundApp(*bgapp2.get()));
  service->UninstallExtension(bgapp2->id(), false, NULL);
  ASSERT_EQ(1U, service->extensions()->size());
  ASSERT_EQ(1U, model->size());
  ASSERT_TRUE(IsBackgroundApp(*bgapp3.get()));
  service->UninstallExtension(bgapp3->id(), false, NULL);
  ASSERT_EQ(0U, service->extensions()->size());
  ASSERT_EQ(0U, model->size());
}



// With minimal test logic, verifies behavior with dynamic permissions.
TEST_F(BackgroundApplicationListModelTest, AddRemovePermissionsTest) {
  InitializeAndLoadEmptyExtensionService();
  ExtensionService* service = extensions::ExtensionSystem::Get(profile_.get())->
      extension_service();
  ASSERT_TRUE(service);
  ASSERT_TRUE(service->is_ready());
  ASSERT_TRUE(service->extensions());
  ASSERT_TRUE(service->extensions()->is_empty());
  scoped_ptr<BackgroundApplicationListModel> model(
      new BackgroundApplicationListModel(profile_.get()));
  ASSERT_EQ(0U, model->size());

  scoped_refptr<Extension> ext = CreateExtension("extension", false);
  ASSERT_FALSE(ext->HasAPIPermission(APIPermission::kBackground));
  scoped_refptr<Extension> bgapp = CreateExtension("application", true);
  ASSERT_TRUE(bgapp->HasAPIPermission(APIPermission::kBackground));
  ASSERT_TRUE(service->extensions() != NULL);
  ASSERT_EQ(0U, service->extensions()->size());
  ASSERT_EQ(0U, model->size());

  // Add one (non-background) extension and one background application
  ASSERT_FALSE(IsBackgroundApp(*ext.get()));
  service->AddExtension(ext.get());
  ASSERT_EQ(1U, service->extensions()->size());
  ASSERT_EQ(0U, model->size());
  ASSERT_TRUE(IsBackgroundApp(*bgapp.get()));
  service->AddExtension(bgapp.get());
  ASSERT_EQ(2U, service->extensions()->size());
  ASSERT_EQ(1U, model->size());

  // Change permissions back and forth
  AddBackgroundPermission(service, ext.get());
  ASSERT_TRUE(ext->HasAPIPermission(APIPermission::kBackground));
  ASSERT_EQ(2U, service->extensions()->size());
  ASSERT_EQ(2U, model->size());
  RemoveBackgroundPermission(service, bgapp.get());
  ASSERT_FALSE(bgapp->HasAPIPermission(APIPermission::kBackground));
  ASSERT_EQ(2U, service->extensions()->size());
  ASSERT_EQ(1U, model->size());
  RemoveBackgroundPermission(service, ext.get());
  ASSERT_FALSE(ext->HasAPIPermission(APIPermission::kBackground));
  ASSERT_EQ(2U, service->extensions()->size());
  ASSERT_EQ(0U, model->size());
  AddBackgroundPermission(service, bgapp.get());
  ASSERT_TRUE(bgapp->HasAPIPermission(APIPermission::kBackground));
  ASSERT_EQ(2U, service->extensions()->size());
  ASSERT_EQ(1U, model->size());
}

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

namespace {
void AddExtension(ExtensionService* service,
                  ExtensionCollection* extensions,
                  BackgroundApplicationListModel* model,
                  size_t* expected,
                  size_t* count) {
  bool create_background = false;
  if (rand() % 2) {
    create_background = true;
    ++*expected;
  }
  scoped_refptr<Extension> extension =
      CreateExtension(GenerateUniqueExtensionName(), create_background);
  ASSERT_EQ(BackgroundApplicationListModel::IsBackgroundApp(*extension.get(),
                                                            service->profile()),
            create_background);
  extensions->insert(extension);
  ++*count;
  ASSERT_EQ(*count, extensions->size());
  service->AddExtension(extension.get());
  ASSERT_EQ(*count, service->extensions()->size());
  ASSERT_EQ(*expected, model->size());
}

void RemoveExtension(ExtensionService* service,
                     ExtensionCollection* extensions,
                     BackgroundApplicationListModel* model,
                     size_t* expected,
                     size_t* count) {  // Maybe remove an extension.
  ExtensionCollection::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.get(),
                                                        service->profile())) {
      --*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());
  }
}

void TogglePermission(ExtensionService* service,
                      ExtensionCollection* extensions,
                      BackgroundApplicationListModel* model,
                      size_t* expected,
                      size_t* count) {
  ExtensionCollection::iterator cursor = extensions->begin();
  if (cursor == extensions->end()) {
    // Nothing to toggle.  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 toggle.
    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.get(),
                                                        service->profile())) {
      --*expected;
      ASSERT_EQ(*count, extensions->size());
      RemoveBackgroundPermission(service, extension.get());
      ASSERT_EQ(*count, service->extensions()->size());
      ASSERT_EQ(*expected, model->size());
    } else {
      ++*expected;
      ASSERT_EQ(*count, extensions->size());
      AddBackgroundPermission(service, extension.get());
      ASSERT_EQ(*count, service->extensions()->size());
      ASSERT_EQ(*expected, model->size());
    }
  }
}
}  // namespace

// 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, RandomTest) {
  InitializeAndLoadEmptyExtensionService();
  ExtensionService* service = extensions::ExtensionSystem::Get(profile_.get())->
      extension_service();
  ASSERT_TRUE(service);
  ASSERT_TRUE(service->is_ready());
  ASSERT_TRUE(service->extensions());
  ASSERT_TRUE(service->extensions()->is_empty());
  scoped_ptr<BackgroundApplicationListModel> model(
      new BackgroundApplicationListModel(profile_.get()));
  ASSERT_EQ(0U, model->size());

  static const int kIterations = 20;
  ExtensionCollection extensions;
  size_t count = 0;
  size_t expected = 0;
  srand(RANDOM_SEED);
  for (int index = 0; index < kIterations; ++index) {
    switch (rand() % 3) {
      case 0:
        AddExtension(service, &extensions, model.get(), &expected, &count);
        break;
      case 1:
        RemoveExtension(service, &extensions, model.get(), &expected, &count);
        break;
      case 2:
        TogglePermission(service, &extensions, model.get(), &expected, &count);
        break;
      default:
        NOTREACHED();
        break;
    }
  }
}

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