Adding instrumentation to locate the source of jankiness
[chromium-blink-merge.git] / chrome / browser / extensions / extension_crash_recovery_browsertest.cc
blob1b09d2a310a41897a6a53fb0211cd8714a68b8f5
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.
5 #include "chrome/browser/browser_process.h"
6 #include "chrome/browser/extensions/extension_browsertest.h"
7 #include "chrome/browser/extensions/extension_service.h"
8 #include "chrome/browser/notifications/notification.h"
9 #include "chrome/browser/notifications/notification_delegate.h"
10 #include "chrome/browser/notifications/notification_ui_manager.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/browser/ui/browser.h"
13 #include "chrome/browser/ui/browser_commands.h"
14 #include "chrome/browser/ui/tabs/tab_strip_model.h"
15 #include "chrome/test/base/ui_test_utils.h"
16 #include "content/public/browser/navigation_controller.h"
17 #include "content/public/browser/render_process_host.h"
18 #include "content/public/browser/render_view_host.h"
19 #include "content/public/browser/web_contents.h"
20 #include "content/public/common/result_codes.h"
21 #include "content/public/common/url_constants.h"
22 #include "extensions/browser/extension_host.h"
23 #include "extensions/browser/extension_registry.h"
24 #include "extensions/browser/extension_system.h"
25 #include "extensions/browser/process_manager.h"
26 #include "extensions/browser/process_map.h"
27 #include "extensions/common/constants.h"
28 #include "ui/message_center/message_center.h"
29 #include "ui/message_center/notification_list.h"
31 using content::NavigationController;
32 using content::WebContents;
33 using extensions::Extension;
34 using extensions::ExtensionRegistry;
36 // Tests are timing out waiting for extension to crash.
37 // http://crbug.com/174705
38 #if defined(OS_MACOSX) || defined(USE_AURA) || defined(OS_LINUX)
39 #define MAYBE_ExtensionCrashRecoveryTest DISABLED_ExtensionCrashRecoveryTest
40 #else
41 #define MAYBE_ExtensionCrashRecoveryTest ExtensionCrashRecoveryTest
42 #endif // defined(OS_MACOSX) || defined(USE_AURA) || defined(OS_LINUX)
44 class ExtensionCrashRecoveryTestBase : public ExtensionBrowserTest {
45 protected:
46 virtual void AcceptNotification(size_t index) = 0;
47 virtual void CancelNotification(size_t index) = 0;
48 virtual size_t CountBalloons() = 0;
50 ExtensionService* GetExtensionService() {
51 return extensions::ExtensionSystem::Get(browser()->profile())->
52 extension_service();
55 extensions::ProcessManager* GetProcessManager() {
56 return extensions::ExtensionSystem::Get(browser()->profile())->
57 process_manager();
60 ExtensionRegistry* GetExtensionRegistry() {
61 return ExtensionRegistry::Get(browser()->profile());
64 size_t GetEnabledExtensionCount() {
65 return GetExtensionRegistry()->enabled_extensions().size();
68 size_t GetTerminatedExtensionCount() {
69 return GetExtensionRegistry()->terminated_extensions().size();
72 void CrashExtension(const std::string& extension_id) {
73 const Extension* extension = GetExtensionRegistry()->GetExtensionById(
74 extension_id, ExtensionRegistry::ENABLED);
75 ASSERT_TRUE(extension);
76 extensions::ExtensionHost* extension_host = GetProcessManager()->
77 GetBackgroundHostForExtension(extension_id);
78 ASSERT_TRUE(extension_host);
80 base::KillProcess(extension_host->render_process_host()->GetHandle(),
81 content::RESULT_CODE_KILLED, false);
82 ASSERT_TRUE(WaitForExtensionCrash(extension_id));
83 ASSERT_FALSE(GetProcessManager()->
84 GetBackgroundHostForExtension(extension_id));
86 // Wait for extension crash balloon to appear.
87 base::MessageLoop::current()->RunUntilIdle();
90 void CheckExtensionConsistency(const std::string& extension_id) {
91 const Extension* extension = GetExtensionRegistry()->GetExtensionById(
92 extension_id, ExtensionRegistry::ENABLED);
93 ASSERT_TRUE(extension);
94 extensions::ExtensionHost* extension_host = GetProcessManager()->
95 GetBackgroundHostForExtension(extension_id);
96 ASSERT_TRUE(extension_host);
97 extensions::ProcessManager::ViewSet all_views =
98 GetProcessManager()->GetAllViews();
99 extensions::ProcessManager::ViewSet::const_iterator it =
100 all_views.find(extension_host->host_contents()->GetRenderViewHost());
101 ASSERT_FALSE(it == all_views.end());
102 ASSERT_TRUE(extension_host->IsRenderViewLive());
103 extensions::ProcessMap* process_map =
104 extensions::ProcessMap::Get(browser()->profile());
105 ASSERT_TRUE(process_map->Contains(
106 extension_id,
107 extension_host->render_view_host()->GetProcess()->GetID()));
110 void LoadTestExtension() {
111 ExtensionBrowserTest::SetUpInProcessBrowserTestFixture();
112 const Extension* extension = LoadExtension(
113 test_data_dir_.AppendASCII("common").AppendASCII("background_page"));
114 ASSERT_TRUE(extension);
115 first_extension_id_ = extension->id();
116 CheckExtensionConsistency(first_extension_id_);
119 void LoadSecondExtension() {
120 const Extension* extension = LoadExtension(
121 test_data_dir_.AppendASCII("install").AppendASCII("install"));
122 ASSERT_TRUE(extension);
123 second_extension_id_ = extension->id();
124 CheckExtensionConsistency(second_extension_id_);
127 std::string first_extension_id_;
128 std::string second_extension_id_;
131 class MAYBE_ExtensionCrashRecoveryTest : public ExtensionCrashRecoveryTestBase {
132 protected:
133 virtual void AcceptNotification(size_t index) override {
134 message_center::MessageCenter* message_center =
135 message_center::MessageCenter::Get();
136 ASSERT_GT(message_center->NotificationCount(), index);
137 message_center::NotificationList::Notifications::reverse_iterator it =
138 message_center->GetVisibleNotifications().rbegin();
139 for (size_t i = 0; i < index; ++i)
140 ++it;
141 std::string id = (*it)->id();
142 message_center->ClickOnNotification(id);
143 WaitForExtensionLoad();
146 virtual void CancelNotification(size_t index) override {
147 message_center::MessageCenter* message_center =
148 message_center::MessageCenter::Get();
149 ASSERT_GT(message_center->NotificationCount(), index);
150 message_center::NotificationList::Notifications::reverse_iterator it =
151 message_center->GetVisibleNotifications().rbegin();
152 for (size_t i = 0; i < index; ++i)
153 ++it;
154 ASSERT_TRUE(g_browser_process->notification_ui_manager()->CancelById(
155 (*it)->id(),
156 NotificationUIManager::GetProfileID(browser()->profile())));
159 virtual size_t CountBalloons() override {
160 return message_center::MessageCenter::Get()->NotificationCount();
164 // Flaky: http://crbug.com/242167.
165 IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest, DISABLED_Basic) {
166 const size_t count_before = GetEnabledExtensionCount();
167 const size_t crash_count_before = GetTerminatedExtensionCount();
168 LoadTestExtension();
169 CrashExtension(first_extension_id_);
170 ASSERT_EQ(count_before, GetEnabledExtensionCount());
171 ASSERT_EQ(crash_count_before + 1, GetTerminatedExtensionCount());
172 ASSERT_NO_FATAL_FAILURE(AcceptNotification(0));
174 SCOPED_TRACE("after clicking the balloon");
175 CheckExtensionConsistency(first_extension_id_);
176 ASSERT_EQ(crash_count_before, GetTerminatedExtensionCount());
179 // Flaky, http://crbug.com/241191.
180 IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
181 DISABLED_CloseAndReload) {
182 const size_t count_before = GetEnabledExtensionCount();
183 const size_t crash_count_before = GetTerminatedExtensionCount();
184 LoadTestExtension();
185 CrashExtension(first_extension_id_);
187 ASSERT_EQ(count_before, GetEnabledExtensionCount());
188 ASSERT_EQ(crash_count_before + 1, GetTerminatedExtensionCount());
190 ASSERT_NO_FATAL_FAILURE(CancelNotification(0));
191 ReloadExtension(first_extension_id_);
193 SCOPED_TRACE("after reloading");
194 CheckExtensionConsistency(first_extension_id_);
195 ASSERT_EQ(crash_count_before, GetTerminatedExtensionCount());
198 // Test is timing out on Windows http://crbug.com/174705.
199 #if defined(OS_WIN)
200 #define MAYBE_ReloadIndependently DISABLED_ReloadIndependently
201 #else
202 #define MAYBE_ReloadIndependently ReloadIndependently
203 #endif // defined(OS_WIN)
204 IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
205 MAYBE_ReloadIndependently) {
206 const size_t count_before = GetEnabledExtensionCount();
207 LoadTestExtension();
208 CrashExtension(first_extension_id_);
209 ASSERT_EQ(count_before, GetEnabledExtensionCount());
211 ReloadExtension(first_extension_id_);
213 SCOPED_TRACE("after reloading");
214 CheckExtensionConsistency(first_extension_id_);
216 WebContents* current_tab =
217 browser()->tab_strip_model()->GetActiveWebContents();
218 ASSERT_TRUE(current_tab);
220 // The balloon should automatically hide after the extension is successfully
221 // reloaded.
222 ASSERT_EQ(0U, CountBalloons());
225 // Test is timing out on Windows http://crbug.com/174705.
226 #if defined(OS_WIN)
227 #define MAYBE_ReloadIndependentlyChangeTabs DISABLED_ReloadIndependentlyChangeTabs
228 #else
229 #define MAYBE_ReloadIndependentlyChangeTabs ReloadIndependentlyChangeTabs
230 #endif // defined(OS_WIN)
232 IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
233 MAYBE_ReloadIndependentlyChangeTabs) {
234 const size_t count_before = GetEnabledExtensionCount();
235 LoadTestExtension();
236 CrashExtension(first_extension_id_);
237 ASSERT_EQ(count_before, GetEnabledExtensionCount());
239 WebContents* original_tab =
240 browser()->tab_strip_model()->GetActiveWebContents();
241 ASSERT_TRUE(original_tab);
242 ASSERT_EQ(1U, CountBalloons());
244 // Open a new tab, but the balloon will still be there.
245 chrome::NewTab(browser());
246 WebContents* new_current_tab =
247 browser()->tab_strip_model()->GetActiveWebContents();
248 ASSERT_TRUE(new_current_tab);
249 ASSERT_NE(new_current_tab, original_tab);
250 ASSERT_EQ(1U, CountBalloons());
252 ReloadExtension(first_extension_id_);
254 SCOPED_TRACE("after reloading");
255 CheckExtensionConsistency(first_extension_id_);
257 // The balloon should automatically hide after the extension is successfully
258 // reloaded.
259 ASSERT_EQ(0U, CountBalloons());
262 IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
263 DISABLED_ReloadIndependentlyNavigatePage) {
264 const size_t count_before = GetEnabledExtensionCount();
265 LoadTestExtension();
266 CrashExtension(first_extension_id_);
267 ASSERT_EQ(count_before, GetEnabledExtensionCount());
269 WebContents* current_tab =
270 browser()->tab_strip_model()->GetActiveWebContents();
271 ASSERT_TRUE(current_tab);
272 ASSERT_EQ(1U, CountBalloons());
274 // Navigate to another page.
275 ui_test_utils::NavigateToURL(
276 browser(), ui_test_utils::GetTestUrl(
277 base::FilePath(base::FilePath::kCurrentDirectory),
278 base::FilePath(FILE_PATH_LITERAL("title1.html"))));
279 ASSERT_EQ(1U, CountBalloons());
281 ReloadExtension(first_extension_id_);
283 SCOPED_TRACE("after reloading");
284 CheckExtensionConsistency(first_extension_id_);
286 // The balloon should automatically hide after the extension is successfully
287 // reloaded.
288 ASSERT_EQ(0U, CountBalloons());
291 // Make sure that when we don't do anything about the crashed extension
292 // and close the browser, it doesn't crash. The browser is closed implicitly
293 // at the end of each browser test.
295 // http://crbug.com/84719
296 #if defined(OS_LINUX)
297 #define MAYBE_ShutdownWhileCrashed DISABLED_ShutdownWhileCrashed
298 #else
299 #define MAYBE_ShutdownWhileCrashed ShutdownWhileCrashed
300 #endif // defined(OS_LINUX)
302 IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
303 MAYBE_ShutdownWhileCrashed) {
304 const size_t count_before = GetEnabledExtensionCount();
305 LoadTestExtension();
306 CrashExtension(first_extension_id_);
307 ASSERT_EQ(count_before, GetEnabledExtensionCount());
310 // Flaky, http://crbug.com/241245.
311 IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
312 DISABLED_TwoExtensionsCrashFirst) {
313 const size_t count_before = GetEnabledExtensionCount();
314 LoadTestExtension();
315 LoadSecondExtension();
316 CrashExtension(first_extension_id_);
317 ASSERT_EQ(count_before + 1, GetEnabledExtensionCount());
318 ASSERT_NO_FATAL_FAILURE(AcceptNotification(0));
320 SCOPED_TRACE("after clicking the balloon");
321 CheckExtensionConsistency(first_extension_id_);
322 CheckExtensionConsistency(second_extension_id_);
325 // Flaky: http://crbug.com/242196
326 IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
327 DISABLED_TwoExtensionsCrashSecond) {
328 const size_t count_before = GetEnabledExtensionCount();
329 LoadTestExtension();
330 LoadSecondExtension();
331 CrashExtension(second_extension_id_);
332 ASSERT_EQ(count_before + 1, GetEnabledExtensionCount());
333 ASSERT_NO_FATAL_FAILURE(AcceptNotification(0));
335 SCOPED_TRACE("after clicking the balloon");
336 CheckExtensionConsistency(first_extension_id_);
337 CheckExtensionConsistency(second_extension_id_);
340 IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
341 TwoExtensionsCrashBothAtOnce) {
342 const size_t count_before = GetEnabledExtensionCount();
343 const size_t crash_count_before = GetTerminatedExtensionCount();
344 LoadTestExtension();
345 LoadSecondExtension();
346 CrashExtension(first_extension_id_);
347 ASSERT_EQ(count_before + 1, GetEnabledExtensionCount());
348 ASSERT_EQ(crash_count_before + 1, GetTerminatedExtensionCount());
349 CrashExtension(second_extension_id_);
350 ASSERT_EQ(count_before, GetEnabledExtensionCount());
351 ASSERT_EQ(crash_count_before + 2, GetTerminatedExtensionCount());
354 SCOPED_TRACE("first balloon");
355 ASSERT_NO_FATAL_FAILURE(AcceptNotification(0));
356 CheckExtensionConsistency(first_extension_id_);
360 SCOPED_TRACE("second balloon");
361 ASSERT_NO_FATAL_FAILURE(AcceptNotification(0));
362 CheckExtensionConsistency(first_extension_id_);
363 CheckExtensionConsistency(second_extension_id_);
367 IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
368 TwoExtensionsOneByOne) {
369 const size_t count_before = GetEnabledExtensionCount();
370 LoadTestExtension();
371 CrashExtension(first_extension_id_);
372 ASSERT_EQ(count_before, GetEnabledExtensionCount());
373 LoadSecondExtension();
374 CrashExtension(second_extension_id_);
375 ASSERT_EQ(count_before, GetEnabledExtensionCount());
378 SCOPED_TRACE("first balloon");
379 ASSERT_NO_FATAL_FAILURE(AcceptNotification(0));
380 CheckExtensionConsistency(first_extension_id_);
384 SCOPED_TRACE("second balloon");
385 ASSERT_NO_FATAL_FAILURE(AcceptNotification(0));
386 CheckExtensionConsistency(first_extension_id_);
387 CheckExtensionConsistency(second_extension_id_);
391 // http://crbug.com/84719
392 #if defined(OS_LINUX)
393 #define MAYBE_TwoExtensionsShutdownWhileCrashed \
394 DISABLED_TwoExtensionsShutdownWhileCrashed
395 #else
396 #define MAYBE_TwoExtensionsShutdownWhileCrashed \
397 TwoExtensionsShutdownWhileCrashed
398 #endif // defined(OS_LINUX)
400 // Make sure that when we don't do anything about the crashed extensions
401 // and close the browser, it doesn't crash. The browser is closed implicitly
402 // at the end of each browser test.
403 IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
404 MAYBE_TwoExtensionsShutdownWhileCrashed) {
405 const size_t count_before = GetEnabledExtensionCount();
406 LoadTestExtension();
407 CrashExtension(first_extension_id_);
408 ASSERT_EQ(count_before, GetEnabledExtensionCount());
409 LoadSecondExtension();
410 CrashExtension(second_extension_id_);
411 ASSERT_EQ(count_before, GetEnabledExtensionCount());
414 // Flaky, http://crbug.com/241573.
415 IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
416 DISABLED_TwoExtensionsIgnoreFirst) {
417 const size_t count_before = GetEnabledExtensionCount();
418 LoadTestExtension();
419 LoadSecondExtension();
420 CrashExtension(first_extension_id_);
421 ASSERT_EQ(count_before + 1, GetEnabledExtensionCount());
422 CrashExtension(second_extension_id_);
423 ASSERT_EQ(count_before, GetEnabledExtensionCount());
425 // Accept notification 1 before canceling notification 0.
426 // Otherwise, on Linux and Windows, there is a race here, in which
427 // canceled notifications do not immediately go away.
428 ASSERT_NO_FATAL_FAILURE(AcceptNotification(1));
429 ASSERT_NO_FATAL_FAILURE(CancelNotification(0));
431 SCOPED_TRACE("balloons done");
432 ASSERT_EQ(count_before + 1, GetEnabledExtensionCount());
433 CheckExtensionConsistency(second_extension_id_);
436 // Flaky, http://crbug.com/241164.
437 IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
438 DISABLED_TwoExtensionsReloadIndependently) {
439 const size_t count_before = GetEnabledExtensionCount();
440 LoadTestExtension();
441 LoadSecondExtension();
442 CrashExtension(first_extension_id_);
443 ASSERT_EQ(count_before + 1, GetEnabledExtensionCount());
444 CrashExtension(second_extension_id_);
445 ASSERT_EQ(count_before, GetEnabledExtensionCount());
448 SCOPED_TRACE("first: reload");
449 WebContents* current_tab =
450 browser()->tab_strip_model()->GetActiveWebContents();
451 ASSERT_TRUE(current_tab);
452 // At the beginning we should have one balloon displayed for each extension.
453 ASSERT_EQ(2U, CountBalloons());
454 ReloadExtension(first_extension_id_);
455 // One of the balloons should hide after the extension is reloaded.
456 ASSERT_EQ(1U, CountBalloons());
457 CheckExtensionConsistency(first_extension_id_);
461 SCOPED_TRACE("second: balloon");
462 ASSERT_NO_FATAL_FAILURE(AcceptNotification(0));
463 CheckExtensionConsistency(first_extension_id_);
464 CheckExtensionConsistency(second_extension_id_);
468 // http://crbug.com/243648
469 #if defined(OS_WIN)
470 #define MAYBE_CrashAndUninstall DISABLED_CrashAndUninstall
471 #else
472 #define MAYBE_CrashAndUninstall CrashAndUninstall
473 #endif
474 IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
475 MAYBE_CrashAndUninstall) {
476 const size_t count_before = GetEnabledExtensionCount();
477 const size_t crash_count_before = GetTerminatedExtensionCount();
478 LoadTestExtension();
479 LoadSecondExtension();
480 CrashExtension(first_extension_id_);
481 ASSERT_EQ(count_before + 1, GetEnabledExtensionCount());
482 ASSERT_EQ(crash_count_before + 1, GetTerminatedExtensionCount());
484 ASSERT_EQ(1U, CountBalloons());
485 UninstallExtension(first_extension_id_);
486 base::MessageLoop::current()->RunUntilIdle();
488 SCOPED_TRACE("after uninstalling");
489 ASSERT_EQ(count_before + 1, GetEnabledExtensionCount());
490 ASSERT_EQ(crash_count_before, GetTerminatedExtensionCount());
491 ASSERT_EQ(0U, CountBalloons());
494 // http://crbug.com/84719
495 #if defined(OS_LINUX)
496 #define MAYBE_CrashAndUnloadAll DISABLED_CrashAndUnloadAll
497 #else
498 #define MAYBE_CrashAndUnloadAll CrashAndUnloadAll
499 #endif // defined(OS_LINUX)
501 IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
502 MAYBE_CrashAndUnloadAll) {
503 const size_t count_before = GetEnabledExtensionCount();
504 const size_t crash_count_before = GetTerminatedExtensionCount();
505 LoadTestExtension();
506 LoadSecondExtension();
507 CrashExtension(first_extension_id_);
508 ASSERT_EQ(count_before + 1, GetEnabledExtensionCount());
509 ASSERT_EQ(crash_count_before + 1, GetTerminatedExtensionCount());
511 GetExtensionService()->UnloadAllExtensionsForTest();
512 ASSERT_EQ(crash_count_before, GetTerminatedExtensionCount());
515 // Fails a DCHECK on Aura and Linux: http://crbug.com/169622
516 // Failing on Windows: http://crbug.com/232340
517 #if defined(USE_AURA)
518 #define MAYBE_ReloadTabsWithBackgroundPage DISABLED_ReloadTabsWithBackgroundPage
519 #else
520 #define MAYBE_ReloadTabsWithBackgroundPage ReloadTabsWithBackgroundPage
521 #endif
523 // Test that when an extension with a background page that has a tab open
524 // crashes, the tab stays open, and reloading it reloads the extension.
525 // Regression test for issue 71629.
526 IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
527 MAYBE_ReloadTabsWithBackgroundPage) {
528 TabStripModel* tab_strip = browser()->tab_strip_model();
529 const size_t count_before = GetEnabledExtensionCount();
530 const size_t crash_count_before = GetTerminatedExtensionCount();
531 LoadTestExtension();
533 // Open a tab extension.
534 chrome::NewTab(browser());
535 ui_test_utils::NavigateToURL(browser(),
536 GURL(std::string(extensions::kExtensionScheme) +
537 url::kStandardSchemeSeparator +
538 first_extension_id_ + "/background.html"));
540 const int tabs_before = tab_strip->count();
541 CrashExtension(first_extension_id_);
543 // Tab should still be open, and extension should be crashed.
544 EXPECT_EQ(tabs_before, tab_strip->count());
545 EXPECT_EQ(count_before, GetEnabledExtensionCount());
546 EXPECT_EQ(crash_count_before + 1, GetTerminatedExtensionCount());
549 content::WindowedNotificationObserver observer(
550 content::NOTIFICATION_LOAD_STOP,
551 content::Source<NavigationController>(
552 &browser()->tab_strip_model()->GetActiveWebContents()->
553 GetController()));
554 chrome::Reload(browser(), CURRENT_TAB);
555 observer.Wait();
557 // Extension should now be loaded.
558 SCOPED_TRACE("after reloading the tab");
559 CheckExtensionConsistency(first_extension_id_);
560 ASSERT_EQ(count_before + 1, GetEnabledExtensionCount());
561 ASSERT_EQ(0U, CountBalloons());