text: // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "base/bind.h"
  #include "base/utf_string_conversions.h"
  #include "chrome/browser/speech/speech_recognition_bubble_controller.h"
  #include "chrome/browser/ui/browser.h"
  #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
  #include "chrome/test/base/browser_with_test_window_test.h"
  #include "chrome/test/base/testing_profile.h"
  #include "content/test/test_browser_thread.h"
  #include "testing/gtest/include/gtest/gtest.h"
  #include "ui/gfx/rect.h"
  
  using content::BrowserThread;
  using content::WebContents;
  
  class SkBitmap;
  
  namespace speech {
  
  // A mock bubble class which fakes a focus change or recognition cancel by the
  // user and closing of the info bubble.
  class MockSpeechRecognitionBubble : public SpeechRecognitionBubbleBase {
   public:
    enum BubbleType {
      BUBBLE_TEST_FOCUS_CHANGED,
      BUBBLE_TEST_CLICK_CANCEL,
      BUBBLE_TEST_CLICK_TRY_AGAIN,
    };
  
    MockSpeechRecognitionBubble(WebContents* web_contents,
                          Delegate* delegate,
                          const gfx::Rect&)
        : SpeechRecognitionBubbleBase(web_contents) {
      VLOG(1) << "MockSpeechRecognitionBubble created";
      MessageLoop::current()->PostTask(
          FROM_HERE, base::Bind(&InvokeDelegate, delegate));
    }
  
    static void InvokeDelegate(Delegate* delegate) {
      VLOG(1) << "MockSpeechRecognitionBubble invoking delegate for type "
              << type_;
      switch (type_) {
        case BUBBLE_TEST_FOCUS_CHANGED:
          delegate->InfoBubbleFocusChanged();
          break;
        case BUBBLE_TEST_CLICK_CANCEL:
          delegate->InfoBubbleButtonClicked(
              SpeechRecognitionBubble::BUTTON_CANCEL);
          break;
        case BUBBLE_TEST_CLICK_TRY_AGAIN:
          delegate->InfoBubbleButtonClicked(
              SpeechRecognitionBubble::BUTTON_TRY_AGAIN);
          break;
      }
    }
  
    static void set_type(BubbleType type) {
      type_ = type;
    }
    static BubbleType type() {
      return type_;
    }
  
    virtual void Show() {}
    virtual void Hide() {}
    virtual void UpdateLayout() {}
    virtual void UpdateImage() {}
  
   private:
    static BubbleType type_;
  };
  
  // The test fixture.
  class SpeechRecognitionBubbleControllerTest
      : public SpeechRecognitionBubbleControllerDelegate,
        public BrowserWithTestWindowTest {
   public:
    SpeechRecognitionBubbleControllerTest()
        : BrowserWithTestWindowTest(),
          io_thread_(BrowserThread::IO),  // constructs a new thread and loop
          cancel_clicked_(false),
          try_again_clicked_(false),
          focus_changed_(false),
          controller_(ALLOW_THIS_IN_INITIALIZER_LIST(
              new SpeechRecognitionBubbleController(this))) {
      EXPECT_EQ(NULL, test_fixture_);
      test_fixture_ = this;
    }
  
    ~SpeechRecognitionBubbleControllerTest() {
      test_fixture_ = NULL;
    }
  
    // SpeechRecognitionBubbleControllerDelegate methods.
    virtual void InfoBubbleButtonClicked(int session_id,
                                         SpeechRecognitionBubble::Button button) {
      VLOG(1) << "Received InfoBubbleButtonClicked for button " << button;
      EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
      if (button == SpeechRecognitionBubble::BUTTON_CANCEL) {
        cancel_clicked_ = true;
      } else if (button == SpeechRecognitionBubble::BUTTON_TRY_AGAIN) {
        try_again_clicked_ = true;
      }
      message_loop()->PostTask(FROM_HERE, MessageLoop::QuitClosure());
    }
  
    virtual void InfoBubbleFocusChanged(int session_id) {
      VLOG(1) << "Received InfoBubbleFocusChanged";
      EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
      focus_changed_ = true;
      message_loop()->PostTask(FROM_HERE, MessageLoop::QuitClosure());
    }
  
    // testing::Test methods.
    virtual void SetUp() {
      BrowserWithTestWindowTest::SetUp();
      SpeechRecognitionBubble::set_factory(
          &SpeechRecognitionBubbleControllerTest::CreateBubble);
      io_thread_.Start();
    }
  
    virtual void TearDown() {
      SpeechRecognitionBubble::set_factory(NULL);
      io_thread_.Stop();
      BrowserWithTestWindowTest::TearDown();
    }
  
    static void ActivateBubble() {
      if (MockSpeechRecognitionBubble::type() ==
          MockSpeechRecognitionBubble::BUBBLE_TEST_FOCUS_CHANGED) {
        test_fixture_->controller_->SetBubbleWarmUpMode(kBubbleSessionId);
      } else {
        test_fixture_->controller_->SetBubbleMessage(kBubbleSessionId,
                                                     ASCIIToUTF16("Test"));
      }
    }
  
    static SpeechRecognitionBubble* CreateBubble(
        WebContents* web_contents,
        SpeechRecognitionBubble::Delegate* delegate,
        const gfx::Rect& element_rect) {
      EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI));
      // Set up to activate the bubble soon after it gets created, since we test
      // events sent by the bubble and those are handled only when the bubble is
      // active.
      MessageLoop::current()->PostTask(FROM_HERE,
                                       base::Bind(&ActivateBubble));
  
      // The |tab_contents| parameter would be NULL since the dummy session id
      // passed to CreateBubble would not have matched any active tab. So get a
      // real WebContents pointer from the test fixture and pass that, because
      // the bubble controller registers for tab close notifications which need
      // a valid WebContents.
      TabContentsWrapper* wrapper =
          test_fixture_->browser()->GetSelectedTabContentsWrapper();
      if (wrapper)
        web_contents = wrapper->web_contents();
      return new MockSpeechRecognitionBubble(web_contents, delegate,
                                             element_rect);
    }
  
   protected:
    // The main thread of the test is marked as the IO thread and we create a new
    // one for the UI thread.
    content::TestBrowserThread io_thread_;
    bool cancel_clicked_;
    bool try_again_clicked_;
    bool focus_changed_;
    scoped_refptr<SpeechRecognitionBubbleController> controller_;
  
    static const int kBubbleSessionId;
    static SpeechRecognitionBubbleControllerTest* test_fixture_;
  };
  
  SpeechRecognitionBubbleControllerTest*
  SpeechRecognitionBubbleControllerTest::test_fixture_ = NULL;
  
  const int SpeechRecognitionBubbleControllerTest::kBubbleSessionId = 1;
  
  MockSpeechRecognitionBubble::BubbleType MockSpeechRecognitionBubble::type_ =
      MockSpeechRecognitionBubble::BUBBLE_TEST_FOCUS_CHANGED;
  
  // Test that the speech bubble UI gets created in the UI thread and that the
  // focus changed callback comes back in the IO thread.
  TEST_F(SpeechRecognitionBubbleControllerTest, TestFocusChanged) {
    MockSpeechRecognitionBubble::set_type(
        MockSpeechRecognitionBubble::BUBBLE_TEST_FOCUS_CHANGED);
  
    controller_->CreateBubble(kBubbleSessionId, 1, 1, gfx::Rect(1, 1));
    MessageLoop::current()->Run();
    EXPECT_TRUE(focus_changed_);
    EXPECT_FALSE(cancel_clicked_);
    EXPECT_FALSE(try_again_clicked_);
    controller_->CloseBubble(kBubbleSessionId);
  }
  
  // Test that the speech bubble UI gets created in the UI thread and that the
  // recognition cancelled callback comes back in the IO thread.
  TEST_F(SpeechRecognitionBubbleControllerTest, TestRecognitionCancelled) {
    MockSpeechRecognitionBubble::set_type(
        MockSpeechRecognitionBubble::BUBBLE_TEST_CLICK_CANCEL);
  
    controller_->CreateBubble(kBubbleSessionId, 1, 1, gfx::Rect(1, 1));
    MessageLoop::current()->Run();
    EXPECT_TRUE(cancel_clicked_);
    EXPECT_FALSE(try_again_clicked_);
    EXPECT_FALSE(focus_changed_);
    controller_->CloseBubble(kBubbleSessionId);
  }
  
  // Test that the speech bubble UI gets created in the UI thread and that the
  // try-again button click event comes back in the IO thread.
  TEST_F(SpeechRecognitionBubbleControllerTest, TestTryAgainClicked) {
    MockSpeechRecognitionBubble::set_type(
        MockSpeechRecognitionBubble::BUBBLE_TEST_CLICK_TRY_AGAIN);
  
    controller_->CreateBubble(kBubbleSessionId, 1, 1, gfx::Rect(1, 1));
    MessageLoop::current()->Run();
    EXPECT_FALSE(cancel_clicked_);
    EXPECT_TRUE(try_again_clicked_);
    EXPECT_FALSE(focus_changed_);
    controller_->CloseBubble(kBubbleSessionId);
  }
  
  }  // namespace speech
  