OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2011 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 <string> |
| 6 #include <vector> |
| 7 |
| 8 #include "base/command_line.h" |
| 9 #include "base/stringprintf.h" |
| 10 #include "base/basictypes.h" |
| 11 #include "base/logging.h" |
| 12 #include "base/memory/ref_counted.h" |
| 13 #include "base/memory/scoped_ptr.h" |
| 14 #include "base/message_loop.h" |
| 15 #include "base/string16.h" |
| 16 #include "chrome/browser/extensions/extension_browsertest.h" |
| 17 #include "chrome/browser/extensions/extension_service.h" |
| 18 #include "chrome/browser/extensions/webstore_bundle.h" |
| 19 #include "chrome/browser/tabs/tab_strip_model.h" |
| 20 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" |
| 21 #include "chrome/browser/ui/browser.h" |
| 22 #include "chrome/common/chrome_switches.h" |
| 23 #include "chrome/test/base/testing_profile.h" |
| 24 #include "chrome/test/base/ui_test_utils.h" |
| 25 #include "content/browser/browser_thread.h" |
| 26 #include "content/browser/tab_contents/navigation_controller.h" |
| 27 #include "googleurl/src/gurl.h" |
| 28 #include "net/base/host_port_pair.h" |
| 29 #include "net/base/mock_host_resolver.h" |
| 30 |
| 31 namespace { |
| 32 |
| 33 // These are just shorter aliases for some of the WebstoreBundle enums. |
| 34 WebstoreBundle::ItemMatcher MATCH_ALL = WebstoreBundle::MATCH_ALL; |
| 35 WebstoreBundle::ItemMatcher MATCH_APPS = WebstoreBundle::MATCH_APPS; |
| 36 WebstoreBundle::ItemMatcher MATCH_EXTENSIONS = WebstoreBundle::MATCH_EXTENSIONS; |
| 37 |
| 38 WebstoreBundle::Item::State STATE_INITIAL = |
| 39 WebstoreBundle::Item::STATE_INITIAL; |
| 40 WebstoreBundle::Item::State STATE_APPROVED = |
| 41 WebstoreBundle::Item::STATE_APPROVED; |
| 42 WebstoreBundle::Item::State STATE_INSTALLED = |
| 43 WebstoreBundle::Item::STATE_INSTALLED; |
| 44 WebstoreBundle::Item::State STATE_NOT_INSTALLED = |
| 45 WebstoreBundle::Item::STATE_NOT_INSTALLED; |
| 46 |
| 47 const char kGalleryDomain[] = "cws.com"; |
| 48 |
| 49 struct TestItem { |
| 50 std::string id; |
| 51 std::string localized_name; |
| 52 std::string manifest; |
| 53 }; |
| 54 |
| 55 const TestItem kBundleItems[] = { |
| 56 { "pkapffpjmiilhlhbibjhamlmdhfneidj", |
| 57 "Bundle App 1", |
| 58 "{" |
| 59 " \"name\": \"Bundle App 1\"," |
| 60 " \"version\": \"1\"," |
| 61 " \"app\": {" |
| 62 " \"urls\": [ \"http://www.testapp.com\" ]," |
| 63 " \"launch\": { \"web_url\": \"http://www.testapp.com\" }" |
| 64 " }," |
| 65 " \"permissions\": [ \"clipboardRead\" ]" |
| 66 "}" }, |
| 67 { "bmfoocgfinpmkmlbjhcbofejhkhlbchk", |
| 68 "Extension Bundle 1", |
| 69 "{" |
| 70 " \"name\": \"Extension Bundle 1\"," |
| 71 " \"version\": \"1\"," |
| 72 " \"permissions\": [ \"tabs\" ]" |
| 73 "}" }, |
| 74 { "mpneghmdnmaolkljkipbhaienajcflfe", |
| 75 "Extension Bundle 2", |
| 76 "{" |
| 77 " \"name\": \"Extension Bundle 2\"," |
| 78 " \"version\": \"1\"," |
| 79 " \"permissions\": [\"management\", \"http://google.com\" ]," |
| 80 " \"content_script\": [{" |
| 81 " \"matches\": [ \"http://www.example.com/*\" ]," |
| 82 " \"js\": [ \"content_script.js\" ]," |
| 83 " \"run_at\": \"document_start\"" |
| 84 " }]" |
| 85 "}" } |
| 86 }; |
| 87 |
| 88 const TestItem k404BundleItems[] = { |
| 89 { "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", |
| 90 "Extension A", |
| 91 "{" |
| 92 " \"name\": \"Extension A\"," |
| 93 " \"version\": \"1\"," |
| 94 " \"permissions\": [ \"tabs\" ]" |
| 95 "}" } |
| 96 }; |
| 97 |
| 98 const TestItem kBadItems[] = { |
| 99 { "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", |
| 100 "Extension B", |
| 101 "{" |
| 102 " \"name\": \"Extension B\"," |
| 103 " \"version\": \"1\"," |
| 104 " \"permissions\" [ \"http://*/*\" ]" // Syntax error on this line. |
| 105 "}" }, |
| 106 }; |
| 107 |
| 108 const TestItem kBadCrxItems[] = { |
| 109 { "cccccccccccccccccccccccccccccccc", |
| 110 "Extension C", |
| 111 "{" |
| 112 " \"name\": \"Extension C\"," |
| 113 " \"version\": \"1\"," |
| 114 " \"permissions\": [ \"bookmarks\", \"http://example.org/*\" ]" |
| 115 "}" }, |
| 116 }; |
| 117 |
| 118 // A WebstoreBundle::Delegate that waits for the callbacks and record the |
| 119 // results. |
| 120 class WebstoreBundleListener : public WebstoreBundle::Delegate { |
| 121 public: |
| 122 WebstoreBundleListener() |
| 123 : approved_(false), |
| 124 canceled_(false), |
| 125 completed_(false), |
| 126 waiting_(false) {} |
| 127 virtual ~WebstoreBundleListener() {} |
| 128 |
| 129 void OnBundleInstallApproved() OVERRIDE; |
| 130 void OnBundleInstallCanceled(bool user_initiated) OVERRIDE; |
| 131 void OnBundleInstallCompleted() OVERRIDE; |
| 132 |
| 133 void ResetResults(); |
| 134 void Wait(); |
| 135 |
| 136 bool approved() { return approved_; } |
| 137 bool canceled() { return canceled_; } |
| 138 bool completed() { return completed_; } |
| 139 |
| 140 private: |
| 141 void QuitMessageLoopIfWaiting(); |
| 142 |
| 143 bool approved_; |
| 144 bool canceled_; |
| 145 bool completed_; |
| 146 bool waiting_; |
| 147 }; |
| 148 |
| 149 void WebstoreBundleListener::OnBundleInstallApproved() { |
| 150 approved_ = true; |
| 151 QuitMessageLoopIfWaiting(); |
| 152 } |
| 153 |
| 154 void WebstoreBundleListener::OnBundleInstallCanceled(bool user_initiated) { |
| 155 canceled_ = true; |
| 156 QuitMessageLoopIfWaiting(); |
| 157 } |
| 158 |
| 159 void WebstoreBundleListener::OnBundleInstallCompleted() { |
| 160 completed_ = true; |
| 161 QuitMessageLoopIfWaiting(); |
| 162 } |
| 163 |
| 164 void WebstoreBundleListener::ResetResults() { |
| 165 approved_ = false; |
| 166 canceled_ = false; |
| 167 completed_ = false; |
| 168 } |
| 169 |
| 170 void WebstoreBundleListener::Wait() { |
| 171 if (approved_ || canceled_ || completed_) |
| 172 return; |
| 173 |
| 174 waiting_ = true; |
| 175 ui_test_utils::RunMessageLoop(); |
| 176 } |
| 177 |
| 178 void WebstoreBundleListener::QuitMessageLoopIfWaiting() { |
| 179 if (waiting_) { |
| 180 waiting_ = false; |
| 181 MessageLoopForUI::current()->Quit(); |
| 182 } |
| 183 } |
| 184 |
| 185 class WebstoreBundleTest : public ExtensionBrowserTest { |
| 186 public: |
| 187 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE; |
| 188 virtual void SetUpInProcessBrowserTestFixture() OVERRIDE; |
| 189 protected: |
| 190 GURL GetTestServerURL(const std::string& domain, const std::string& filename); |
| 191 }; |
| 192 |
| 193 void WebstoreBundleTest::SetUpCommandLine(CommandLine* command_line) { |
| 194 // We start the test server now instead of in SetUpInProcessTestFixture so |
| 195 // we can get its port number. |
| 196 ASSERT_TRUE(test_server()->Start()); |
| 197 |
| 198 InProcessBrowserTest::SetUpCommandLine(command_line); |
| 199 |
| 200 net::HostPortPair host_pair = test_server()->host_port_pair(); |
| 201 std::string download_url = base::StringPrintf( |
| 202 "http://%s:%d/files/extensions/api_test/webstore_private/bundle/%s.crx", |
| 203 kGalleryDomain, host_pair.port(), "%s"); |
| 204 command_line->AppendSwitchASCII( |
| 205 switches::kAppsGalleryDownloadURL, download_url); |
| 206 command_line->AppendSwitchASCII( |
| 207 switches::kAppsGalleryURL, |
| 208 base::StringPrintf("http://%s", kGalleryDomain)); |
| 209 } |
| 210 |
| 211 void WebstoreBundleTest::SetUpInProcessBrowserTestFixture() { |
| 212 host_resolver()->AddRule(kGalleryDomain, "127.0.0.1"); |
| 213 } |
| 214 |
| 215 GURL WebstoreBundleTest::GetTestServerURL(const std::string& domain, |
| 216 const std::string& filename) { |
| 217 GURL page_url = test_server()->GetURL( |
| 218 "files/extensions/api_test/webstore_private/" + filename); |
| 219 |
| 220 GURL::Replacements replace_host; |
| 221 replace_host.SetHostStr(domain); |
| 222 return page_url.ReplaceComponents(replace_host); |
| 223 } |
| 224 |
| 225 void AddItemsToItemList(WebstoreBundle::ItemList* item_list, |
| 226 const TestItem items[], |
| 227 size_t item_count) { |
| 228 for (size_t i = 0; i < item_count; ++i) |
| 229 item_list->push_back(new WebstoreBundle::Item( |
| 230 items[i].id, items[i].manifest, items[i].localized_name)); |
| 231 } |
| 232 |
| 233 } // namespace |
| 234 |
| 235 // Tests the state transitions and delegate callbacks when the user denies |
| 236 // the confirmation prompt. |
| 237 IN_PROC_BROWSER_TEST_F(WebstoreBundleTest, ParseAndCancel) { |
| 238 WebstoreBundle::SetAutoApproveForTesting(false); |
| 239 |
| 240 Profile* profile = browser()->profile(); |
| 241 |
| 242 WebstoreBundle::ItemList items; |
| 243 AddItemsToItemList(&items, kBundleItems, ARRAYSIZE_UNSAFE(kBundleItems)); |
| 244 AddItemsToItemList(&items, kBadItems, ARRAYSIZE_UNSAFE(kBadItems)); |
| 245 |
| 246 scoped_refptr<WebstoreBundle> bundle = new WebstoreBundle(profile, &items); |
| 247 scoped_ptr<WebstoreBundleListener> listener(new WebstoreBundleListener()); |
| 248 |
| 249 // All should start in STATE_INITIAL; |
| 250 EXPECT_EQ(4u, bundle->GetItemsInState(STATE_INITIAL, MATCH_ALL).size()); |
| 251 |
| 252 bundle->PromptForApproval(listener.get()); |
| 253 listener->Wait(); |
| 254 |
| 255 EXPECT_TRUE(listener->canceled()); |
| 256 EXPECT_FALSE(listener->approved()); |
| 257 |
| 258 // All the extensions should be in STATE_NOT_INSTALLED now. |
| 259 EXPECT_EQ(4u, bundle->GetItemsInState(STATE_NOT_INSTALLED, MATCH_ALL).size()); |
| 260 } |
| 261 |
| 262 // Tests the regular flow for approving and installing a bundle. |
| 263 IN_PROC_BROWSER_TEST_F(WebstoreBundleTest, InstallSuccess) { |
| 264 WebstoreBundle::SetAutoApproveForTesting(true); |
| 265 |
| 266 Profile* profile = browser()->profile(); |
| 267 ExtensionService* service = profile->GetExtensionService(); |
| 268 |
| 269 WebstoreBundle::ItemList items; |
| 270 AddItemsToItemList(&items, kBundleItems, ARRAYSIZE_UNSAFE(kBundleItems)); |
| 271 |
| 272 scoped_refptr<WebstoreBundle> bundle = new WebstoreBundle(profile, &items); |
| 273 scoped_ptr<WebstoreBundleListener> listener(new WebstoreBundleListener()); |
| 274 bundle->PromptForApproval(listener.get()); |
| 275 listener->Wait(); |
| 276 |
| 277 EXPECT_TRUE(listener->approved()); |
| 278 EXPECT_FALSE(listener->canceled()); |
| 279 |
| 280 std::vector<const WebstoreBundle::Item*> approved_items = |
| 281 bundle->GetItemsInState(STATE_APPROVED, MATCH_ALL); |
| 282 |
| 283 // All members should have been parsed successfully. |
| 284 EXPECT_EQ(3u, approved_items.size()); |
| 285 |
| 286 // The items should have had there manifests parsed. |
| 287 for (size_t i = 0; i < approved_items.size(); ++i) |
| 288 EXPECT_TRUE(approved_items[i]->dummy_extension().get()); |
| 289 |
| 290 // No extensions should have failed. |
| 291 EXPECT_EQ(0u, bundle->GetItemsInState(STATE_NOT_INSTALLED, MATCH_ALL).size()); |
| 292 |
| 293 // Two extensions and one app were passed in. |
| 294 EXPECT_EQ(1u, bundle->GetItemsInState(STATE_APPROVED, MATCH_APPS).size()); |
| 295 EXPECT_EQ(2u, bundle->GetItemsInState( |
| 296 STATE_APPROVED, MATCH_EXTENSIONS).size()); |
| 297 |
| 298 EXPECT_TRUE(bundle->HasExtensionsInState(STATE_APPROVED)); |
| 299 EXPECT_TRUE(bundle->HasAppsInState(STATE_APPROVED)); |
| 300 |
| 301 listener->ResetResults(); |
| 302 |
| 303 // This page doesn't exist, but it doesn't matter. |
| 304 ui_test_utils::NavigateToURL(browser(), GetTestServerURL(kGalleryDomain, "")); |
| 305 TabStripModel* tabstrip = browser()->tabstrip_model(); |
| 306 |
| 307 // Install the bundle. |
| 308 bundle->CompleteInstall(&tabstrip->GetTabContentsAt(0)->controller(), |
| 309 listener.get()); |
| 310 |
| 311 listener->Wait(); |
| 312 |
| 313 ASSERT_TRUE(listener->completed()); |
| 314 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kBundleItems); ++i) |
| 315 EXPECT_TRUE(service->GetExtensionById(kBundleItems[i].id, false)); |
| 316 |
| 317 // 2 apps and 1 extension should have been installed. |
| 318 EXPECT_EQ(3u, bundle->GetItemsInState(STATE_INSTALLED, MATCH_ALL).size()); |
| 319 EXPECT_EQ(1u, bundle->GetItemsInState(STATE_INSTALLED, MATCH_APPS).size()); |
| 320 EXPECT_EQ(2u, bundle->GetItemsInState( |
| 321 STATE_INSTALLED, MATCH_EXTENSIONS).size()); |
| 322 |
| 323 // The failed heading should be empty since all succeeded. |
| 324 EXPECT_TRUE(bundle->GetFailedHeadingTextForBubble().empty()); |
| 325 EXPECT_TRUE(!bundle->GetInstalledHeadingTextForBubble().empty()); |
| 326 } |
| 327 |
| 328 // Tests that the WebstoreBundle handles crx files that fail to install. |
| 329 IN_PROC_BROWSER_TEST_F(WebstoreBundleTest, InstallSuccessAndFailure) { |
| 330 WebstoreBundle::SetAutoApproveForTesting(true); |
| 331 |
| 332 Profile* profile = browser()->profile(); |
| 333 ExtensionService* service = profile->GetExtensionService(); |
| 334 |
| 335 WebstoreBundle::ItemList items; |
| 336 // Install 2 extensions and 1 app. |
| 337 AddItemsToItemList(&items, kBundleItems, ARRAYSIZE_UNSAFE(kBundleItems)); |
| 338 // Fail to parse 1 extension manifest. |
| 339 AddItemsToItemList(&items, kBadItems, ARRAYSIZE_UNSAFE(kBadItems)); |
| 340 // Fail to install 1 extension. |
| 341 AddItemsToItemList( |
| 342 &items, kBadCrxItems, ARRAYSIZE_UNSAFE(kBadCrxItems)); |
| 343 |
| 344 scoped_refptr<WebstoreBundle> bundle = new WebstoreBundle(profile, &items); |
| 345 scoped_ptr<WebstoreBundleListener> listener(new WebstoreBundleListener()); |
| 346 bundle->PromptForApproval(listener.get()); |
| 347 listener->Wait(); |
| 348 |
| 349 EXPECT_TRUE(listener->approved()); |
| 350 EXPECT_FALSE(listener->canceled()); |
| 351 |
| 352 std::vector<const WebstoreBundle::Item*> approved_items = |
| 353 bundle->GetItemsInState(STATE_APPROVED, MATCH_ALL); |
| 354 |
| 355 // All items except the one with the bad manifest should have been parsed. |
| 356 EXPECT_EQ(4u, approved_items.size()); |
| 357 for (size_t i = 0; i < approved_items.size(); ++i) |
| 358 EXPECT_TRUE(approved_items[i]->dummy_extension().get()); |
| 359 |
| 360 // While the one with the syntax error should have gone to |
| 361 // STATE_NOT_INSTALLED. |
| 362 EXPECT_EQ(1u, bundle->GetItemsInState(STATE_NOT_INSTALLED, MATCH_ALL).size()); |
| 363 |
| 364 // Three extensions and one app should have been parsed successfully. |
| 365 EXPECT_EQ(1u, bundle->GetItemsInState(STATE_APPROVED, MATCH_APPS).size()); |
| 366 EXPECT_EQ(3u, bundle->GetItemsInState( |
| 367 STATE_APPROVED, MATCH_EXTENSIONS).size()); |
| 368 |
| 369 EXPECT_TRUE(bundle->HasExtensionsInState(STATE_APPROVED)); |
| 370 EXPECT_TRUE(bundle->HasAppsInState(STATE_APPROVED)); |
| 371 |
| 372 listener->ResetResults(); |
| 373 |
| 374 // This page doesn't exist, but it doesn't matter. |
| 375 ui_test_utils::NavigateToURL(browser(), GetTestServerURL(kGalleryDomain, "")); |
| 376 TabStripModel* tabstrip = browser()->tabstrip_model(); |
| 377 |
| 378 // Install the bundle. |
| 379 bundle->CompleteInstall(&tabstrip->GetTabContentsAt(0)->controller(), |
| 380 listener.get()); |
| 381 |
| 382 listener->Wait(); |
| 383 |
| 384 ASSERT_TRUE(listener->completed()); |
| 385 |
| 386 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kBundleItems); ++i) |
| 387 EXPECT_TRUE(service->GetExtensionById(kBundleItems[i].id, false)); |
| 388 |
| 389 // Two extensions and one app should have been installed. |
| 390 EXPECT_EQ(3u, bundle->GetItemsInState(STATE_INSTALLED, MATCH_ALL).size()); |
| 391 EXPECT_EQ(1u, bundle->GetItemsInState(STATE_INSTALLED, MATCH_APPS).size()); |
| 392 EXPECT_EQ(2u, bundle->GetItemsInState( |
| 393 STATE_INSTALLED, MATCH_EXTENSIONS).size()); |
| 394 |
| 395 // Two extensions failed to be installed -- one at the manifest parse state |
| 396 // and one while unpacking the crx. |
| 397 EXPECT_EQ(2u, bundle->GetItemsInState(STATE_NOT_INSTALLED, MATCH_ALL).size()); |
| 398 |
| 399 EXPECT_TRUE(!bundle->GetFailedHeadingTextForBubble().empty()); |
| 400 EXPECT_TRUE(!bundle->GetInstalledHeadingTextForBubble().empty()); |
| 401 } |
| 402 |
| 403 /* The WebstoreInstaller can't handle 404 errors right now. |
| 404 IN_PROC_BROWSER_TEST_F(WebstoreBundleTest, FAILS_InstallSuccessAnd404Failure) { |
| 405 WebstoreBundle::SetAutoApproveForTesting(true); |
| 406 |
| 407 Profile* profile = browser()->profile(); |
| 408 ExtensionService* service = profile->GetExtensionService(); |
| 409 |
| 410 WebstoreBundle::ItemList items; |
| 411 // kBundleItems will install 2 extensions and 1 app. |
| 412 AddItemsToItemList(&items, kBundleItems, ARRAYSIZE_UNSAFE(kBundleItems)); |
| 413 |
| 414 // kBadItems will fail parsing of 1 extension. |
| 415 AddItemsToItemList(&items, kBadItems, ARRAYSIZE_UNSAFE(kBadItems)); |
| 416 |
| 417 // k404BundleItems will 404 when installing 1 extension. |
| 418 AddItemsToItemList( |
| 419 &items, k404BundleItems, ARRAYSIZE_UNSAFE(k404BundleItems)); |
| 420 |
| 421 scoped_refptr<WebstoreBundle> bundle = new WebstoreBundle(profile, &items); |
| 422 scoped_ptr<WebstoreBundleListener> listener(new WebstoreBundleListener()); |
| 423 bundle->PromptForApproval(listener.get()); |
| 424 listener->Wait(); |
| 425 |
| 426 EXPECT_TRUE(listener->approved()); |
| 427 EXPECT_FALSE(listener->canceled()); |
| 428 |
| 429 std::vector<const WebstoreBundle::Item*> approved_items = |
| 430 bundle->GetItemsInState(STATE_APPROVED, MATCH_ALL); |
| 431 |
| 432 // All members should have been parsed successfully. |
| 433 EXPECT_EQ(4u, approved_items.size()); |
| 434 |
| 435 // The items should have had there manifests parsed. |
| 436 for (size_t i = 0; i < approved_items.size(); ++i) |
| 437 EXPECT_TRUE(approved_items[i]->dummy_extension().get()); |
| 438 |
| 439 // While the one with the syntax error should have gone to |
| 440 // STATE_NOT_INSTALLED. |
| 441 EXPECT_EQ(1u, bundle->GetItemsInState(STATE_NOT_INSTALLED, MATCH_ALL).size()); |
| 442 |
| 443 // Three extensions and one app should have been parsed successfully. |
| 444 EXPECT_EQ(1u, bundle->GetItemsInState(STATE_APPROVED, MATCH_APPS).size()); |
| 445 EXPECT_EQ(3u, bundle->GetItemsInState( |
| 446 STATE_APPROVED, MATCH_EXTENSIONS).size()); |
| 447 |
| 448 EXPECT_TRUE(bundle->HasExtensionsInState(STATE_APPROVED)); |
| 449 EXPECT_TRUE(bundle->HasAppsInState(STATE_APPROVED)); |
| 450 |
| 451 listener->ResetResults(); |
| 452 |
| 453 // This page doesn't exist, but it doesn't matter. |
| 454 ui_test_utils::NavigateToURL(browser(), GetTestServerURL(kGalleryDomain, "")); |
| 455 TabStripModel* tabstrip = browser()->tabstrip_model(); |
| 456 |
| 457 // Install the bundle. |
| 458 bundle->CompleteInstall(&tabstrip->GetTabContentsAt(0)->controller(), |
| 459 listener.get()); |
| 460 |
| 461 listener->Wait(); |
| 462 |
| 463 ASSERT_TRUE(listener->completed()); |
| 464 |
| 465 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kBundleItems); ++i) |
| 466 EXPECT_TRUE(service->GetExtensionById(kBundleItems[i].id, false)); |
| 467 |
| 468 EXPECT_EQ(3u, bundle->GetItemsInState(STATE_INSTALLED, MATCH_ALL).size()); |
| 469 EXPECT_EQ(1u, bundle->GetItemsInState(STATE_INSTALLED, MATCH_APPS).size()); |
| 470 EXPECT_EQ(2u, bundle->GetItemsInState( |
| 471 STATE_INSTALLED, MATCH_EXTENSIONS).size()); |
| 472 EXPECT_EQ(2u, bundle->GetItemsInState(STATE_NOT_INSTALLED, MATCH_ALL).size()); |
| 473 |
| 474 EXPECT_TRUE(!bundle->GetFailedHeadingTextForBubble().empty()); |
| 475 EXPECT_TRUE(!bundle->GetInstalledHeadingTextForBubble().empty()); |
| 476 } |
| 477 */ |
OLD | NEW |