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 "base/bind.h" 6 #include "base/run_loop.h" 7 #include "base/strings/utf_string_conversions.h" 8 #include "chrome/browser/speech/speech_recognition_bubble_controller.h" 9 #include "chrome/browser/ui/browser.h" 10 #include "chrome/browser/ui/browser_tabstrip.h" 11 #include "chrome/browser/ui/tabs/tab_strip_model.h" 12 #include "chrome/test/base/browser_with_test_window_test.h" 13 #include "chrome/test/base/testing_profile.h" 14 #include "content/public/test/test_browser_thread.h" 15 #include "testing/gtest/include/gtest/gtest.h" 16 #include "ui/gfx/rect.h" 17 18 using content::BrowserThread; 19 using content::WebContents; 20 21 namespace speech { 22 23 // A mock bubble class which fakes a focus change or recognition cancel by the 24 // user and closing of the info bubble. 25 class MockSpeechRecognitionBubble : public SpeechRecognitionBubbleBase { 26 public: 27 enum BubbleType { 28 BUBBLE_TEST_FOCUS_CHANGED, 29 BUBBLE_TEST_CLICK_CANCEL, 30 BUBBLE_TEST_CLICK_TRY_AGAIN, 31 }; 32 33 MockSpeechRecognitionBubble(WebContents* web_contents, 34 Delegate* delegate, 35 const gfx::Rect&) 36 : SpeechRecognitionBubbleBase(web_contents) { 37 VLOG(1) << "MockSpeechRecognitionBubble created"; 38 base::MessageLoop::current()->PostTask( 39 FROM_HERE, base::Bind(&InvokeDelegate, delegate)); 40 } 41 42 static void InvokeDelegate(Delegate* delegate) { 43 VLOG(1) << "MockSpeechRecognitionBubble invoking delegate for type " 44 << type_; 45 switch (type_) { 46 case BUBBLE_TEST_FOCUS_CHANGED: 47 delegate->InfoBubbleFocusChanged(); 48 break; 49 case BUBBLE_TEST_CLICK_CANCEL: 50 delegate->InfoBubbleButtonClicked( 51 SpeechRecognitionBubble::BUTTON_CANCEL); 52 break; 53 case BUBBLE_TEST_CLICK_TRY_AGAIN: 54 delegate->InfoBubbleButtonClicked( 55 SpeechRecognitionBubble::BUTTON_TRY_AGAIN); 56 break; 57 } 58 } 59 60 static void set_type(BubbleType type) { 61 type_ = type; 62 } 63 static BubbleType type() { 64 return type_; 65 } 66 67 virtual void Show() OVERRIDE {} 68 virtual void Hide() OVERRIDE {} 69 virtual void UpdateLayout() OVERRIDE {} 70 virtual void UpdateImage() OVERRIDE {} 71 72 private: 73 static BubbleType type_; 74 }; 75 76 // The test fixture. 77 class SpeechRecognitionBubbleControllerTest 78 : public SpeechRecognitionBubbleControllerDelegate, 79 public BrowserWithTestWindowTest { 80 public: 81 SpeechRecognitionBubbleControllerTest() 82 : BrowserWithTestWindowTest(), 83 cancel_clicked_(false), 84 try_again_clicked_(false), 85 focus_changed_(false), 86 controller_(new SpeechRecognitionBubbleController(this)) { 87 EXPECT_EQ(NULL, test_fixture_); 88 test_fixture_ = this; 89 } 90 91 virtual ~SpeechRecognitionBubbleControllerTest() { 92 test_fixture_ = NULL; 93 } 94 95 // SpeechRecognitionBubbleControllerDelegate methods. 96 virtual void InfoBubbleButtonClicked( 97 int session_id, 98 SpeechRecognitionBubble::Button button) OVERRIDE { 99 VLOG(1) << "Received InfoBubbleButtonClicked for button " << button; 100 EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO)); 101 if (button == SpeechRecognitionBubble::BUTTON_CANCEL) { 102 cancel_clicked_ = true; 103 } else if (button == SpeechRecognitionBubble::BUTTON_TRY_AGAIN) { 104 try_again_clicked_ = true; 105 } 106 } 107 108 virtual void InfoBubbleFocusChanged(int session_id) OVERRIDE { 109 VLOG(1) << "Received InfoBubbleFocusChanged"; 110 EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO)); 111 focus_changed_ = true; 112 } 113 114 // testing::Test methods. 115 virtual void SetUp() { 116 BrowserWithTestWindowTest::SetUp(); 117 SpeechRecognitionBubble::set_factory( 118 &SpeechRecognitionBubbleControllerTest::CreateBubble); 119 } 120 121 virtual void TearDown() { 122 SpeechRecognitionBubble::set_factory(NULL); 123 BrowserWithTestWindowTest::TearDown(); 124 } 125 126 static void ActivateBubble() { 127 if (MockSpeechRecognitionBubble::type() != 128 MockSpeechRecognitionBubble::BUBBLE_TEST_FOCUS_CHANGED) { 129 test_fixture_->controller_->SetBubbleMessage(ASCIIToUTF16("Test")); 130 } 131 } 132 133 static SpeechRecognitionBubble* CreateBubble( 134 WebContents* web_contents, 135 SpeechRecognitionBubble::Delegate* delegate, 136 const gfx::Rect& element_rect) { 137 EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI)); 138 // Set up to activate the bubble soon after it gets created, since we test 139 // events sent by the bubble and those are handled only when the bubble is 140 // active. 141 base::MessageLoop::current()->PostTask(FROM_HERE, 142 base::Bind(&ActivateBubble)); 143 144 // The |web_contents| parameter would be NULL since the dummy session id 145 // passed to CreateBubble would not have matched any active tab. So get a 146 // real WebContents pointer from the test fixture and pass that, because 147 // the bubble controller registers for tab close notifications which need 148 // a valid WebContents. 149 web_contents = 150 test_fixture_->browser()->tab_strip_model()->GetActiveWebContents(); 151 return new MockSpeechRecognitionBubble(web_contents, delegate, 152 element_rect); 153 } 154 155 protected: 156 bool cancel_clicked_; 157 bool try_again_clicked_; 158 bool focus_changed_; 159 scoped_refptr<SpeechRecognitionBubbleController> controller_; 160 161 static const int kBubbleSessionId; 162 static SpeechRecognitionBubbleControllerTest* test_fixture_; 163 }; 164 165 SpeechRecognitionBubbleControllerTest* 166 SpeechRecognitionBubbleControllerTest::test_fixture_ = NULL; 167 168 const int SpeechRecognitionBubbleControllerTest::kBubbleSessionId = 1; 169 170 MockSpeechRecognitionBubble::BubbleType MockSpeechRecognitionBubble::type_ = 171 MockSpeechRecognitionBubble::BUBBLE_TEST_FOCUS_CHANGED; 172 173 // Test that the speech bubble UI gets created in the UI thread and that the 174 // focus changed callback comes back in the IO thread. 175 TEST_F(SpeechRecognitionBubbleControllerTest, TestFocusChanged) { 176 MockSpeechRecognitionBubble::set_type( 177 MockSpeechRecognitionBubble::BUBBLE_TEST_FOCUS_CHANGED); 178 179 controller_->CreateBubble(kBubbleSessionId, 1, 1, gfx::Rect(1, 1)); 180 base::RunLoop().RunUntilIdle(); 181 EXPECT_TRUE(focus_changed_); 182 EXPECT_FALSE(cancel_clicked_); 183 EXPECT_FALSE(try_again_clicked_); 184 controller_->CloseBubble(); 185 } 186 187 // Test that the speech bubble UI gets created in the UI thread and that the 188 // recognition cancelled callback comes back in the IO thread. 189 TEST_F(SpeechRecognitionBubbleControllerTest, TestRecognitionCancelled) { 190 MockSpeechRecognitionBubble::set_type( 191 MockSpeechRecognitionBubble::BUBBLE_TEST_CLICK_CANCEL); 192 193 controller_->CreateBubble(kBubbleSessionId, 1, 1, gfx::Rect(1, 1)); 194 base::RunLoop().RunUntilIdle(); 195 EXPECT_TRUE(cancel_clicked_); 196 EXPECT_FALSE(try_again_clicked_); 197 EXPECT_FALSE(focus_changed_); 198 controller_->CloseBubble(); 199 } 200 201 // Test that the speech bubble UI gets created in the UI thread and that the 202 // try-again button click event comes back in the IO thread. 203 TEST_F(SpeechRecognitionBubbleControllerTest, TestTryAgainClicked) { 204 MockSpeechRecognitionBubble::set_type( 205 MockSpeechRecognitionBubble::BUBBLE_TEST_CLICK_TRY_AGAIN); 206 207 controller_->CreateBubble(kBubbleSessionId, 1, 1, gfx::Rect(1, 1)); 208 base::RunLoop().RunUntilIdle(); 209 EXPECT_FALSE(cancel_clicked_); 210 EXPECT_TRUE(try_again_clicked_); 211 EXPECT_FALSE(focus_changed_); 212 controller_->CloseBubble(); 213 } 214 215 } // namespace speech 216