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