blob: 62df67c73bfcaec00c95a561dedb987cb60e8a59 [file] [log] [blame]
// Copyright (c) 2006-2008 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 <algorithm>
#include <vector>
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/json_reader.h"
#include "base/message_loop.h"
#include "base/path_service.h"
#include "base/string_util.h"
#include "chrome/browser/extensions/extension.h"
#include "chrome/browser/extensions/extensions_service.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/json_value_serializer.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
namespace {
struct ExtensionsOrder {
bool operator()(const Extension* a, const Extension* b) {
return a->name() < b->name();
}
};
} // namespace
// A mock implementation of ExtensionsServiceFrontendInterface for testing the
// backend.
class ExtensionsServiceTestFrontend
: public ExtensionsServiceFrontendInterface {
public:
ExtensionsServiceTestFrontend() {
file_util::CreateNewTempDirectory(FILE_PATH_LITERAL("ext_test"),
&install_dir_);
}
~ExtensionsServiceTestFrontend() {
for (ExtensionList::iterator iter = extensions_.begin();
iter != extensions_.end(); ++iter) {
delete *iter;
}
}
std::vector<std::string>* errors() {
return &errors_;
}
ExtensionList* extensions() {
return &extensions_;
}
std::vector<FilePath>* installed() {
return &installed_;
}
FilePath install_dir() {
return install_dir_;
}
// ExtensionsServiceFrontendInterface
virtual MessageLoop* GetMessageLoop() {
return &message_loop_;
}
virtual void InstallExtension(const FilePath& extension_path) {
}
virtual void LoadExtension(const FilePath& extension_path) {
}
virtual void OnExtensionLoadError(bool alert_on_error,
const std::string& message) {
// In the development environment, we get errors when trying to load
// extensions out of the .svn directories.
if (message.find(".svn") != std::string::npos)
return;
errors_.push_back(message);
}
virtual void OnExtensionsLoadedFromDirectory(ExtensionList* new_extensions) {
extensions_.insert(extensions_.end(), new_extensions->begin(),
new_extensions->end());
delete new_extensions;
// In the tests we rely on extensions and errors being in particular order,
// which is not always the case (and is not guaranteed by used APIs).
std::stable_sort(extensions_.begin(), extensions_.end(), ExtensionsOrder());
std::stable_sort(errors_.begin(), errors_.end());
}
virtual void OnExtensionInstallError(bool alert_on_error,
const std::string& message) {
errors_.push_back(message);
}
virtual void OnExtensionInstalled(FilePath path, bool is_update) {
installed_.push_back(path);
}
void TestInstallExtension(const FilePath& path,
ExtensionsServiceBackend* backend,
bool should_succeed) {
ASSERT_TRUE(file_util::PathExists(path));
backend->InstallExtension(path, install_dir_, false,
scoped_refptr<ExtensionsServiceFrontendInterface>(this));
message_loop_.RunAllPending();
if (should_succeed) {
EXPECT_EQ(1u, installed_.size());
EXPECT_EQ(0u, errors_.size()) << path.value();
} else {
EXPECT_EQ(0u, installed_.size());
EXPECT_EQ(1u, errors_.size());
}
installed_.clear();
errors_.clear();
}
private:
MessageLoop message_loop_;
ExtensionList extensions_;
std::vector<std::string> errors_;
std::vector<FilePath> installed_;
FilePath install_dir_;
};
// make the test a PlatformTest to setup autorelease pools properly on mac
typedef PlatformTest ExtensionsServiceTest;
// Test loading good extensions from the profile directory.
TEST_F(ExtensionsServiceTest, LoadAllExtensionsFromDirectorySuccess) {
FilePath extensions_path;
ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &extensions_path));
extensions_path = extensions_path.AppendASCII("extensions");
extensions_path = extensions_path.AppendASCII("good");
scoped_refptr<ExtensionsServiceBackend> backend(new ExtensionsServiceBackend);
scoped_refptr<ExtensionsServiceTestFrontend> frontend(
new ExtensionsServiceTestFrontend);
std::vector<Extension*> extensions;
backend->LoadExtensionsFromDirectory(extensions_path,
scoped_refptr<ExtensionsServiceFrontendInterface>(frontend.get()));
frontend->GetMessageLoop()->RunAllPending();
ASSERT_EQ(3u, frontend->extensions()->size());
EXPECT_EQ(std::string("com.google.myextension1"),
frontend->extensions()->at(0)->id());
EXPECT_EQ(std::string("My extension 1"),
frontend->extensions()->at(0)->name());
EXPECT_EQ(std::string("The first extension that I made."),
frontend->extensions()->at(0)->description());
Extension* extension = frontend->extensions()->at(0);
const UserScriptList& scripts = extension->content_scripts();
ASSERT_EQ(2u, scripts.size());
EXPECT_EQ(2u, scripts[0].url_patterns().size());
EXPECT_EQ("http://*.google.com/*",
scripts[0].url_patterns()[0].GetAsString());
EXPECT_EQ("https://*.google.com/*",
scripts[0].url_patterns()[1].GetAsString());
EXPECT_EQ(extension->path().AppendASCII("script1.js").value(),
scripts[0].path().value());
EXPECT_EQ(1u, scripts[1].url_patterns().size());
EXPECT_EQ("http://*.yahoo.com/*", scripts[1].url_patterns()[0].GetAsString());
EXPECT_EQ(extension->path().AppendASCII("script2.js").value(),
scripts[1].path().value());
EXPECT_EQ(std::string("com.google.myextension2"),
frontend->extensions()->at(1)->id());
EXPECT_EQ(std::string("My extension 2"),
frontend->extensions()->at(1)->name());
EXPECT_EQ(std::string(""),
frontend->extensions()->at(1)->description());
EXPECT_EQ(frontend->extensions()->at(1)->path().AppendASCII("npapi").value(),
frontend->extensions()->at(1)->plugins_dir().value());
ASSERT_EQ(0u, frontend->extensions()->at(1)->content_scripts().size());
EXPECT_EQ(std::string("com.google.myextension3"),
frontend->extensions()->at(2)->id());
EXPECT_EQ(std::string("My extension 3"),
frontend->extensions()->at(2)->name());
EXPECT_EQ(std::string(""),
frontend->extensions()->at(2)->description());
ASSERT_EQ(0u, frontend->extensions()->at(2)->content_scripts().size());
};
// Test loading bad extensions from the profile directory.
TEST_F(ExtensionsServiceTest, LoadAllExtensionsFromDirectoryFail) {
FilePath extensions_path;
ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &extensions_path));
extensions_path = extensions_path.AppendASCII("extensions");
extensions_path = extensions_path.AppendASCII("bad");
scoped_refptr<ExtensionsServiceBackend> backend(new ExtensionsServiceBackend);
scoped_refptr<ExtensionsServiceTestFrontend> frontend(
new ExtensionsServiceTestFrontend);
std::vector<Extension*> extensions;
backend->LoadExtensionsFromDirectory(extensions_path,
scoped_refptr<ExtensionsServiceFrontendInterface>(frontend.get()));
frontend->GetMessageLoop()->RunAllPending();
EXPECT_EQ(4u, frontend->errors()->size());
EXPECT_EQ(0u, frontend->extensions()->size());
EXPECT_TRUE(MatchPattern(frontend->errors()->at(0),
std::string("Could not load extension from '*'. * ") +
JSONReader::kBadRootElementType));
EXPECT_TRUE(MatchPattern(frontend->errors()->at(1),
std::string("Could not load extension from '*'. ") +
Extension::kInvalidJsListError));
EXPECT_TRUE(MatchPattern(frontend->errors()->at(2),
std::string("Could not load extension from '*'. ") +
Extension::kInvalidManifestError));
EXPECT_TRUE(MatchPattern(frontend->errors()->at(3),
"Could not load extension from '*'. Could not read '*' file."));
};
// Test installing extensions.
TEST_F(ExtensionsServiceTest, InstallExtension) {
FilePath extensions_path;
ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &extensions_path));
extensions_path = extensions_path.AppendASCII("extensions");
scoped_refptr<ExtensionsServiceBackend> backend(new ExtensionsServiceBackend);
scoped_refptr<ExtensionsServiceTestFrontend> frontend(
new ExtensionsServiceTestFrontend);
FilePath path = extensions_path.AppendASCII("good.crx");
// A simple extension that should install without error.
frontend->TestInstallExtension(path, backend, true);
// TODO(erikkay): verify the contents of the installed extension.
// Installing the same extension twice should fail.
frontend->TestInstallExtension(path, backend, false);
// 0-length extension file.
path = extensions_path.AppendASCII("not_an_extension.crx");
frontend->TestInstallExtension(path, backend, false);
// Bad magic number.
path = extensions_path.AppendASCII("bad_magic.crx");
frontend->TestInstallExtension(path, backend, false);
// Poorly formed JSON.
path = extensions_path.AppendASCII("bad_json.crx");
frontend->TestInstallExtension(path, backend, false);
// Incorrect zip hash.
path = extensions_path.AppendASCII("bad_hash.crx");
frontend->TestInstallExtension(path, backend, false);
// TODO(erikkay): add more tests for many of the failure cases.
// TODO(erikkay): add tests for upgrade cases.
}
TEST_F(ExtensionsServiceTest, LoadExtension) {
FilePath extensions_path;
ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &extensions_path));
extensions_path = extensions_path.AppendASCII("extensions");
scoped_refptr<ExtensionsServiceBackend> backend(new ExtensionsServiceBackend);
scoped_refptr<ExtensionsServiceTestFrontend> frontend(
new ExtensionsServiceTestFrontend);
FilePath ext1 = extensions_path.AppendASCII("good").AppendASCII("extension1")
.AppendASCII("1");
backend->LoadSingleExtension(ext1,
scoped_refptr<ExtensionsServiceFrontendInterface>(frontend.get()));
frontend->GetMessageLoop()->RunAllPending();
EXPECT_EQ(0u, frontend->errors()->size());
ASSERT_EQ(1u, frontend->extensions()->size());
FilePath no_manifest = extensions_path.AppendASCII("bad")
.AppendASCII("no_manifest").AppendASCII("1");
backend->LoadSingleExtension(no_manifest,
scoped_refptr<ExtensionsServiceFrontendInterface>(frontend.get()));
frontend->GetMessageLoop()->RunAllPending();
EXPECT_EQ(1u, frontend->errors()->size());
ASSERT_EQ(1u, frontend->extensions()->size());
}