普通文本  |  216行  |  7.45 KB

// Copyright (c) 2011 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/utf_string_conversions.h"
#include "chrome/browser/speech/speech_input_bubble_controller.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/browser_with_test_window_test.h"
#include "chrome/test/testing_profile.h"
#include "content/browser/browser_thread.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/rect.h"

class SkBitmap;

namespace speech_input {

// A mock bubble class which fakes a focus change or recognition cancel by the
// user and closing of the info bubble.
class MockSpeechInputBubble : public SpeechInputBubbleBase {
 public:
  enum BubbleType {
    BUBBLE_TEST_FOCUS_CHANGED,
    BUBBLE_TEST_CLICK_CANCEL,
    BUBBLE_TEST_CLICK_TRY_AGAIN,
  };

  MockSpeechInputBubble(TabContents* tab_contents,
                        Delegate* delegate,
                        const gfx::Rect&)
      : SpeechInputBubbleBase(tab_contents) {
    VLOG(1) << "MockSpeechInputBubble created";
    MessageLoop::current()->PostTask(
        FROM_HERE, NewRunnableFunction(&InvokeDelegate, delegate));
  }

  static void InvokeDelegate(Delegate* delegate) {
    VLOG(1) << "MockSpeechInputBubble invoking delegate for type " << type_;
    switch (type_) {
      case BUBBLE_TEST_FOCUS_CHANGED:
        delegate->InfoBubbleFocusChanged();
        break;
      case BUBBLE_TEST_CLICK_CANCEL:
        delegate->InfoBubbleButtonClicked(SpeechInputBubble::BUTTON_CANCEL);
        break;
      case BUBBLE_TEST_CLICK_TRY_AGAIN:
        delegate->InfoBubbleButtonClicked(SpeechInputBubble::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 SpeechInputBubbleControllerTest
    : public SpeechInputBubbleControllerDelegate,
      public BrowserWithTestWindowTest {
 public:
  SpeechInputBubbleControllerTest()
      : 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 SpeechInputBubbleController(this))) {
    EXPECT_EQ(NULL, test_fixture_);
    test_fixture_ = this;
  }

  ~SpeechInputBubbleControllerTest() {
    test_fixture_ = NULL;
  }

  // SpeechInputBubbleControllerDelegate methods.
  virtual void InfoBubbleButtonClicked(int caller_id,
                                       SpeechInputBubble::Button button) {
    VLOG(1) << "Received InfoBubbleButtonClicked for button " << button;
    EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
    if (button == SpeechInputBubble::BUTTON_CANCEL) {
      cancel_clicked_ = true;
    } else if (button == SpeechInputBubble::BUTTON_TRY_AGAIN) {
      try_again_clicked_ = true;
    }
    message_loop()->PostTask(FROM_HERE, new MessageLoop::QuitTask());
  }

  virtual void InfoBubbleFocusChanged(int caller_id) {
    VLOG(1) << "Received InfoBubbleFocusChanged";
    EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
    focus_changed_ = true;
    message_loop()->PostTask(FROM_HERE, new MessageLoop::QuitTask());
  }

  // testing::Test methods.
  virtual void SetUp() {
    BrowserWithTestWindowTest::SetUp();
    SpeechInputBubble::set_factory(
        &SpeechInputBubbleControllerTest::CreateBubble);
    io_thread_.Start();
  }

  virtual void TearDown() {
    SpeechInputBubble::set_factory(NULL);
    io_thread_.Stop();
    BrowserWithTestWindowTest::TearDown();
  }

  static void ActivateBubble() {
    if (MockSpeechInputBubble::type() ==
        MockSpeechInputBubble::BUBBLE_TEST_FOCUS_CHANGED) {
      test_fixture_->controller_->SetBubbleWarmUpMode(kBubbleCallerId);
    } else {
      test_fixture_->controller_->SetBubbleMessage(kBubbleCallerId,
                                                   ASCIIToUTF16("Test"));
    }
  }

  static SpeechInputBubble* CreateBubble(TabContents* tab_contents,
                                         SpeechInputBubble::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,
                                     NewRunnableFunction(&ActivateBubble));

    // The |tab_contents| parameter would be NULL since the dummy caller id
    // passed to CreateBubble would not have matched any active tab. So get a
    // real TabContents pointer from the test fixture and pass that, because
    // the bubble controller registers for tab close notifications which need
    // a valid TabContents.
    tab_contents = test_fixture_->browser()->GetSelectedTabContents();
    return new MockSpeechInputBubble(tab_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.
  BrowserThread io_thread_;
  bool cancel_clicked_;
  bool try_again_clicked_;
  bool focus_changed_;
  scoped_refptr<SpeechInputBubbleController> controller_;

  static const int kBubbleCallerId;
  static SpeechInputBubbleControllerTest* test_fixture_;
};

SpeechInputBubbleControllerTest*
SpeechInputBubbleControllerTest::test_fixture_ = NULL;

const int SpeechInputBubbleControllerTest::kBubbleCallerId = 1;

MockSpeechInputBubble::BubbleType MockSpeechInputBubble::type_ =
    MockSpeechInputBubble::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(SpeechInputBubbleControllerTest, TestFocusChanged) {
  MockSpeechInputBubble::set_type(
      MockSpeechInputBubble::BUBBLE_TEST_FOCUS_CHANGED);

  controller_->CreateBubble(kBubbleCallerId, 1, 1, gfx::Rect(1, 1));
  MessageLoop::current()->Run();
  EXPECT_TRUE(focus_changed_);
  EXPECT_FALSE(cancel_clicked_);
  EXPECT_FALSE(try_again_clicked_);
  controller_->CloseBubble(kBubbleCallerId);
}

// 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(SpeechInputBubbleControllerTest, TestRecognitionCancelled) {
  MockSpeechInputBubble::set_type(
      MockSpeechInputBubble::BUBBLE_TEST_CLICK_CANCEL);

  controller_->CreateBubble(kBubbleCallerId, 1, 1, gfx::Rect(1, 1));
  MessageLoop::current()->Run();
  EXPECT_TRUE(cancel_clicked_);
  EXPECT_FALSE(try_again_clicked_);
  EXPECT_FALSE(focus_changed_);
  controller_->CloseBubble(kBubbleCallerId);
}

// 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(SpeechInputBubbleControllerTest, TestTryAgainClicked) {
  MockSpeechInputBubble::set_type(
      MockSpeechInputBubble::BUBBLE_TEST_CLICK_TRY_AGAIN);

  controller_->CreateBubble(kBubbleCallerId, 1, 1, gfx::Rect(1, 1));
  MessageLoop::current()->Run();
  EXPECT_FALSE(cancel_clicked_);
  EXPECT_TRUE(try_again_clicked_);
  EXPECT_FALSE(focus_changed_);
  controller_->CloseBubble(kBubbleCallerId);
}

}  // namespace speech_input