[Cleanup] Used scoped pointers in KeyedServiceFactory's SetTestingFactory functions.
[chromium-blink-merge.git] / chrome / browser / extensions / api / developer_private / developer_private_api_unittest.cc
blob2b1358797687d57b6134718d1a5a5e620de74e16
1 // Copyright 2015 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.
5 #include "base/files/file_util.h"
6 #include "base/memory/scoped_ptr.h"
7 #include "base/strings/utf_string_conversions.h"
8 #include "chrome/browser/extensions/api/developer_private/developer_private_api.h"
9 #include "chrome/browser/extensions/error_console/error_console.h"
10 #include "chrome/browser/extensions/extension_function_test_utils.h"
11 #include "chrome/browser/extensions/extension_service.h"
12 #include "chrome/browser/extensions/extension_service_test_base.h"
13 #include "chrome/browser/extensions/extension_util.h"
14 #include "chrome/browser/extensions/test_extension_dir.h"
15 #include "chrome/browser/extensions/test_extension_system.h"
16 #include "chrome/browser/extensions/unpacked_installer.h"
17 #include "chrome/browser/ui/browser.h"
18 #include "chrome/browser/ui/host_desktop.h"
19 #include "chrome/common/extensions/api/developer_private.h"
20 #include "chrome/common/pref_names.h"
21 #include "chrome/test/base/test_browser_window.h"
22 #include "components/crx_file/id_util.h"
23 #include "content/public/test/test_web_contents_factory.h"
24 #include "extensions/browser/event_router_factory.h"
25 #include "extensions/browser/extension_error_test_util.h"
26 #include "extensions/browser/extension_prefs.h"
27 #include "extensions/browser/extension_registry.h"
28 #include "extensions/browser/extension_system.h"
29 #include "extensions/browser/test_extension_registry_observer.h"
30 #include "extensions/common/extension.h"
31 #include "extensions/common/extension_builder.h"
32 #include "extensions/common/extension_set.h"
33 #include "extensions/common/feature_switch.h"
34 #include "extensions/common/manifest_constants.h"
35 #include "extensions/common/test_util.h"
36 #include "extensions/common/value_builder.h"
38 namespace extensions {
40 namespace {
42 scoped_ptr<KeyedService> BuildAPI(content::BrowserContext* context) {
43 return make_scoped_ptr(new DeveloperPrivateAPI(context));
46 scoped_ptr<KeyedService> BuildEventRouter(content::BrowserContext* profile) {
47 return make_scoped_ptr(
48 new EventRouter(profile, ExtensionPrefs::Get(profile)));
51 } // namespace
53 class DeveloperPrivateApiUnitTest : public ExtensionServiceTestBase {
54 protected:
55 DeveloperPrivateApiUnitTest() {}
56 ~DeveloperPrivateApiUnitTest() override {}
58 // A wrapper around extension_function_test_utils::RunFunction that runs with
59 // the associated browser, no flags, and can take stack-allocated arguments.
60 bool RunFunction(const scoped_refptr<UIThreadExtensionFunction>& function,
61 const base::ListValue& args);
63 // Loads an unpacked extension that is backed by a real directory, allowing
64 // it to be reloaded.
65 const Extension* LoadUnpackedExtension();
67 // Loads an extension with no real directory; this is faster, but means the
68 // extension can't be reloaded.
69 const Extension* LoadSimpleExtension();
71 // Tests modifying the extension's configuration.
72 void TestExtensionPrefSetting(
73 bool (*has_pref)(const std::string&, content::BrowserContext*),
74 const std::string& key,
75 const std::string& extension_id);
77 testing::AssertionResult TestPackExtensionFunction(
78 const base::ListValue& args,
79 api::developer_private::PackStatus expected_status,
80 int expected_flags);
82 Browser* browser() { return browser_.get(); }
84 private:
85 // ExtensionServiceTestBase:
86 void SetUp() override;
87 void TearDown() override;
89 // The browser (and accompanying window).
90 scoped_ptr<TestBrowserWindow> browser_window_;
91 scoped_ptr<Browser> browser_;
93 ScopedVector<TestExtensionDir> test_extension_dirs_;
95 DISALLOW_COPY_AND_ASSIGN(DeveloperPrivateApiUnitTest);
98 bool DeveloperPrivateApiUnitTest::RunFunction(
99 const scoped_refptr<UIThreadExtensionFunction>& function,
100 const base::ListValue& args) {
101 return extension_function_test_utils::RunFunction(
102 function.get(),
103 make_scoped_ptr(args.DeepCopy()),
104 browser(),
105 extension_function_test_utils::NONE);
108 const Extension* DeveloperPrivateApiUnitTest::LoadUnpackedExtension() {
109 const char kManifest[] =
111 " \"name\": \"foo\","
112 " \"version\": \"1.0\","
113 " \"manifest_version\": 2,"
114 " \"permissions\": [\"*://*/*\"]"
115 "}";
117 test_extension_dirs_.push_back(new TestExtensionDir);
118 TestExtensionDir* dir = test_extension_dirs_.back();
119 dir->WriteManifest(kManifest);
121 // TODO(devlin): We should extract out methods to load an unpacked extension
122 // synchronously. We do it in ExtensionBrowserTest, but that's not helpful
123 // for unittests.
124 TestExtensionRegistryObserver registry_observer(registry());
125 scoped_refptr<UnpackedInstaller> installer(
126 UnpackedInstaller::Create(service()));
127 installer->Load(dir->unpacked_path());
128 base::FilePath extension_path =
129 base::MakeAbsoluteFilePath(dir->unpacked_path());
130 const Extension* extension = nullptr;
131 do {
132 extension = registry_observer.WaitForExtensionLoaded();
133 } while (extension->path() != extension_path);
134 // The fact that unpacked extensions get file access by default is an
135 // irrelevant detail to these tests. Disable it.
136 ExtensionPrefs::Get(browser_context())->SetAllowFileAccess(extension->id(),
137 false);
138 return extension;
141 const Extension* DeveloperPrivateApiUnitTest::LoadSimpleExtension() {
142 const char kName[] = "extension name";
143 const char kVersion[] = "1.0.0.1";
144 std::string id = crx_file::id_util::GenerateId(kName);
145 DictionaryBuilder manifest;
146 manifest.Set("name", kName)
147 .Set("version", kVersion)
148 .Set("manifest_version", 2)
149 .Set("description", "an extension");
150 scoped_refptr<const Extension> extension =
151 ExtensionBuilder().SetManifest(manifest)
152 .SetLocation(Manifest::INTERNAL)
153 .SetID(id)
154 .Build();
155 service()->AddExtension(extension.get());
156 return extension.get();
159 void DeveloperPrivateApiUnitTest::TestExtensionPrefSetting(
160 bool (*has_pref)(const std::string&, content::BrowserContext*),
161 const std::string& key,
162 const std::string& extension_id) {
163 scoped_refptr<UIThreadExtensionFunction> function(
164 new api::DeveloperPrivateUpdateExtensionConfigurationFunction());
166 base::ListValue args;
167 base::DictionaryValue* parameters = new base::DictionaryValue();
168 parameters->SetString("extensionId", extension_id);
169 parameters->SetBoolean(key, true);
170 args.Append(parameters);
172 EXPECT_FALSE(has_pref(extension_id, profile())) << key;
174 EXPECT_FALSE(RunFunction(function, args)) << key;
175 EXPECT_EQ(std::string("This action requires a user gesture."),
176 function->GetError());
178 ExtensionFunction::ScopedUserGestureForTests scoped_user_gesture;
179 function = new api::DeveloperPrivateUpdateExtensionConfigurationFunction();
180 EXPECT_TRUE(RunFunction(function, args)) << key;
181 EXPECT_TRUE(has_pref(extension_id, profile())) << key;
183 parameters->SetBoolean(key, false);
184 function = new api::DeveloperPrivateUpdateExtensionConfigurationFunction();
185 EXPECT_TRUE(RunFunction(function, args)) << key;
186 EXPECT_FALSE(has_pref(extension_id, profile())) << key;
189 testing::AssertionResult DeveloperPrivateApiUnitTest::TestPackExtensionFunction(
190 const base::ListValue& args,
191 api::developer_private::PackStatus expected_status,
192 int expected_flags) {
193 scoped_refptr<UIThreadExtensionFunction> function(
194 new api::DeveloperPrivatePackDirectoryFunction());
195 if (!RunFunction(function, args))
196 return testing::AssertionFailure() << "Could not run function.";
198 // Extract the result. We don't have to test this here, since it's verified as
199 // part of the general extension api system.
200 const base::Value* response_value = nullptr;
201 CHECK(function->GetResultList()->Get(0u, &response_value));
202 scoped_ptr<api::developer_private::PackDirectoryResponse> response =
203 api::developer_private::PackDirectoryResponse::FromValue(*response_value);
204 CHECK(response);
206 if (response->status != expected_status) {
207 return testing::AssertionFailure() << "Expected status: " <<
208 expected_status << ", found status: " << response->status <<
209 ", message: " << response->message;
212 if (response->override_flags != expected_flags) {
213 return testing::AssertionFailure() << "Expected flags: " <<
214 expected_flags << ", found flags: " << response->override_flags;
217 return testing::AssertionSuccess();
220 void DeveloperPrivateApiUnitTest::SetUp() {
221 ExtensionServiceTestBase::SetUp();
222 InitializeEmptyExtensionService();
224 browser_window_.reset(new TestBrowserWindow());
225 Browser::CreateParams params(profile(), chrome::HOST_DESKTOP_TYPE_NATIVE);
226 params.type = Browser::TYPE_TABBED;
227 params.window = browser_window_.get();
228 browser_.reset(new Browser(params));
230 // Allow the API to be created.
231 EventRouterFactory::GetInstance()->SetTestingFactory(profile(),
232 &BuildEventRouter);
234 DeveloperPrivateAPI::GetFactoryInstance()->SetTestingFactory(
235 profile(), &BuildAPI);
238 void DeveloperPrivateApiUnitTest::TearDown() {
239 test_extension_dirs_.clear();
240 browser_.reset();
241 browser_window_.reset();
242 ExtensionServiceTestBase::TearDown();
245 // Test developerPrivate.updateExtensionConfiguration.
246 TEST_F(DeveloperPrivateApiUnitTest,
247 DeveloperPrivateUpdateExtensionConfiguration) {
248 FeatureSwitch::ScopedOverride scripts_require_action(
249 FeatureSwitch::scripts_require_action(), true);
250 // Sadly, we need a "real" directory here, because toggling prefs causes
251 // a reload (which needs a path).
252 std::string id = LoadUnpackedExtension()->id();
254 TestExtensionPrefSetting(&util::IsIncognitoEnabled, "incognitoAccess", id);
255 TestExtensionPrefSetting(&util::AllowFileAccess, "fileAccess", id);
256 TestExtensionPrefSetting(
257 &util::AllowedScriptingOnAllUrls, "runOnAllUrls", id);
260 // Test developerPrivate.reload.
261 TEST_F(DeveloperPrivateApiUnitTest, DeveloperPrivateReload) {
262 const Extension* extension = LoadUnpackedExtension();
263 std::string extension_id = extension->id();
264 scoped_refptr<UIThreadExtensionFunction> function(
265 new api::DeveloperPrivateReloadFunction());
266 base::ListValue reload_args;
267 reload_args.AppendString(extension_id);
269 TestExtensionRegistryObserver registry_observer(registry());
270 EXPECT_TRUE(RunFunction(function, reload_args));
271 const Extension* unloaded_extension =
272 registry_observer.WaitForExtensionUnloaded();
273 EXPECT_EQ(extension, unloaded_extension);
274 const Extension* reloaded_extension =
275 registry_observer.WaitForExtensionLoaded();
276 EXPECT_EQ(extension_id, reloaded_extension->id());
279 // Test developerPrivate.packDirectory.
280 TEST_F(DeveloperPrivateApiUnitTest, DeveloperPrivatePackFunction) {
281 ResetThreadBundle(content::TestBrowserThreadBundle::DEFAULT);
283 base::FilePath root_path = data_dir().AppendASCII("good_unpacked");
284 base::FilePath crx_path = data_dir().AppendASCII("good_unpacked.crx");
285 base::FilePath pem_path = data_dir().AppendASCII("good_unpacked.pem");
287 // First, test a directory that should pack properly.
288 base::ListValue pack_args;
289 pack_args.AppendString(root_path.AsUTF8Unsafe());
290 EXPECT_TRUE(TestPackExtensionFunction(
291 pack_args, api::developer_private::PACK_STATUS_SUCCESS, 0));
293 // Should have created crx file and pem file.
294 EXPECT_TRUE(base::PathExists(crx_path));
295 EXPECT_TRUE(base::PathExists(pem_path));
297 // Deliberately don't cleanup the files, and append the pem path.
298 pack_args.AppendString(pem_path.AsUTF8Unsafe());
300 // Try to pack again - we should get a warning abot overwriting the crx.
301 EXPECT_TRUE(TestPackExtensionFunction(
302 pack_args,
303 api::developer_private::PACK_STATUS_WARNING,
304 ExtensionCreator::kOverwriteCRX));
306 // Try to pack again, with the overwrite flag; this should succeed.
307 pack_args.AppendInteger(ExtensionCreator::kOverwriteCRX);
308 EXPECT_TRUE(TestPackExtensionFunction(
309 pack_args, api::developer_private::PACK_STATUS_SUCCESS, 0));
311 // Try to pack a final time when omitting (an existing) pem file. We should
312 // get an error.
313 base::DeleteFile(crx_path, false);
314 EXPECT_TRUE(pack_args.Remove(1u, nullptr)); // Remove the pem key argument.
315 EXPECT_TRUE(pack_args.Remove(1u, nullptr)); // Remove the flags argument.
316 EXPECT_TRUE(TestPackExtensionFunction(
317 pack_args, api::developer_private::PACK_STATUS_ERROR, 0));
319 base::DeleteFile(crx_path, false);
320 base::DeleteFile(pem_path, false);
323 // Test developerPrivate.choosePath.
324 TEST_F(DeveloperPrivateApiUnitTest, DeveloperPrivateChoosePath) {
325 ResetThreadBundle(content::TestBrowserThreadBundle::DEFAULT);
326 content::TestWebContentsFactory web_contents_factory;
327 content::WebContents* web_contents =
328 web_contents_factory.CreateWebContents(profile());
330 base::FilePath expected_dir_path = data_dir().AppendASCII("good_unpacked");
331 api::EntryPicker::SkipPickerAndAlwaysSelectPathForTest(&expected_dir_path);
333 // Try selecting a directory.
334 base::ListValue choose_args;
335 choose_args.AppendString("FOLDER");
336 choose_args.AppendString("LOAD");
337 scoped_refptr<UIThreadExtensionFunction> function(
338 new api::DeveloperPrivateChoosePathFunction());
339 function->SetRenderViewHost(web_contents->GetRenderViewHost());
340 EXPECT_TRUE(RunFunction(function, choose_args)) << function->GetError();
341 std::string path;
342 EXPECT_TRUE(function->GetResultList() &&
343 function->GetResultList()->GetString(0, &path));
344 EXPECT_EQ(path, expected_dir_path.AsUTF8Unsafe());
346 // Try selecting a pem file.
347 base::FilePath expected_file_path =
348 data_dir().AppendASCII("good_unpacked.pem");
349 api::EntryPicker::SkipPickerAndAlwaysSelectPathForTest(&expected_file_path);
350 choose_args.Clear();
351 choose_args.AppendString("FILE");
352 choose_args.AppendString("PEM");
353 function = new api::DeveloperPrivateChoosePathFunction();
354 function->SetRenderViewHost(web_contents->GetRenderViewHost());
355 EXPECT_TRUE(RunFunction(function, choose_args)) << function->GetError();
356 EXPECT_TRUE(function->GetResultList() &&
357 function->GetResultList()->GetString(0, &path));
358 EXPECT_EQ(path, expected_file_path.AsUTF8Unsafe());
360 // Try canceling the file dialog.
361 api::EntryPicker::SkipPickerAndAlwaysCancelForTest();
362 function = new api::DeveloperPrivateChoosePathFunction();
363 function->SetRenderViewHost(web_contents->GetRenderViewHost());
364 EXPECT_FALSE(RunFunction(function, choose_args));
365 EXPECT_EQ(std::string("File selection was canceled."), function->GetError());
368 // Test developerPrivate.loadUnpacked.
369 TEST_F(DeveloperPrivateApiUnitTest, DeveloperPrivateLoadUnpacked) {
370 ResetThreadBundle(content::TestBrowserThreadBundle::DEFAULT);
371 content::TestWebContentsFactory web_contents_factory;
372 content::WebContents* web_contents =
373 web_contents_factory.CreateWebContents(profile());
375 base::FilePath path = data_dir().AppendASCII("good_unpacked");
376 api::EntryPicker::SkipPickerAndAlwaysSelectPathForTest(&path);
378 // Try loading a good extension (it should succeed, and the extension should
379 // be added).
380 scoped_refptr<UIThreadExtensionFunction> function(
381 new api::DeveloperPrivateLoadUnpackedFunction());
382 function->SetRenderViewHost(web_contents->GetRenderViewHost());
383 ExtensionIdSet current_ids = registry()->enabled_extensions().GetIDs();
384 EXPECT_TRUE(RunFunction(function, base::ListValue())) << function->GetError();
385 // We should have added one new extension.
386 ExtensionIdSet id_difference = base::STLSetDifference<ExtensionIdSet>(
387 registry()->enabled_extensions().GetIDs(), current_ids);
388 ASSERT_EQ(1u, id_difference.size());
389 // The new extension should have the same path.
390 EXPECT_EQ(
391 path,
392 registry()->enabled_extensions().GetByID(*id_difference.begin())->path());
394 path = data_dir().AppendASCII("empty_manifest");
395 api::EntryPicker::SkipPickerAndAlwaysSelectPathForTest(&path);
397 // Try loading a bad extension (it should fail, and we should get an error).
398 function = new api::DeveloperPrivateLoadUnpackedFunction();
399 function->SetRenderViewHost(web_contents->GetRenderViewHost());
400 base::ListValue unpacked_args;
401 scoped_ptr<base::DictionaryValue> options(new base::DictionaryValue());
402 options->SetBoolean("failQuietly", true);
403 unpacked_args.Append(options.release());
404 current_ids = registry()->enabled_extensions().GetIDs();
405 EXPECT_FALSE(RunFunction(function, unpacked_args));
406 EXPECT_EQ(manifest_errors::kManifestUnreadable, function->GetError());
407 // We should have no new extensions installed.
408 EXPECT_EQ(0u, base::STLSetDifference<ExtensionIdSet>(
409 registry()->enabled_extensions().GetIDs(),
410 current_ids).size());
413 // Test developerPrivate.requestFileSource.
414 TEST_F(DeveloperPrivateApiUnitTest, DeveloperPrivateRequestFileSource) {
415 ResetThreadBundle(content::TestBrowserThreadBundle::DEFAULT);
416 // Testing of this function seems light, but that's because it basically just
417 // forwards to reading a file to a string, and highlighting it - both of which
418 // are already tested separately.
419 const Extension* extension = LoadUnpackedExtension();
420 const char kErrorMessage[] = "Something went wrong";
421 api::developer_private::RequestFileSourceProperties properties;
422 properties.extension_id = extension->id();
423 properties.path_suffix = "manifest.json";
424 properties.message = kErrorMessage;
425 properties.manifest_key.reset(new std::string("name"));
427 scoped_refptr<UIThreadExtensionFunction> function(
428 new api::DeveloperPrivateRequestFileSourceFunction());
429 base::ListValue file_source_args;
430 file_source_args.Append(properties.ToValue().release());
431 EXPECT_TRUE(RunFunction(function, file_source_args)) << function->GetError();
433 const base::Value* response_value = nullptr;
434 ASSERT_TRUE(function->GetResultList()->Get(0u, &response_value));
435 scoped_ptr<api::developer_private::RequestFileSourceResponse> response =
436 api::developer_private::RequestFileSourceResponse::FromValue(
437 *response_value);
438 EXPECT_FALSE(response->before_highlight.empty());
439 EXPECT_EQ("\"name\": \"foo\"", response->highlight);
440 EXPECT_FALSE(response->after_highlight.empty());
441 EXPECT_EQ("foo: manifest.json", response->title);
442 EXPECT_EQ(kErrorMessage, response->message);
445 // Test developerPrivate.getExtensionsInfo.
446 TEST_F(DeveloperPrivateApiUnitTest, DeveloperPrivateGetExtensionsInfo) {
447 ResetThreadBundle(content::TestBrowserThreadBundle::DEFAULT);
448 LoadSimpleExtension();
450 // The test here isn't so much about the generated value (that's tested in
451 // ExtensionInfoGenerator's unittest), but rather just to make sure we can
452 // serialize/deserialize the result - which implicity tests that everything
453 // has a sane value.
454 scoped_refptr<UIThreadExtensionFunction> function(
455 new api::DeveloperPrivateGetExtensionsInfoFunction());
456 EXPECT_TRUE(RunFunction(function, base::ListValue())) << function->GetError();
457 const base::ListValue* results = function->GetResultList();
458 ASSERT_EQ(1u, results->GetSize());
459 const base::ListValue* list = nullptr;
460 ASSERT_TRUE(results->GetList(0u, &list));
461 ASSERT_EQ(1u, list->GetSize());
462 const base::Value* value = nullptr;
463 ASSERT_TRUE(list->Get(0u, &value));
464 scoped_ptr<api::developer_private::ExtensionInfo> info =
465 api::developer_private::ExtensionInfo::FromValue(*value);
466 ASSERT_TRUE(info);
468 // As a sanity check, also run the GetItemsInfo and make sure it returns a
469 // sane value.
470 function = new api::DeveloperPrivateGetItemsInfoFunction();
471 base::ListValue args;
472 args.AppendBoolean(false);
473 args.AppendBoolean(false);
474 EXPECT_TRUE(RunFunction(function, args)) << function->GetError();
475 results = function->GetResultList();
476 ASSERT_EQ(1u, results->GetSize());
477 ASSERT_TRUE(results->GetList(0u, &list));
478 ASSERT_EQ(1u, list->GetSize());
479 ASSERT_TRUE(list->Get(0u, &value));
480 scoped_ptr<api::developer_private::ItemInfo> item_info =
481 api::developer_private::ItemInfo::FromValue(*value);
482 ASSERT_TRUE(item_info);
485 // Test developerPrivate.deleteExtensionErrors.
486 TEST_F(DeveloperPrivateApiUnitTest, DeveloperPrivateDeleteExtensionErrors) {
487 FeatureSwitch::ScopedOverride error_console_override(
488 FeatureSwitch::error_console(), true);
489 profile()->GetPrefs()->SetBoolean(prefs::kExtensionsUIDeveloperMode, true);
490 const Extension* extension = LoadSimpleExtension();
492 // Report some errors.
493 ErrorConsole* error_console = ErrorConsole::Get(profile());
494 error_console->SetReportingAllForExtension(extension->id(), true);
495 error_console->ReportError(
496 error_test_util::CreateNewRuntimeError(extension->id(), "foo"));
497 error_console->ReportError(
498 error_test_util::CreateNewRuntimeError(extension->id(), "bar"));
499 error_console->ReportError(
500 error_test_util::CreateNewManifestError(extension->id(), "baz"));
501 EXPECT_EQ(3u, error_console->GetErrorsForExtension(extension->id()).size());
503 // Start by removing all errors for the extension of a given type (manifest).
504 std::string type_string = api::developer_private::ToString(
505 api::developer_private::ERROR_TYPE_MANIFEST);
506 scoped_ptr<base::ListValue> args =
507 ListBuilder()
508 .Append(DictionaryBuilder()
509 .Set("extensionId", extension->id())
510 .Set("type", type_string))
511 .Build();
512 scoped_refptr<UIThreadExtensionFunction> function =
513 new api::DeveloperPrivateDeleteExtensionErrorsFunction();
514 EXPECT_TRUE(RunFunction(function, *args)) << function->GetError();
515 // Two errors should remain.
516 const ErrorList& error_list =
517 error_console->GetErrorsForExtension(extension->id());
518 ASSERT_EQ(2u, error_list.size());
520 // Next remove errors by id.
521 int error_id = error_list[0]->id();
522 args = ListBuilder()
523 .Append(DictionaryBuilder()
524 .Set("extensionId", extension->id())
525 .Set("errorIds", ListBuilder().Append(error_id)))
526 .Build();
527 function = new api::DeveloperPrivateDeleteExtensionErrorsFunction();
528 EXPECT_TRUE(RunFunction(function, *args)) << function->GetError();
529 // And then there was one.
530 EXPECT_EQ(1u, error_console->GetErrorsForExtension(extension->id()).size());
532 // Finally remove all errors for the extension.
533 args = ListBuilder()
534 .Append(DictionaryBuilder().Set("extensionId", extension->id()))
535 .Build();
536 function = new api::DeveloperPrivateDeleteExtensionErrorsFunction();
537 EXPECT_TRUE(RunFunction(function, *args)) << function->GetError();
538 // No more errors!
539 EXPECT_TRUE(error_console->GetErrorsForExtension(extension->id()).empty());
542 } // namespace extensions