1// Copyright (c) 2012 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/common/extensions/manifest_tests/extension_manifest_test.h"
6
7#include "base/file_util.h"
8#include "base/files/file_path.h"
9#include "base/json/json_file_value_serializer.h"
10#include "base/path_service.h"
11#include "base/values.h"
12#include "chrome/common/chrome_paths.h"
13#include "extensions/common/extension_l10n_util.h"
14#include "extensions/common/test_util.h"
15#include "ui/base/l10n/l10n_util.h"
16
17using extensions::Extension;
18
19namespace {
20
21// If filename is a relative path, LoadManifestFile will treat it relative to
22// the appropriate test directory.
23base::DictionaryValue* LoadManifestFile(const base::FilePath& filename_path,
24                                        std::string* error) {
25  base::FilePath extension_path;
26  base::FilePath manifest_path;
27
28  PathService::Get(chrome::DIR_TEST_DATA, &manifest_path);
29  manifest_path = manifest_path.Append(filename_path);
30  extension_path = manifest_path.DirName();
31
32  EXPECT_TRUE(base::PathExists(manifest_path)) <<
33      "Couldn't find " << manifest_path.value();
34
35  JSONFileValueSerializer serializer(manifest_path);
36  base::DictionaryValue* manifest =
37      static_cast<base::DictionaryValue*>(serializer.Deserialize(NULL, error));
38
39  // Most unit tests don't need localization, and they'll fail if we try to
40  // localize them, since their manifests don't have a default_locale key.
41  // Only localize manifests that indicate they want to be localized.
42  // Calling LocalizeExtension at this point mirrors file_util::LoadExtension.
43  if (manifest &&
44      filename_path.value().find(FILE_PATH_LITERAL("localized")) !=
45      std::string::npos)
46    extension_l10n_util::LocalizeExtension(extension_path, manifest, error);
47
48  return manifest;
49}
50
51}  // namespace
52
53ExtensionManifestTest::ExtensionManifestTest()
54    : enable_apps_(true),
55      // UNKNOWN == trunk.
56      current_channel_(chrome::VersionInfo::CHANNEL_UNKNOWN) {}
57
58// Helper class that simplifies creating methods that take either a filename
59// to a manifest or the manifest itself.
60ExtensionManifestTest::Manifest::Manifest(const char* name)
61    : name_(name), manifest_(NULL) {
62}
63
64ExtensionManifestTest::Manifest::Manifest(base::DictionaryValue* manifest,
65                                          const char* name)
66    : name_(name), manifest_(manifest) {
67  CHECK(manifest_) << "Manifest NULL";
68}
69
70ExtensionManifestTest::Manifest::Manifest(
71    scoped_ptr<base::DictionaryValue> manifest)
72    : manifest_(manifest.get()), manifest_holder_(manifest.Pass()) {
73  CHECK(manifest_) << "Manifest NULL";
74}
75
76ExtensionManifestTest::Manifest::Manifest(const Manifest& m) {
77  NOTREACHED();
78}
79
80ExtensionManifestTest::Manifest::~Manifest() {
81}
82
83base::DictionaryValue* ExtensionManifestTest::Manifest::GetManifest(
84    char const* test_data_dir, std::string* error) const {
85  if (manifest_)
86    return manifest_;
87
88  base::FilePath filename_path;
89  filename_path = filename_path.AppendASCII("extensions")
90      .AppendASCII(test_data_dir)
91      .AppendASCII(name_);
92  manifest_ = LoadManifestFile(filename_path, error);
93  manifest_holder_.reset(manifest_);
94  return manifest_;
95}
96
97char const* ExtensionManifestTest::test_data_dir() {
98  return "manifest_tests";
99}
100
101scoped_ptr<base::DictionaryValue> ExtensionManifestTest::LoadManifest(
102    char const* manifest_name, std::string* error) {
103  base::FilePath filename_path;
104  filename_path = filename_path.AppendASCII("extensions")
105      .AppendASCII(test_data_dir())
106      .AppendASCII(manifest_name);
107  return make_scoped_ptr(LoadManifestFile(filename_path, error));
108}
109
110scoped_refptr<Extension> ExtensionManifestTest::LoadExtension(
111    const Manifest& manifest,
112    std::string* error,
113    extensions::Manifest::Location location,
114    int flags) {
115  base::DictionaryValue* value = manifest.GetManifest(test_data_dir(), error);
116  if (!value)
117    return NULL;
118  base::FilePath path;
119  PathService::Get(chrome::DIR_TEST_DATA, &path);
120  path = path.AppendASCII("extensions").AppendASCII(test_data_dir());
121  return Extension::Create(path.DirName(), location, *value, flags, error);
122}
123
124scoped_refptr<Extension> ExtensionManifestTest::LoadAndExpectSuccess(
125    const Manifest& manifest,
126    extensions::Manifest::Location location,
127    int flags) {
128  std::string error;
129  scoped_refptr<Extension> extension =
130      LoadExtension(manifest, &error, location, flags);
131  EXPECT_TRUE(extension.get()) << manifest.name();
132  EXPECT_EQ("", error) << manifest.name();
133  return extension;
134}
135
136scoped_refptr<Extension> ExtensionManifestTest::LoadAndExpectSuccess(
137    char const* manifest_name,
138    extensions::Manifest::Location location,
139    int flags) {
140  return LoadAndExpectSuccess(Manifest(manifest_name), location, flags);
141}
142
143scoped_refptr<Extension> ExtensionManifestTest::LoadFromStringAndExpectSuccess(
144    char const* manifest_json) {
145  return LoadAndExpectSuccess(
146      Manifest(extensions::test_util::ParseJsonDictionaryWithSingleQuotes(
147          manifest_json)));
148}
149
150scoped_refptr<Extension> ExtensionManifestTest::LoadAndExpectWarning(
151    const Manifest& manifest,
152    const std::string& expected_warning,
153    extensions::Manifest::Location location,
154    int flags) {
155  std::string error;
156  scoped_refptr<Extension> extension =
157      LoadExtension(manifest, &error, location, flags);
158  EXPECT_TRUE(extension.get()) << manifest.name();
159  EXPECT_EQ("", error) << manifest.name();
160  EXPECT_EQ(1u, extension->install_warnings().size());
161  EXPECT_EQ(expected_warning, extension->install_warnings()[0].message);
162  return extension;
163}
164
165scoped_refptr<Extension> ExtensionManifestTest::LoadAndExpectWarning(
166    char const* manifest_name,
167    const std::string& expected_warning,
168    extensions::Manifest::Location location,
169    int flags) {
170  return LoadAndExpectWarning(
171      Manifest(manifest_name), expected_warning, location, flags);
172}
173
174void ExtensionManifestTest::VerifyExpectedError(
175    Extension* extension,
176    const std::string& name,
177    const std::string& error,
178    const std::string& expected_error) {
179  EXPECT_FALSE(extension) <<
180      "Expected failure loading extension '" << name <<
181      "', but didn't get one.";
182  EXPECT_TRUE(MatchPattern(error, expected_error)) << name <<
183      " expected '" << expected_error << "' but got '" << error << "'";
184}
185
186void ExtensionManifestTest::LoadAndExpectError(
187    const Manifest& manifest,
188    const std::string& expected_error,
189    extensions::Manifest::Location location,
190    int flags) {
191  std::string error;
192  scoped_refptr<Extension> extension(
193      LoadExtension(manifest, &error, location, flags));
194  VerifyExpectedError(extension.get(), manifest.name(), error,
195                      expected_error);
196}
197
198void ExtensionManifestTest::LoadAndExpectError(
199    char const* manifest_name,
200    const std::string& expected_error,
201    extensions::Manifest::Location location,
202    int flags) {
203  return LoadAndExpectError(
204      Manifest(manifest_name), expected_error, location, flags);
205}
206
207void ExtensionManifestTest::LoadFromStringAndExpectError(
208    char const* manifest_json,
209    const std::string& expected_error) {
210  return LoadAndExpectError(
211      Manifest(extensions::test_util::ParseJsonDictionaryWithSingleQuotes(
212          manifest_json)),
213      expected_error);
214}
215
216void ExtensionManifestTest::AddPattern(extensions::URLPatternSet* extent,
217                                       const std::string& pattern) {
218  int schemes = URLPattern::SCHEME_ALL;
219  extent->AddPattern(URLPattern(schemes, pattern));
220}
221
222ExtensionManifestTest::Testcase::Testcase(
223    std::string manifest_filename,
224    std::string expected_error,
225    extensions::Manifest::Location location,
226    int flags)
227    : manifest_filename_(manifest_filename),
228      expected_error_(expected_error),
229      location_(location), flags_(flags) {
230}
231
232ExtensionManifestTest::Testcase::Testcase(std::string manifest_filename,
233                                          std::string expected_error)
234    : manifest_filename_(manifest_filename),
235      expected_error_(expected_error),
236      location_(extensions::Manifest::INTERNAL),
237      flags_(Extension::NO_FLAGS) {
238}
239
240ExtensionManifestTest::Testcase::Testcase(std::string manifest_filename)
241    : manifest_filename_(manifest_filename),
242      location_(extensions::Manifest::INTERNAL),
243      flags_(Extension::NO_FLAGS) {}
244
245ExtensionManifestTest::Testcase::Testcase(
246    std::string manifest_filename,
247    extensions::Manifest::Location location,
248    int flags)
249    : manifest_filename_(manifest_filename),
250      location_(location),
251      flags_(flags) {}
252
253void ExtensionManifestTest::RunTestcases(const Testcase* testcases,
254                                         size_t num_testcases,
255                                         ExpectType type) {
256  for (size_t i = 0; i < num_testcases; ++i)
257    RunTestcase(testcases[i], type);
258}
259
260void ExtensionManifestTest::RunTestcase(const Testcase& testcase,
261                                        ExpectType type) {
262  switch (type) {
263    case EXPECT_TYPE_ERROR:
264      LoadAndExpectError(testcase.manifest_filename_.c_str(),
265                         testcase.expected_error_,
266                         testcase.location_,
267                         testcase.flags_);
268      break;
269    case EXPECT_TYPE_WARNING:
270      LoadAndExpectWarning(testcase.manifest_filename_.c_str(),
271                           testcase.expected_error_,
272                           testcase.location_,
273                           testcase.flags_);
274      break;
275    case EXPECT_TYPE_SUCCESS:
276      LoadAndExpectSuccess(testcase.manifest_filename_.c_str(),
277                           testcase.location_,
278                           testcase.flags_);
279      break;
280   }
281}
282