diff options
24 files changed, 1831 insertions, 654 deletions
diff --git a/chrome/browser/ui/webui/javascript2webui.js b/chrome/browser/ui/webui/javascript2webui.js index 3f748cf..b0fc9bd 100644 --- a/chrome/browser/ui/webui/javascript2webui.js +++ b/chrome/browser/ui/webui/javascript2webui.js @@ -4,36 +4,60 @@ if (arguments.length < 3) { print('usage: ' + arguments[0] + ' path-to-testfile.js testfile.js [output.cc]'); - quit(); -} -var js_file = arguments[1]; -var js_file_base = arguments[2]; -var outputfile = arguments[3]; -var prevfuncs = {}; -for (var func in this) { - if (this[func] instanceof Function) - prevfuncs[func] = func; -} -var js = load(js_file); -if (!('test_fixture' in this)) { - print(js_file + ' did not define test_fixture.'); quit(-1); } -if (!('test_add_library' in this)) { - this['test_add_library'] = true; -} +var jsFile = arguments[1]; +var jsFileBase = arguments[2]; +var outputFile = arguments[3]; + +// Generate the file to stdout. print('// GENERATED FILE'); print('// ' + arguments.join(' ')); print('// PLEASE DO NOT HAND EDIT!'); print(); -for (var func in this) { - if (!prevfuncs[func] && this[func] instanceof Function) { - print('IN_PROC_BROWSER_TEST_F(' + test_fixture + ', ' + func + ') {'); - if (test_add_library) - print(' AddLibrary(FilePath(FILE_PATH_LITERAL("' + js_file_base + - '")));'); - print(' ASSERT_TRUE(RunJavascriptTest("' + func + '"));'); - print('}'); - print(); +print('#include "chrome/browser/ui/webui/web_ui_browsertest.h"'); +print('#include "googleurl/src/gurl.h"'); +print('#include "testing/gtest/include/gtest/gtest.h"'); +print(); + +function GEN(code) { + print(code); +} + +var typedeffedCppFixtures = {}; + +function TEST_F(testFixture, testFunction, testBody) { + var browsePreload = this[testFixture].prototype.browsePreload; + var browsePrintPreload = this[testFixture].prototype.browsePrintPreload; + var testGenPreamble = this[testFixture].prototype.testGenPreamble; + var testGenPostamble = this[testFixture].prototype.testGenPostamble; + var typedefCppFixture = this[testFixture].prototype.typedefCppFixture; + + if (typedefCppFixture && !(testFixture in typedeffedCppFixtures)) { + print('typedef ' + typedefCppFixture + ' ' + testFixture + ';'); + typedeffedCppFixtures[testFixture] = typedefCppFixture; + } + + print('IN_PROC_BROWSER_TEST_F(' + testFixture + ', ' + testFunction + ') {'); + if (testGenPreamble) + testGenPreamble(testFixture, testFunction); + print(' AddLibrary(FilePath(FILE_PATH_LITERAL("' + jsFileBase + '")));'); + if (browsePreload) { + print(' BrowsePreload(GURL("' + browsePreload + '"), "' + testFixture + + '", "' + testFunction + '");'); + } + if (browsePrintPreload) { + print(' BrowsePrintPreload(GURL(WebUITestDataPathToURL(\n' + + ' FILE_PATH_LITERAL("' + browsePrintPreload + '"))),\n' + + ' "' + testFixture + '", "' + testFunction + '");'); } + print(' ASSERT_TRUE(RunJavascriptTestF("' + testFixture + '", "' + + testFunction + '"));'); + if (testGenPostamble) + testGenPostamble(testFixture, testFunction); + print('}'); + print(); } + +var js = read(jsFile); +eval(js); diff --git a/chrome/browser/ui/webui/print_preview_browsertest.cc b/chrome/browser/ui/webui/print_preview_browsertest.cc deleted file mode 100644 index 706e56f..0000000 --- a/chrome/browser/ui/webui/print_preview_browsertest.cc +++ /dev/null @@ -1,80 +0,0 @@ -// 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/command_line.h" -#include "base/path_service.h" -#include "base/stringprintf.h" -#include "base/values.h" -#include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/webui/web_ui_browsertest.h" -#include "chrome/common/chrome_paths.h" -#include "chrome/common/chrome_switches.h" -#include "chrome/test/test_tab_strip_model_observer.h" -#include "chrome/test/ui_test_utils.h" -#include "googleurl/src/gurl.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace { - -// TODO(scr) migrate this to ui_test_utils? -// Cause Print on the currently selected tab of |browser|, blocking until the -// Print Preview tab shows up. -void PrintAndWaitForPrintPreviewTab( - Browser* browser, - TestTabStripModelObserver::LoadStartObserver* load_start_observer) { - TestTabStripModelObserver tabstrip_observer(browser->tabstrip_model(), - load_start_observer); - TabContents* initiator_tab_contents = browser->GetSelectedTabContents(); - browser->Print(); - tabstrip_observer.WaitForObservation(); - EXPECT_NE(initiator_tab_contents, browser->GetSelectedTabContents()); -} - -} // namespace - -// crbug.com/88104 - v8_shell#host doesn't build when host=="arm". -#if !defined(ARCH_CPU_ARM_FAMILY) - -class PrintPreviewWebUITest - : public WebUIBrowserTest, - public TestTabStripModelObserver::LoadStartObserver { - protected: - // WebUIBrowserTest: - virtual void SetUpOnMainThread() OVERRIDE { - WebUIBrowserTest::SetUpOnMainThread(); - if (!HasPDFLib()) - skipTest(base::StringPrintf("%s:%d: No PDF Lib.", __FILE__, __LINE__)); - - AddLibrary(FilePath(FILE_PATH_LITERAL("print_preview.js"))); - GURL print_preview_test_url = WebUITestDataPathToURL( - FILE_PATH_LITERAL("print_preview_hello_world_test.html")); - ui_test_utils::NavigateToURL(browser(), print_preview_test_url); - PrintAndWaitForPrintPreviewTab(browser(), this); - } - - virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { - WebUIBrowserTest::SetUpCommandLine(command_line); -#if !defined(GOOGLE_CHROME_BUILD) || defined(OS_CHROMEOS) || defined(OS_MACOSX) - // Don't enable the flag for chrome builds, which should be on by default. - command_line->AppendSwitch(switches::kEnablePrintPreview); -#else - ASSERT_TRUE(switches::IsPrintPreviewEnabled()); -#endif - } - - // TestTabStripModelObserver::LoadStartObserver: - virtual void OnLoadStart() OVERRIDE { - PreLoadJavascriptLibraries(true); - } - - bool HasPDFLib() const { - FilePath pdf; - return PathService::Get(chrome::FILE_PDF_PLUGIN, &pdf) && - file_util::PathExists(pdf); - } -}; - -#include "js2webui/chrome/test/data/webui/print_preview-inl.h" - -#endif // !defined(ARCH_CPU_ARM_FAMILY) diff --git a/chrome/browser/ui/webui/settings_browsertest.cc b/chrome/browser/ui/webui/settings_browsertest.cc deleted file mode 100644 index 6bd5766..0000000 --- a/chrome/browser/ui/webui/settings_browsertest.cc +++ /dev/null @@ -1,126 +0,0 @@ -// 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 "chrome/browser/ui/webui/options/core_options_handler.h" -#include "chrome/browser/ui/webui/web_ui_browsertest.h" -#include "chrome/common/url_constants.h" -#include "chrome/test/ui_test_utils.h" -#include "googleurl/src/gurl.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -using ::testing::StrictMock; -using ::testing::_; - -MATCHER_P(Eq_ListValue, inList, "") { - return arg->Equals(inList); -} - -class MockCoreOptionsHandler : public CoreOptionsHandler { - public: - MOCK_METHOD1(HandleInitialize, - void(const ListValue* args)); - MOCK_METHOD1(HandleFetchPrefs, - void(const ListValue* args)); - MOCK_METHOD1(HandleObservePrefs, - void(const ListValue* args)); - MOCK_METHOD1(HandleSetBooleanPref, - void(const ListValue* args)); - MOCK_METHOD1(HandleSetIntegerPref, - void(const ListValue* args)); - MOCK_METHOD1(HandleSetDoublePref, - void(const ListValue* args)); - MOCK_METHOD1(HandleSetStringPref, - void(const ListValue* args)); - MOCK_METHOD1(HandleSetObjectPref, - void(const ListValue* args)); - MOCK_METHOD1(HandleClearPref, - void(const ListValue* args)); - MOCK_METHOD1(HandleUserMetricsAction, - void(const ListValue* args)); - - virtual void RegisterMessages() { - web_ui_->RegisterMessageCallback("coreOptionsInitialize", - NewCallback(this, &MockCoreOptionsHandler ::HandleInitialize)); - web_ui_->RegisterMessageCallback("fetchPrefs", - NewCallback(this, &MockCoreOptionsHandler ::HandleFetchPrefs)); - web_ui_->RegisterMessageCallback("observePrefs", - NewCallback(this, &MockCoreOptionsHandler ::HandleObservePrefs)); - web_ui_->RegisterMessageCallback("setBooleanPref", - NewCallback(this, &MockCoreOptionsHandler ::HandleSetBooleanPref)); - web_ui_->RegisterMessageCallback("setIntegerPref", - NewCallback(this, &MockCoreOptionsHandler ::HandleSetIntegerPref)); - web_ui_->RegisterMessageCallback("setDoublePref", - NewCallback(this, &MockCoreOptionsHandler ::HandleSetDoublePref)); - web_ui_->RegisterMessageCallback("setStringPref", - NewCallback(this, &MockCoreOptionsHandler ::HandleSetStringPref)); - web_ui_->RegisterMessageCallback("setObjectPref", - NewCallback(this, &MockCoreOptionsHandler ::HandleSetObjectPref)); - web_ui_->RegisterMessageCallback("clearPref", - NewCallback(this, &MockCoreOptionsHandler ::HandleClearPref)); - web_ui_->RegisterMessageCallback("coreOptionsUserMetricsAction", - NewCallback(this, &MockCoreOptionsHandler ::HandleUserMetricsAction)); - } -}; - -class SettingsWebUITest : public WebUIBrowserTest { - protected: - virtual void SetUpInProcessBrowserTestFixture() { - WebUIBrowserTest::SetUpInProcessBrowserTestFixture(); - AddLibrary(FilePath(FILE_PATH_LITERAL("settings.js"))); - } - - virtual void SetUpOnMainThread() { - mock_core_options_handler_.reset(new StrictMock<MockCoreOptionsHandler>()); - ui_test_utils::NavigateToURL( - browser(), GURL(chrome::kChromeUISettingsURL)); - } - - virtual void CleanUpOnMainThread() { - mock_core_options_handler_.reset(); - } - - virtual WebUIMessageHandler* GetMockMessageHandler() { - return mock_core_options_handler_.get(); - } - - scoped_ptr<StrictMock<MockCoreOptionsHandler> > mock_core_options_handler_; -}; - -// Test the end to end js to WebUI handler code path for -// the message setBooleanPref. -// TODO(dtseng): add more EXPECT_CALL's when updating js test. - -// Crashes on Mac only. See http://crbug.com/79181 -#if defined(OS_MACOSX) -#define MAYBE_TestSetBooleanPrefTriggers DISABLED_TestSetBooleanPrefTriggers -#else -#define MAYBE_TestSetBooleanPrefTriggers TestSetBooleanPrefTriggers -#endif -IN_PROC_BROWSER_TEST_F(SettingsWebUITest, MAYBE_TestSetBooleanPrefTriggers) { - // This serves as an example of a very constrained test. - ListValue true_list_value; - true_list_value.Append(Value::CreateStringValue("browser.show_home_button")); - true_list_value.Append(Value::CreateBooleanValue(true)); - true_list_value.Append( - Value::CreateStringValue("Options_Homepage_HomeButton")); - EXPECT_CALL(*mock_core_options_handler_, - HandleSetBooleanPref(Eq_ListValue(&true_list_value))); - ASSERT_TRUE(RunJavascriptTest("testSetBooleanPrefTriggers")); -} - -// Not meant to run on ChromeOS at this time. -// Not finishing in windows. http://crbug.com/81723 -#if defined(OS_CHROMEOS) || defined(OS_MACOSX) || defined (OS_WIN) || defined(TOUCH_UI) -#define MAYBE_TestRefreshStaysOnCurrentPage\ - DISABLED_TestRefreshStaysOnCurrentPage -#else -#define MAYBE_TestRefreshStaysOnCurrentPage TestRefreshStaysOnCurrentPage -#endif -IN_PROC_BROWSER_TEST_F(SettingsWebUITest, - MAYBE_TestRefreshStaysOnCurrentPage) { - ASSERT_TRUE(RunJavascriptFunction("openUnderTheHood")); - ASSERT_TRUE(RunJavascriptFunction("refreshPage")); - ASSERT_TRUE(RunJavascriptTest("testPageIsUnderTheHood")); -} diff --git a/chrome/browser/ui/webui/web_ui_browsertest.cc b/chrome/browser/ui/webui/web_ui_browsertest.cc index b6e5da1..e33c483 100644 --- a/chrome/browser/ui/webui/web_ui_browsertest.cc +++ b/chrome/browser/ui/webui/web_ui_browsertest.cc @@ -11,10 +11,12 @@ #include "base/utf_string_conversions.h" #include "base/values.h" #include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_navigator.h" #include "chrome/browser/ui/webui/chrome_web_ui.h" -#include "chrome/browser/ui/webui/test_chrome_web_ui_factory.h" +#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/url_constants.h" +#include "chrome/test/test_tab_strip_model_observer.h" #include "chrome/test/ui_test_utils.h" #include "content/browser/tab_contents/tab_contents.h" #include "testing/gtest/include/gtest/gtest-spi.h" @@ -22,6 +24,7 @@ namespace { +const FilePath::CharType kMockJS[] = FILE_PATH_LITERAL("mock4js.js"); const FilePath::CharType kWebUILibraryJS[] = FILE_PATH_LITERAL("test_api.js"); const FilePath::CharType kWebUITestFolder[] = FILE_PATH_LITERAL("webui"); base::LazyInstance<std::vector<std::string> > error_messages_( @@ -43,6 +46,10 @@ bool LogHandler(int severity, WebUIBrowserTest::~WebUIBrowserTest() {} +void WebUIBrowserTest::AddLibrary(const FilePath& library_path) { + user_libraries_.push_back(library_path); +} + bool WebUIBrowserTest::RunJavascriptFunction(const std::string& function_name) { return RunJavascriptFunction(function_name, ConstValueVector()); } @@ -69,6 +76,14 @@ bool WebUIBrowserTest::RunJavascriptFunction( return RunJavascriptUsingHandler(function_name, function_arguments, false); } +bool WebUIBrowserTest::RunJavascriptTestF(const std::string& test_fixture, + const std::string& test_name) { + ConstValueVector args; + args.push_back(Value::CreateStringValue(test_fixture)); + args.push_back(Value::CreateStringValue(test_name)); + return RunJavascriptTest("RUN_TEST_F", args); +} + bool WebUIBrowserTest::RunJavascriptTest(const std::string& test_name) { return RunJavascriptTest(test_name, ConstValueVector()); } @@ -95,19 +110,52 @@ bool WebUIBrowserTest::RunJavascriptTest( return RunJavascriptUsingHandler(test_name, test_arguments, true); } -void WebUIBrowserTest::PreLoadJavascriptLibraries(bool override_chrome_send) { +void WebUIBrowserTest::PreLoadJavascriptLibraries( + const std::string& preload_test_fixture, + const std::string& preload_test_name) { ASSERT_FALSE(libraries_preloaded_); - scoped_ptr<Value> override_chrome_send_arg( - Value::CreateBooleanValue(override_chrome_send)); - RunJavascriptFunction("preloadJavascriptLibraries", - *override_chrome_send_arg); + ConstValueVector args; + args.push_back(Value::CreateStringValue(preload_test_fixture)); + args.push_back(Value::CreateStringValue(preload_test_name)); + RunJavascriptFunction("preloadJavascriptLibraries", args); libraries_preloaded_ = true; } +void WebUIBrowserTest::BrowsePreload(const GURL& browse_to, + const std::string& preload_test_fixture, + const std::string& preload_test_name) { + // Remember for callback OnJsInjectionReady(). + preload_test_fixture_ = preload_test_fixture; + preload_test_name_ = preload_test_name; + + TestNavigationObserver navigation_observer( + &browser()->GetSelectedTabContentsWrapper()->controller(), this, 1); + browser::NavigateParams params( + browser(), GURL(browse_to), PageTransition::TYPED); + params.disposition = CURRENT_TAB; + browser::Navigate(¶ms); + navigation_observer.WaitForObservation(); +} + +void WebUIBrowserTest::BrowsePrintPreload( + const GURL& browse_to, + const std::string& preload_test_fixture, + const std::string& preload_test_name) { + // Remember for callback OnJsInjectionReady(). + preload_test_fixture_ = preload_test_fixture; + preload_test_name_ = preload_test_name; + + ui_test_utils::NavigateToURL(browser(), browse_to); + + TestTabStripModelObserver tabstrip_observer( + browser()->tabstrip_model(), this); + browser()->Print(); + tabstrip_observer.WaitForObservation(); +} + WebUIBrowserTest::WebUIBrowserTest() : test_handler_(new WebUITestHandler()), - libraries_preloaded_(false), - skip_test_(false) {} + libraries_preloaded_(false) {} void WebUIBrowserTest::SetUpInProcessBrowserTestFixture() { ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_data_directory_)); @@ -118,6 +166,13 @@ void WebUIBrowserTest::SetUpInProcessBrowserTestFixture() { PathService::Get(chrome::FILE_RESOURCES_PACK, &resources_pack_path); ResourceBundle::AddDataPackToSharedInstance(resources_pack_path); + FilePath mockPath; + ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &mockPath)); + mockPath = mockPath.AppendASCII("chrome"); + mockPath = mockPath.AppendASCII("third_party"); + mockPath = mockPath.AppendASCII("mock4js"); + mockPath = mockPath.Append(kMockJS); + AddLibrary(mockPath); AddLibrary(FilePath(kWebUILibraryJS)); } @@ -125,11 +180,6 @@ WebUIMessageHandler* WebUIBrowserTest::GetMockMessageHandler() { return NULL; } -void WebUIBrowserTest::skipTest(const std::string& skip_test_message) { - skip_test_ = true; - skip_test_message_.assign(skip_test_message); -} - GURL WebUIBrowserTest::WebUITestDataPathToURL( const FilePath::StringType& path) { FilePath dir_test_data; @@ -140,14 +190,17 @@ GURL WebUIBrowserTest::WebUITestDataPathToURL( return net::FilePathToFileURL(test_path); } +void WebUIBrowserTest::OnJsInjectionReady() { + PreLoadJavascriptLibraries(preload_test_fixture_, preload_test_name_); +} + void WebUIBrowserTest::BuildJavascriptLibraries(std::string* content) { ASSERT_TRUE(content != NULL); - std::string library_content, src_content; - std::vector<FilePath>::iterator user_libraries_iterator; for (user_libraries_iterator = user_libraries_.begin(); user_libraries_iterator != user_libraries_.end(); ++user_libraries_iterator) { + std::string library_content; if (user_libraries_iterator->IsAbsolute()) { ASSERT_TRUE(file_util::ReadFileToString(*user_libraries_iterator, &library_content)); @@ -182,13 +235,6 @@ bool WebUIBrowserTest::RunJavascriptUsingHandler( const std::string& function_name, const ConstValueVector& function_arguments, bool is_test) { - if (skip_test_) { - SUCCEED(); - LOG(WARNING) - << "Skipping test " << function_name << ": " << skip_test_message_; - return true; - } - std::string content; if (!libraries_preloaded_) BuildJavascriptLibraries(&content); @@ -227,23 +273,6 @@ void WebUIBrowserTest::SetupHandlers() { GetMockMessageHandler()->Attach(web_ui_instance); } -void WebUIBrowserTest::AddLibrary(const FilePath& library_path) { - user_libraries_.push_back(library_path); -} - -IN_PROC_BROWSER_TEST_F(WebUIBrowserTest, TestSamplePass) { - AddLibrary(FilePath(FILE_PATH_LITERAL("sample_downloads.js"))); - - // Navigate to UI. - // TODO(dtseng): make accessor for subclasses to return? - ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUIDownloadsURL)); - - ASSERT_TRUE(RunJavascriptTest("testAssertFalse")); - ASSERT_FALSE(RunJavascriptTest("FAILS_testAssertFalse")); - ASSERT_TRUE(RunJavascriptTest("testInitialFocus")); - ASSERT_FALSE(RunJavascriptTest("testConsoleError")); -} - // According to the interface for EXPECT_FATAL_FAILURE // (http://code.google.com/p/googletest/wiki/AdvancedGuide#Catching_Failures) // the statement must be statically available. Therefore, we make a static @@ -270,6 +299,7 @@ class WebUIBrowserExpectFailTest : public WebUIBrowserTest { private: static WebUIBrowserTest* s_test_; }; + WebUIBrowserTest* WebUIBrowserExpectFailTest::s_test_ = NULL; IN_PROC_BROWSER_TEST_F(WebUIBrowserExpectFailTest, TestFailsFast) { @@ -278,48 +308,3 @@ IN_PROC_BROWSER_TEST_F(WebUIBrowserExpectFailTest, TestFailsFast) { EXPECT_FATAL_FAILURE(RunJavascriptTestNoReturn("FAILS_BogusFunctionName"), "WebUITestHandler::Observe"); } - -// crbug.com/88104 - v8_shell#host doesn't build when host=="arm". -#if !defined(ARCH_CPU_ARM_FAMILY) -// This test framework is used in the generated tests, which are included -// below. WebUIBrowserTest requires being on a page which is a WebUI page. Using -// the TestChromeWebUIFactory, we use a dummy URL |kChromeTestBrowserTestPass|, -// which we force to be a WebUI page. -class WebUIBrowserTestPass - : public WebUIBrowserTest, - public TestChromeWebUIFactory::WebUIProvider { - private: - // TestChromeWebUIFactory::WebUIProvider: - virtual WebUI* NewWebUI(TabContents* tab_contents, - const GURL& url) OVERRIDE { - return new ChromeWebUI(tab_contents); - } - - // InProcessBrowserTest: - virtual void SetUpOnMainThread() OVERRIDE { - WebUIBrowserTest::SetUpOnMainThread(); - ui_test_utils::NavigateToURL(browser(), - GURL(kChromeTestBrowserTestPass)); - } - - virtual void SetUpInProcessBrowserTestFixture() OVERRIDE { - WebUIBrowserTest::SetUpInProcessBrowserTestFixture(); - TestChromeWebUIFactory::AddFactoryOverride( - GURL(kChromeTestBrowserTestPass).host(), this); - } - - virtual void TearDownInProcessBrowserTestFixture() OVERRIDE { - WebUIBrowserTest::TearDownInProcessBrowserTestFixture(); - TestChromeWebUIFactory::RemoveFactoryOverride( - GURL(kChromeTestBrowserTestPass).host()); - } - - static const char kChromeTestBrowserTestPass[]; -}; - -const char WebUIBrowserTestPass::kChromeTestBrowserTestPass[] = - "chrome://WebUIBrowserTestPass"; - -#include "js2webui/chrome/test/data/webui/sample_pass-inl.h" - -#endif // !defined(ARCH_CPU_ARM_FAMILY) diff --git a/chrome/browser/ui/webui/web_ui_browsertest.h b/chrome/browser/ui/webui/web_ui_browsertest.h index 91f136d..635b0c0 100644 --- a/chrome/browser/ui/webui/web_ui_browsertest.h +++ b/chrome/browser/ui/webui/web_ui_browsertest.h @@ -11,6 +11,7 @@ #include "base/file_path.h" #include "chrome/browser/ui/webui/web_ui_test_handler.h" #include "chrome/test/in_process_browser_test.h" +#include "chrome/test/test_navigation_observer.h" class Value; class WebUIMessageHandler; @@ -29,7 +30,9 @@ class WebUIMessageHandler; // These tests should follow the form given in: // chrome/test/data/webui/sample_downloads.js. // and the lone test within this class. -class WebUIBrowserTest : public InProcessBrowserTest { +class WebUIBrowserTest + : public InProcessBrowserTest, + public TestNavigationObserver::JsInjectionReadyObserver { public: typedef std::vector<const Value*> ConstValueVector; virtual ~WebUIBrowserTest(); @@ -50,6 +53,10 @@ class WebUIBrowserTest : public InProcessBrowserTest { bool RunJavascriptFunction(const std::string& function_name, const ConstValueVector& function_arguments); + // Runs a test fixture that may include calls to functions in test_api.js. + bool RunJavascriptTestF(const std::string& test_fixture, + const std::string& test_name); + // Runs a test that may include calls to functions in test_api.js. bool RunJavascriptTest(const std::string& test_name); bool RunJavascriptTest(const std::string& test_name, @@ -64,7 +71,24 @@ class WebUIBrowserTest : public InProcessBrowserTest { // to prevent re-loading at next javascript invocation. If // |override_chrome_send| is true, then chrome.send is overridden for // javascript to register handlers. - void PreLoadJavascriptLibraries(bool override_chrome_send); + void PreLoadJavascriptLibraries(const std::string& preload_test_fixture, + const std::string& preload_test_name); + + // Called by javascript-generated test bodies to browse to a page and preload + // the javascript for the given |preload_test_fixture| and + // |preload_test_name|. chrome.send will be overridden to allow javascript + // handler mocking. + void BrowsePreload(const GURL& browse_to, + const std::string& preload_test_fixture, + const std::string& preload_test_name); + + // Called by javascript-generated test bodies to browse to a page and preload + // the javascript for the given |preload_test_fixture| and + // |preload_test_name|. chrome.send will be overridden to allow javascript + // handler mocking. + void BrowsePrintPreload(const GURL& browse_to, + const std::string& preload_test_fixture, + const std::string& preload_test_name); protected: WebUIBrowserTest(); @@ -75,14 +99,14 @@ class WebUIBrowserTest : public InProcessBrowserTest { // Returns a mock WebUI object under test (if any). virtual WebUIMessageHandler* GetMockMessageHandler(); - // Skip this test with |skip_test_message|. - void skipTest(const std::string& skip_test_message); - // Returns a file:// GURL constructed from |path| inside the test data dir for // webui tests. static GURL WebUITestDataPathToURL(const FilePath::StringType& path); private: + // TestNavigationObserver::JsInjectionReadyObserver implementation. + virtual void OnJsInjectionReady() OVERRIDE; + // Builds a string containing all added javascript libraries. void BuildJavascriptLibraries(std::string* content); @@ -114,8 +138,10 @@ class WebUIBrowserTest : public InProcessBrowserTest { // again. bool libraries_preloaded_; - bool skip_test_; - std::string skip_test_message_; + // Saves the states of |test_fixture| and |test_name| for calling + // PreloadJavascriptLibraries(). + std::string preload_test_fixture_; + std::string preload_test_name_; }; #endif // CHROME_BROWSER_UI_WEBUI_WEB_UI_BROWSERTEST_H_ diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index b8ea8ef..7dd6857 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -153,6 +153,8 @@ 'test/test_launcher_utils.h', 'test/test_location_bar.cc', 'test/test_location_bar.h', + 'test/test_navigation_observer.cc', + 'test/test_navigation_observer.h', 'test/test_switches.cc', 'test/test_switches.h', 'test/test_tab_strip_model_observer.cc', @@ -2294,7 +2296,9 @@ 'gypv8sh': '../tools/gypv8sh.py', 'js2webui': 'browser/ui/webui/javascript2webui.js', 'js2webui_out_dir': '<(SHARED_INTERMEDIATE_DIR)/js2webui', + 'mock_js': 'third_party/mock4js/mock4js.js', 'rule_input_relpath': 'test/data/webui', + 'test_api_js': 'test/data/webui/test_api.js', }, 'dependencies': [ 'browser', @@ -2529,8 +2533,6 @@ 'browser/ui/views/html_dialog_view_browsertest.cc', 'browser/ui/webui/chrome_url_data_manager_browsertest.cc', 'browser/ui/webui/ntp/most_visited_browsertest.cc', - 'browser/ui/webui/print_preview_browsertest.cc', - 'browser/ui/webui/settings_browsertest.cc', 'browser/ui/webui/test_chrome_web_ui_factory_browsertest.cc', 'browser/ui/webui/BidiCheckerWebUITest.cc', 'browser/ui/webui/BidiCheckerWebUITest.h', @@ -2556,7 +2558,7 @@ 'renderer/translate_helper_browsertest.cc', 'test/automation/dom_automation_browsertest.cc', 'test/data/webui/print_preview.js', - 'test/data/webui/sample_pass.js', + 'test/data/webui/options.js', 'test/gpu/gpu_browsertest.cc', 'test/in_process_browser_test_browsertest.cc', 'test/out_of_proc_test_runner.cc', @@ -2594,7 +2596,7 @@ }, { #else: OS == "chromeos" 'sources!': [ 'browser/service/service_process_control_browsertest.cc', - 'browser/ui/webui/print_preview_browsertest.cc', + 'browser/ui/webui/print_preview.js', ], }], ['file_manager_extension==0', { @@ -2757,10 +2759,12 @@ 'inputs': [ '<(gypv8sh)', '<(PRODUCT_DIR)/v8_shell<(EXECUTABLE_SUFFIX)', + '<(mock_js)', + '<(test_api_js)', '<(js2webui)', ], 'outputs': [ - '<(js2webui_out_dir)/js2webui/chrome/<(rule_input_relpath)/<(RULE_INPUT_ROOT)-inl.h', + '<(js2webui_out_dir)/chrome/<(rule_input_relpath)/<(RULE_INPUT_ROOT).cc', ], 'process_outputs_as_sources': 1, 'action': [ @@ -2768,9 +2772,6 @@ ], }, ], - 'include_dirs': [ - '<(js2webui_out_dir)', - ], 'dependencies': [ # build time dependency. '../v8/tools/gyp/v8.gyp:v8_shell#host', diff --git a/chrome/test/data/webui/options.js b/chrome/test/data/webui/options.js new file mode 100644 index 0000000..d8a5089 --- /dev/null +++ b/chrome/test/data/webui/options.js @@ -0,0 +1,104 @@ +// 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. + +/** + * TestFixture for OptionsPage WebUI testing. + * @extends {testing.Test} + * @constructor + **/ +function OptionsWebUITest() {} + +OptionsWebUITest.prototype = { + __proto__: testing.Test.prototype, + + /** + * Browse to the options page & call our PreLoad(). + **/ + browsePreload: 'chrome://settings', + + /** + * Register a mock handler to ensure expectations are met and options pages + * behave correctly. + **/ + PreLoad: function() { + + /** + * Create handler class with empty methods to allow mocking to register + * expectations and for registration of handlers with chrome.send. + **/ + function MockOptionsHandler() {} + + MockOptionsHandler.prototype = { + coreOptionsInitialize: function() {}, + fetchPrefs: function() {}, + observePrefs: function() {}, + setBooleanPref: function() {}, + setIntegerPref: function() {}, + setDoublePref: function() {}, + setStringPref: function() {}, + setObjectPref: function() {}, + clearPref: function() {}, + coreOptionsUserMetricsAction: function() {}, + }; + + // Create the actual mock and register stubs for methods expected to be + // called before our tests run. Specific expectations can be made in the + // tests themselves. + var mockHandler = this.mockHandler = mock(MockOptionsHandler); + mockHandler.stubs().fetchPrefs(ANYTHING); + mockHandler.stubs().observePrefs(ANYTHING); + mockHandler.stubs().coreOptionsInitialize(); + + // Register our mock as a handler of the chrome.send messages. + registerMockMessageCallbacks(mockHandler, MockOptionsHandler); + }, +}; + +// Crashes on Mac only. See http://crbug.com/79181 +GEN('#if defined(OS_MACOSX)'); +GEN('#define MAYBE_testSetBooleanPrefTriggers ' + + 'DISABLED_testSetBooleanPrefTriggers'); +GEN('#else'); +GEN('#define MAYBE_testSetBooleanPrefTriggers testSetBooleanPrefTriggers'); +GEN('#endif // defined(OS_MACOSX)'); + +TEST_F('OptionsWebUITest', 'MAYBE_testSetBooleanPrefTriggers', function() { + // TODO(dtseng): make generic to click all buttons. + var showHomeButton = $('toolbarShowHomeButton'); + var trueListValue = [ + 'browser.show_home_button', + true, + 'Options_Homepage_HomeButton', + ]; + // Note: this expectation is checked in testing::Test::TearDown. + this.mockHandler.expects(once()).setBooleanPref(trueListValue); + + // Cause the handler to be called. + showHomeButton.click(); + showHomeButton.blur(); +}); + +// Not meant to run on ChromeOS at this time. +// Not finishing in windows. http://crbug.com/81723 +GEN('#if defined(OS_CHROMEOS) || defined(OS_MACOSX) || defined(OS_WIN) \\'); +GEN(' || defined(TOUCH_UI)'); +GEN('#define MAYBE_testRefreshStaysOnCurrentPage \\'); +GEN(' DISABLED_testRefreshStaysOnCurrentPage'); +GEN('#else'); +GEN('#define MAYBE_testRefreshStaysOnCurrentPage ' + + 'testRefreshStaysOnCurrentPage'); +GEN('#endif'); + +TEST_F('OptionsWebUITest', 'MAYBE_testRefreshStaysOnCurrentPage', function() { + var item = $('advancedPageNav'); + item.onclick(); + window.location.reload(); + var pageInstance = AdvancedOptions.getInstance(); + var topPage = OptionsPage.getTopmostVisiblePage(); + var expectedTitle = pageInstance.title; + var actualTitle = document.title; + assertEquals("chrome://settings/advanced", document.location.href); + assertEquals(expectedTitle, actualTitle); + assertEquals(pageInstance, topPage); +}); diff --git a/chrome/test/data/webui/print_preview.js b/chrome/test/data/webui/print_preview.js index beb1c14..7b83977 100644 --- a/chrome/test/data/webui/print_preview.js +++ b/chrome/test/data/webui/print_preview.js @@ -2,85 +2,132 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -(function() { - function MockHandler() { - this.__proto__ = MockHandler.prototype; - }; +/** + * TestFixture for print preview WebUI testing. + * @extends {testing.Test} + * @constructor + **/ +function PrintPreviewWebUITest() {} - MockHandler.prototype = { - 'getDefaultPrinter': function() { - console.log('getDefaultPrinter'); - setDefaultPrinter('FooDevice'); - }, - 'getPrinters': function() { - console.log('getPrinters'); - setPrinters([ - { - 'printerName': 'FooName', - 'deviceName': 'FooDevice', - }, - { - 'printerName': 'BarName', - 'deviceName': 'BarDevice', - }, - ]); - }, - 'getPreview': function(settings) { - console.log('getPreview(' + settings + ')'); - updatePrintPreview(1, 'title', true); - }, - 'print': function(settings) { - console.log('print(' + settings + ')'); - }, - 'getPrinterCapabilities': function(printer_name) { - console.log('getPrinterCapabilities(' + printer_name + ')'); - updateWithPrinterCapabilities({ - 'disableColorOption': true, - 'setColorAsDefault': true, - 'disableCopiesOption': true - }); - }, - 'showSystemDialog': function() { - console.log('showSystemDialog'); - }, - 'managePrinters': function() { - console.log('managePrinters'); - }, - 'closePrintPreviewTab': function() { - console.log('closePrintPreviewTab'); - }, - 'hidePreview': function() { - console.log('hidePreview'); - }, - }; +PrintPreviewWebUITest.prototype = { + __proto__: testing.Test.prototype, - function registerCallbacks() { - console.log('registeringCallbacks'); - var mock_handler = new MockHandler(); - for (func in MockHandler.prototype) { - if (typeof(mock_handler[func]) == 'function') - registerMessageCallback(func, - mock_handler, - mock_handler[func]); - } - }; + /** + * Browse to the sample page, cause print preview & call our PreLoad(). + **/ + browsePrintPreload: 'print_preview_hello_world_test.html', - if ('window' in this && 'registerMessageCallback' in window) - registerCallbacks(); - })(); + /** + * Register a mock handler to ensure expectations are met and print preview + * behaves correctly. + **/ + PreLoad: function() { -// Tests. -function FLAKY_TestPrinterList() { - var printer_list = $('printer-list'); - assertTrue(!!printer_list, 'printer_list'); - assertTrue(printer_list.options.length >= 2, 'printer-list has at least 2'); - expectEquals('FooName', printer_list.options[0].text, '0 text is FooName'); - expectEquals('FooDevice', printer_list.options[0].value, + /** + * Create a handler class with empty methods to allow mocking to register + * expectations and for registration of handlers with chrome.send. + **/ + function MockPrintPreviewHandler() {} + + MockPrintPreviewHandler.prototype = { + getDefaultPrinter: function() {}, + getPrinters: function() {}, + getPreview: function(settings) {}, + print: function(settings) {}, + getPrinterCapabilities: function(printerName) {}, + showSystemDialog: function() {}, + managePrinters: function() {}, + closePrintPreviewTab: function() {}, + hidePreview: function() {}, + }; + + // Create the actual mock and register stubs for methods expected to be + // called before our tests run. Specific expectations can be made in the + // tests themselves. + var mockHandler = this.mockHandler = mock(MockPrintPreviewHandler); + mockHandler.stubs().getDefaultPrinter(). + will(callFunction(function() { + setDefaultPrinter('FooDevice'); + })); + mockHandler.stubs().getPrinterCapabilities(NOT_NULL). + will(callFunction(function() { + updateWithPrinterCapabilities({ + disableColorOption: true, + setColorAsDefault: true, + disableCopiesOption: true, + }); + })); + mockHandler.stubs().getPreview(NOT_NULL). + will(callFunction(function() { + updatePrintPreview(1, 'title', true); + })); + + mockHandler.stubs().getPrinters(). + will(callFunction(function() { + setPrinters([{ + printerName: 'FooName', + deviceName: 'FooDevice', + }, { + printerName: 'BarName', + deviceName: 'BarDevice', + }, + ]); + })); + + // Register our mock as a handler of the chrome.send messages. + registerMockMessageCallbacks(mockHandler, MockPrintPreviewHandler); + }, + testGenPreamble: function(testFixture, testName) { + GEN(' if (!HasPDFLib()) {'); + GEN(' LOG(WARNING)'); + GEN(' << "Skipping test ' + testFixture + '.' + testName + '"'); + GEN(' << ": No PDF Lib.";'); + GEN(' SUCCEED();'); + GEN(' return;'); + GEN(' }'); + }, + typedefCppFixture: null, +}; + +GEN('#include "base/command_line.h"'); +GEN('#include "base/path_service.h"'); +GEN('#include "base/stringprintf.h"'); +GEN('#include "chrome/browser/ui/webui/web_ui_browsertest.h"'); +GEN('#include "chrome/common/chrome_paths.h"'); +GEN('#include "chrome/common/chrome_switches.h"'); +GEN(''); +GEN('class PrintPreviewWebUITest'); +GEN(' : public WebUIBrowserTest {'); +GEN(' protected:'); +GEN(' // WebUIBrowserTest override.'); +GEN(' virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {'); +GEN(' WebUIBrowserTest::SetUpCommandLine(command_line);'); +GEN('#if !defined(GOOGLE_CHROME_BUILD) || defined(OS_CHROMEOS) || \\'); +GEN(' defined(OS_MACOSX)'); +GEN(' // Don\'t enable the flag for chrome builds, which should be on by ' + + 'default.'); +GEN(' command_line->AppendSwitch(switches::kEnablePrintPreview);'); +GEN('#else'); +GEN(' ASSERT_TRUE(switches::IsPrintPreviewEnabled());'); +GEN('#endif'); +GEN(' }'); +GEN(''); +GEN(' bool HasPDFLib() const {'); +GEN(' FilePath pdf;'); +GEN(' return PathService::Get(chrome::FILE_PDF_PLUGIN, &pdf) &&'); +GEN(' file_util::PathExists(pdf);'); +GEN(' }'); +GEN('};'); +GEN(''); + +TEST_F('PrintPreviewWebUITest', 'FLAKY_TestPrinterList', function() { + var printerList = $('printer-list'); + assertTrue(!!printerList, 'printerList'); + assertTrue(printerList.options.length >= 2, 'printer-list has at least 2'); + expectEquals('FooName', printerList.options[0].text, '0 text is FooName'); + expectEquals('FooDevice', printerList.options[0].value, '0 value is FooDevice'); - expectEquals('BarName', printer_list.options[1].text, '1 text is BarName'); - expectEquals('BarDevice', printer_list.options[1].value, + expectEquals('BarName', printerList.options[1].text, '1 text is BarName'); + expectEquals('BarDevice', printerList.options[1].value, '1 value is BarDevice'); -} - -var test_fixture = 'PrintPreviewWebUITest'; -var test_add_library = false; +}); diff --git a/chrome/test/data/webui/sample_pass.js b/chrome/test/data/webui/sample_pass.js deleted file mode 100644 index 4e6e9fc..0000000 --- a/chrome/test/data/webui/sample_pass.js +++ /dev/null @@ -1,19 +0,0 @@ -// 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. - -// Sample tests that exercise the test JS library and show how this framework -// could be used to test the downloads page. -function testAssertFalse() { - assertFalse(false); -} - -function testAssertTrue() { - assertTrue(true); -} - -function testAssertEquals() { - assertEquals(5, 5, "fives"); -} - -var test_fixture = 'WebUIBrowserTestPass'; diff --git a/chrome/test/data/webui/settings.js b/chrome/test/data/webui/settings.js deleted file mode 100644 index 2e44536..0000000 --- a/chrome/test/data/webui/settings.js +++ /dev/null @@ -1,34 +0,0 @@ -// 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. - -// Utility functions for settings WebUI page. -function refreshPage() { - window.location.reload(); -} - -function openUnderTheHood() { - var item = $('advancedPageNav'); - assertTrue(item != null); - assertTrue(item.onclick != null); - item.onclick(); -} - -// Tests. -function testSetBooleanPrefTriggers() { - // TODO(dtseng): make generic to click all buttons. - var showHomeButton = $('toolbarShowHomeButton'); - assertTrue(showHomeButton != null); - showHomeButton.click(); - showHomeButton.blur(); -} - -function testPageIsUnderTheHood() { - var pageInstance = AdvancedOptions.getInstance(); - var topPage = OptionsPage.getTopmostVisiblePage(); - var expectedTitle = pageInstance.title; - var actualTitle = document.title; - assertEquals("chrome://settings/advanced", document.location.href); - assertEquals(expectedTitle, actualTitle); - assertEquals(pageInstance, topPage); -} diff --git a/chrome/test/data/webui/test_api.js b/chrome/test/data/webui/test_api.js index c9dddc2..65e3f2f 100644 --- a/chrome/test/data/webui/test_api.js +++ b/chrome/test/data/webui/test_api.js @@ -2,13 +2,233 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Library providing basic test framework functionality. +/** + * @fileoverview Library providing basic test framework functionality. + **/ + +/** + * Namespace for |Test|. + * @type {Object} + **/ +var testing = {}; + +/** + * Hold the currentTestCase across between PreLoad and Run. + * @type {TestCase} + **/ +var currentTestCase = null; (function() { + // Provide global objects for generation case. + if (this['window'] === undefined) + this['window'] = this; + if (this['chrome'] === undefined) { + this['chrome'] = { + send: function() {}, + }; + } + if (this['console'] === undefined) { + this['console'] = { + log: print, + }; + } + + /** + * This class will be exported as testing.Test, and is provided to hold the + * fixture's configuration and callback methods for the various phases of + * invoking a test. It is called "Test" rather than TestFixture to roughly + * mimic the gtest's class names. + * @constructor + **/ + function Test() {} + + Test.prototype = { + /** + * The name of the test. + **/ + name: null, + + /** + * When set to a string value representing a url, generate BrowsePreload + * call, which will browse to the url and call fixture.PreLoad of the + * currentTestCase. + * @type {String} + **/ + browsePreload: null, + + /** + * When set to a string value representing an html page in the test + * directory, generate BrowsePrintPreload call, which will browse to a url + * representing the file, cause print, and call fixture.PreLoad of the + * currentTestCase. + * @type {String} + **/ + browsePrintPreload: null, + + /** + * When set to a function, will be called in the context of the test + * generation inside the function, and before any generated C++. + * @type {function(string,string)} + **/ + testGenPreamble: null, + + /** + * When set to a function, will be called in the context of the test + * generation inside the function, and before any generated C++. + * @type {function(string,string)} + **/ + testGenPostamble: null, + + /** + * When set to a non-null String, auto-generate typedef before generating + * TEST*: {@code typedef typedefCppFixture testFixture}. + * @type {String} + **/ + typedefCppFixture: 'WebUIBrowserTest', + + /** + * This should be initialized by the test fixture and can be referenced + * during the test run. + * @type {Mock4JS.Mock} + **/ + mockHandler: null, + + /** + * Override this method to perform initialization during preload (such as + * creating mocks and registering handlers). + * @type {Function} + **/ + PreLoad: function() {}, + + /** + * Override this method to perform tasks before running your test. + * @type {Function} + **/ + SetUp: function() {}, + + /** + * Override this method to perform tasks after running your test. If you + * create a mock class, you must call Mock4JS.verifyAllMocks() in this + * phase. + * @type {Function} + **/ + TearDown: function() { + Mock4JS.verifyAllMocks(); + } + }; + + /** + * This class is not exported and is available to hold the state of the + * |currentTestCase| throughout preload and test run. + * @param {String} name The name of the test case. + * @param {Test} fixture The fixture object for this test case. + * @param {Function} body The code to run for the test. + * @constructor + **/ + function TestCase(name, fixture, body) { + this.name = name; + this.fixture = fixture; + this.body = body; + } + + TestCase.prototype = { + name: null, + fixture: null, + body: null, + + /** + * Called at preload time, proxies to the fixture. + * @type {Function} + **/ + PreLoad: function(name) { + if (this.fixture) + this.fixture.PreLoad(); + }, + + /** + * Runs this test case. + * @type {Function} + **/ + Run: function() { + if (this.fixture) + this.fixture.SetUp(); + if (this.body) + this.body.call(this.fixture); + if (this.fixture) + this.fixture.TearDown(); + }, + }; + + /** + * Registry of javascript-defined callbacks for {@code chrome.send}. + * @type {Object} + **/ + var sendCallbacks = {}; + + /** + * Registers the message, object and callback for {@code chrome.send} + * @param {String} name The name of the message to route to this |callback|. + * @param {Object} messageHAndler Pass as |this| when calling the |callback|. + * @param {function(...)} callback Called by {@code chrome.send}. + * @see sendCallbacks + **/ + function registerMessageCallback(name, messageHandler, callback) { + sendCallbacks[name] = [messageHandler, callback]; + } + + /** + * Register all methods of {@code mockClass.prototype} with messages of the + * same name as the method, using the proxy of the |mockObject| as the + * |messageHandler| when registering. + * @param {Mock4JS.Mock} mockObject The mock to register callbacks against. + * @param {function(new:Object)} mockClAss Constructor for the mocked class. + * @see registerMessageCallback + **/ + function registerMockMessageCallbacks(mockObject, mockClass) { + var mockProxy = mockObject.proxy(); + for (func in mockClass.prototype) { + if (typeof(mockClass.prototype[func]) == 'function') { + registerMessageCallback(func, + mockProxy, + mockProxy[func]); + } + } + } + + /** + * Holds the old chrome object when overriding for preload and registry of + * handlers. + * @type {Object} + **/ + var oldChrome = chrome; + + /** + * Overrides {@code chrome.send} for routing messages to javascript + * functions. Also fallsback to sending with the |oldChrome| object. + * @param {String} messageName The message to route. + * @see oldChrome + **/ + function send(messageName) { + var callback = sendCallbacks[messageName]; + var args = Array.prototype.slice.call(arguments, 1); + if (callback != undefined) + callback[1].apply(callback[0], args); + else + oldChrome.send.apply(oldChrome, args); + } + // Asserts. // Use the following assertions to verify a condition within a test. // If assertion fails, the C++ backend will be immediately notified. // If assertion passes, no notification will be sent to the C++ backend. + + /** + * When |test| !== |expected|, aborts the current test. + * @param {Boolean} test The predicate to check against |expected|. + * @param {Boolean} expected The expected value of |test|. + * @param {String=} message The message to include in the Error thrown. + * @throws {Error} upon failure. + **/ function assertBool(test, expected, message) { if (test !== expected) { if (message) @@ -19,30 +239,33 @@ } } - var old_chrome = chrome; - var send_callbacks = {}; - - function registerMessageCallback(name, object, callback) { - send_callbacks[name] = [object, callback]; - } - - function send(messageName) { - var callback = send_callbacks[messageName]; - var args = Array.prototype.slice.call(arguments, 1); - if (callback != undefined) - callback[1].apply(callback[0], args); - else - old_chrome.send.apply(old_chrome, args); - } - + /** + * When |test| !== true, aborts the current test. + * @param {Boolean} test The predicate to check against |expected|. + * @param {String=} message The message to include in the Error thrown. + * @throws {Error} upon failure. + **/ function assertTrue(test, message) { assertBool(test, true, message); } + /** + * When |test| !== false, aborts the current test. + * @param {Boolean} test The predicate to check against |expected|. + * @param {String=} message The message to include in the Error thrown. + * @throws {Error} upon failure. + **/ function assertFalse(test, message) { assertBool(test, false, message); } + /** + * When |expected| !== |actual|, aborts the current test. + * @param {*} expected The predicate to check against |expected|. + * @param {*} actual The expected value of |test|. + * @param {String=} message The message to include in the Error thrown. + * @throws {Error} upon failure. + **/ function assertEquals(expected, actual, message) { if (expected != actual) { throw new Error('Test Error. Actual: ' + actual + '\nExpected: ' + @@ -55,56 +278,220 @@ } } + /** + * Always aborts the current test. + * @param {String=} message The message to include in the Error thrown. + * @throws {Error} always. + **/ function assertNotReached(message) { throw new Error(message); } + /** + * Holds the errors, if any, caught by expects so that the test case can fail. + * @type {Array.<Error>} + **/ var errors = []; + /** + * Creates a function based upon a function that thows an exception on + * failure. The new function stuffs any errors into the |errors| array for + * checking by runTest. This allows tests to continue running other checks, + * while failing the overal test if any errors occurrred. + * @param {Function} assertFunc The function which may throw an Error. + * @return {Function} A function that applies its arguments to |assertFunc|. + * @see errors + * @see runTest + **/ function createExpect(assertFunc) { return function() { try { assertFunc.apply(null, arguments); } catch (e) { - console.log('Failed: ' + currentTest.name + '\n' + e.stack); errors.push(e); } }; } + /** + * This is the starting point for tests run by WebUIBrowserTest. It clears + * |errors|, runs the test surrounded by an expect to catch Errors. If + * |errors| is non-empty, it reports a failure and a message by joining + * |errors|. + * @param {String} testFunction The function name to call. + * @param {Array} testArguments The arguments to call |testFunction| with. + * @return {Array.<Boolean, String>} [test-succeeded, message-if-failed] + * @see errors + * @see createExpect + **/ function runTest(testFunction, testArguments) { errors = []; // Avoid eval() if at all possible, since it will not work on pages // that have enabled content-security-policy. - currentTest = this[testFunction]; // global object -- not a method. - if (typeof currentTest === "undefined") { - currentTest = eval(testFunction); - } - console.log('Running test ' + currentTest.name); - createExpect(currentTest).apply(null, testArguments); + var testBody = this[testFunction]; // global object -- not a method. + if (typeof testBody === "undefined") + testBody = eval(testFunction); + if (testBody != RUN_TEST_F) + console.log('Running test ' + testBody.name); + createExpect(testBody).apply(null, testArguments); if (errors.length) { + for (var i = 0; i < errors.length; ++i) { + console.log('Failed: ' + testFunction + '(' + + testArguments.toString() + ')\n' + errors[i].stack); + } return [false, errors.join('\n')]; + } else { + return [true]; } + } - return [true]; + /** + * Creates a new test case for the given |testFixture| and |testName|. Assumes + * |testFixture| describes a globally available subclass of type Test. + * @param {String} testFixture The fixture for this test case. + * @param {String} testName The name for this test case. + * @return {TestCase} A newly created TestCase. + **/ + function createTestCase(testFixture, testName) { + var fixtureConstructor = this[testFixture]; + var testBody = fixtureConstructor.testCaseBodies[testName]; + var fixture = new fixtureConstructor(); + fixture.name = testFixture; + return new TestCase(testName, fixture, testBody); } - function preloadJavascriptLibraries(overload_chrome_send) { - if (overload_chrome_send) - chrome = { 'send': send }; + /** + * Used by WebUIBrowserTest to preload the javascript libraries at the + * appropriate time for javascript injection into the current page. This + * creates a test case and calls its PreLoad for any early initialization such + * as registering handlers before the page's javascript runs it's OnLoad + * method. + * @param {String} testFixture The test fixture name. + * @param {String} testName The test name. + **/ + function preloadJavascriptLibraries(testFixture, testName) { + chrome = { + __proto__: oldChrome, + send: send, + }; + currentTestCase = createTestCase(testFixture, testName); + currentTestCase.PreLoad(); + } + + /** + * During generation phase, this outputs; do nothing at runtime. + **/ + function GEN() {} + + /** + * At runtime, register the testName with a test fixture. Since this method + * doesn't have a test fixture, we create a dummy fixture to hold its |name| + * and |testCaseBodies|. + * @param {String} testCaseName The name of the test case. + * @param {String} testName The name of the test function. + * @param {Function} testBody The body to execute when running this test. + **/ + function TEST(testCaseName, testName, testBody) { + var fixtureConstructor = this[testCaseName]; + if (fixtureConstructor === undefined) { + fixtureConstructor = function() {}; + this[testCaseName] = fixtureConstructor; + fixtureConstructor.prototype = { + __proto__: Test.prototype, + name: testCaseName, + }; + fixtureConstructor.testCaseBodies = {}; + } + fixtureConstructor.testCaseBodies[testName] = testBody; + } + + /** + * At runtime, register the testName with its fixture. Stuff the |name| into + * the |testFixture|'s prototype, if needed, and the |testCaseBodies| into its + * constructor. + * @param {String} testFixture The name of the test fixture class. + * @param {String} testName The name of the test function. + * @param {Function} testBody The body to execute when running this test. + **/ + function TEST_F(testFixture, testName, testBody) { + var fixtureConstructor = this[testFixture]; + if (!fixtureConstructor.prototype.name) + fixtureConstructor.prototype.name = testFixture; + if (fixtureConstructor['testCaseBodies'] === undefined) + fixtureConstructor.testCaseBodies = {}; + fixtureConstructor.testCaseBodies[testName] = testBody; + } + + /** + * RunJavascriptTestF uses this as the |testFunction| when invoking + * runTest. If |currentTestCase| is non-null at this point, verify that + * |testFixture| and |testName| agree with the preloaded values. Create + * |currentTestCase|, if needed, run it, and clear the |currentTestCase|. + * @param {String} testFixture The name of the test fixture class. + * @param {String} testName The name of the test function. + * @see preloadJavascriptLibraries + * @see runTest + **/ + function RUN_TEST_F(testFixture, testName) { + if (!currentTestCase) + currentTestCase = createTestCase(testFixture, testName); + assertEquals(currentTestCase.name, testName); + assertEquals(currentTestCase.fixture.name, testFixture); + console.log('Running TestCase ' + testFixture + '.' + testName); + currentTestCase.Run(); + currentTestCase = null; + } + + /** + * CallFunctionAction is provided to allow mocks to have side effects. + * @param {Function} func The function to call. + * @param {Array} args Any arguments to pass to func. + * @constructor + **/ + function CallFunctionAction(func, args) { + this._func = func; + this._args = args; + } + + CallFunctionAction.prototype = { + invoke: function() { + return this._func.apply(null, this._args); + }, + describe: function() { + return 'calls the given function with arguments ' + this._args; + } + }; + + /** + * Syntactic sugar for will() on a Mock4JS.Mock. + * @param {Function} func the function to call when the method is invoked. + * @param {...*} var_args arguments to pass when calling func. + **/ + function callFunction(func) { + return new CallFunctionAction(func, + Array.prototype.slice.call(arguments, 1)); } // Exports. + testing.Test = Test; window.assertTrue = assertTrue; window.assertFalse = assertFalse; window.assertEquals = assertEquals; window.assertNotReached = assertNotReached; + window.callFunction = callFunction; window.expectTrue = createExpect(assertTrue); window.expectFalse = createExpect(assertFalse); window.expectEquals = createExpect(assertEquals); window.expectNotReached = createExpect(assertNotReached); window.registerMessageCallback = registerMessageCallback; + window.registerMockMessageCallbacks = registerMockMessageCallbacks; window.runTest = runTest; window.preloadJavascriptLibraries = preloadJavascriptLibraries; + window.TEST = TEST; + window.TEST_F = TEST_F; + window.GEN = GEN; + + // Import the Mock4JS helpers. + Mock4JS.addMockSupport(window); })(); diff --git a/chrome/test/test_navigation_observer.cc b/chrome/test/test_navigation_observer.cc new file mode 100644 index 0000000..b7bcc74 --- /dev/null +++ b/chrome/test/test_navigation_observer.cc @@ -0,0 +1,83 @@ +// 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 "chrome/test/test_navigation_observer.h" + +#include "chrome/test/ui_test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" + +TestNavigationObserver::JsInjectionReadyObserver::JsInjectionReadyObserver() { +} + +TestNavigationObserver::JsInjectionReadyObserver::~JsInjectionReadyObserver() { +} + +TestNavigationObserver::TestNavigationObserver( + NavigationController* controller, + TestNavigationObserver::JsInjectionReadyObserver* + js_injection_ready_observer, + int number_of_navigations) + : navigation_started_(false), + navigation_entry_committed_(false), + navigations_completed_(0), + number_of_navigations_(number_of_navigations), + js_injection_ready_observer_(js_injection_ready_observer), + done_(false), + running_(false) { + RegisterAsObserver(controller); +} + +TestNavigationObserver::TestNavigationObserver( + TestNavigationObserver::JsInjectionReadyObserver* + js_injection_ready_observer, + int number_of_navigations) + : navigation_started_(false), + navigations_completed_(0), + number_of_navigations_(number_of_navigations), + js_injection_ready_observer_(js_injection_ready_observer), + done_(false), + running_(false) { +} + +TestNavigationObserver::~TestNavigationObserver() { +} + +void TestNavigationObserver::RegisterAsObserver( + NavigationController* controller) { + registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED, + Source<NavigationController>(controller)); + registrar_.Add(this, content::NOTIFICATION_LOAD_START, + Source<NavigationController>(controller)); + registrar_.Add(this, content::NOTIFICATION_LOAD_STOP, + Source<NavigationController>(controller)); +} + +void TestNavigationObserver::WaitForObservation() { + if (!done_) { + EXPECT_FALSE(running_); + running_ = true; + ui_test_utils::RunMessageLoop(); + } +} + +void TestNavigationObserver::Observe( + int type, const NotificationSource& source, + const NotificationDetails& details) { + if (type == content::NOTIFICATION_NAV_ENTRY_COMMITTED) { + if (!navigation_entry_committed_ && js_injection_ready_observer_) + js_injection_ready_observer_->OnJsInjectionReady(); + navigation_started_ = true; + navigation_entry_committed_ = true; + } else if (type == content::NOTIFICATION_LOAD_START) { + navigation_started_ = true; + } else if (type == content::NOTIFICATION_LOAD_STOP) { + if (navigation_started_ && + ++navigations_completed_ == number_of_navigations_) { + navigation_started_ = false; + done_ = true; + if (running_) + MessageLoopForUI::current()->Quit(); + } + } +} diff --git a/chrome/test/test_navigation_observer.h b/chrome/test/test_navigation_observer.h new file mode 100644 index 0000000..ba76ab5 --- /dev/null +++ b/chrome/test/test_navigation_observer.h @@ -0,0 +1,90 @@ +// 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. + +#ifndef CHROME_TEST_TEST_NAVIGATION_OBSERVER_H_ +#define CHROME_TEST_TEST_NAVIGATION_OBSERVER_H_ +#pragma once + +#include "base/compiler_specific.h" +#include "content/common/notification_observer.h" +#include "content/common/notification_registrar.h" + +class NavigationController; + +// In order to support testing of print preview, we need to wait for the tab to +// be inserted, and then observe notifications on the newly added tab's +// controller to wait for it to be loaded. To support tests registering +// javascript WebUI handlers, we need to inject the framework & registration +// javascript before the webui page loads by calling back through the +// TestTabStripModelObserver::LoadStartObserver when the new page starts +// loading. +class TestNavigationObserver : public NotificationObserver { + public: + class JsInjectionReadyObserver { + public: + JsInjectionReadyObserver(); + virtual ~JsInjectionReadyObserver(); + + // Called to indicate page entry committed and ready for javascript + // injection. + virtual void OnJsInjectionReady() = 0; + }; + + // Create and register a new TestNavigationObserver against the + // |controller|. When |js_injection_ready_observer| is non-null, notify with + // OnEntryCommitted() after |number_of_navigations| navigations. + // Note: |js_injection_ready_observer| is owned by the caller and should be + // valid until this class is destroyed. + TestNavigationObserver(NavigationController* controller, + JsInjectionReadyObserver* js_injection_ready_observer, + int number_of_navigations); + + virtual ~TestNavigationObserver(); + + // Run the UI message loop until |done_| becomes true. + void WaitForObservation(); + + protected: + // Note: |js_injection_ready_observer| is owned by the caller and should be + // valid until this class is destroyed. + explicit TestNavigationObserver( + JsInjectionReadyObserver* js_injection_ready_observer, + int number_of_navigations); + + // Register this TestNavigationObserver as an observer of the |controller|. + void RegisterAsObserver(NavigationController* controller); + + private: + // NotificationObserver: + virtual void Observe(int type, const NotificationSource& source, + const NotificationDetails& details) OVERRIDE; + + NotificationRegistrar registrar_; + + // If true the navigation has started. + bool navigation_started_; + + // If true the navigation has been committed. + bool navigation_entry_committed_; + + // The number of navigations that have been completed. + int navigations_completed_; + + // The number of navigations to wait for. + int number_of_navigations_; + + // Observer to take some action when the page is ready for javascript + // injection. + JsInjectionReadyObserver* js_injection_ready_observer_; + + // |done_| will get set when this object observes a TabStripModel event. + bool done_; + + // |running_| will be true during WaitForObservation until |done_| is true. + bool running_; + + DISALLOW_COPY_AND_ASSIGN(TestNavigationObserver); +}; + +#endif // CHROME_TEST_TEST_NAVIGATION_OBSERVER_H_ diff --git a/chrome/test/test_tab_strip_model_observer.cc b/chrome/test/test_tab_strip_model_observer.cc index 9a209c6..013e2ef 100644 --- a/chrome/test/test_tab_strip_model_observer.cc +++ b/chrome/test/test_tab_strip_model_observer.cc @@ -6,25 +6,13 @@ #include "chrome/browser/tabs/tab_strip_model.h" #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" -#include "chrome/test/ui_test_utils.h" -#include "testing/gtest/include/gtest/gtest.h" - -TestTabStripModelObserver::LoadStartObserver::LoadStartObserver() { -} - -TestTabStripModelObserver::LoadStartObserver::~LoadStartObserver() { -} TestTabStripModelObserver::TestTabStripModelObserver( TabStripModel* tab_strip_model, - TestTabStripModelObserver::LoadStartObserver* load_start_observer) - : navigation_started_(false), - navigations_completed_(0), - number_of_navigations_(1), - tab_strip_model_(tab_strip_model), - load_start_observer_(load_start_observer), - done_(false), - running_(false) { + TestTabStripModelObserver::JsInjectionReadyObserver* + js_injection_ready_observer) + : TestNavigationObserver(js_injection_ready_observer, 1), + tab_strip_model_(tab_strip_model) { tab_strip_model_->AddObserver(this); } @@ -32,41 +20,7 @@ TestTabStripModelObserver::~TestTabStripModelObserver() { tab_strip_model_->RemoveObserver(this); } -void TestTabStripModelObserver::WaitForObservation() { - if (!done_) { - EXPECT_FALSE(running_); - running_ = true; - ui_test_utils::RunMessageLoop(); - } -} - void TestTabStripModelObserver::TabInsertedAt( TabContentsWrapper* contents, int index, bool foreground) { - NavigationController* controller = &contents->controller(); - registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED, - Source<NavigationController>(controller)); - registrar_.Add(this, content::NOTIFICATION_LOAD_START, - Source<NavigationController>(controller)); - registrar_.Add(this, content::NOTIFICATION_LOAD_STOP, - Source<NavigationController>(controller)); -} - -void TestTabStripModelObserver::Observe( - int type, const NotificationSource& source, - const NotificationDetails& details) { - if (type == content::NOTIFICATION_NAV_ENTRY_COMMITTED || - type == content::NOTIFICATION_LOAD_START) { - if (!navigation_started_) { - load_start_observer_->OnLoadStart(); - navigation_started_ = true; - } - } else if (type == content::NOTIFICATION_LOAD_STOP) { - if (navigation_started_ && - ++navigations_completed_ == number_of_navigations_) { - navigation_started_ = false; - done_ = true; - if (running_) - MessageLoopForUI::current()->Quit(); - } - } + RegisterAsObserver(&contents->controller()); } diff --git a/chrome/test/test_tab_strip_model_observer.h b/chrome/test/test_tab_strip_model_observer.h index c384e45..39890a9 100644 --- a/chrome/test/test_tab_strip_model_observer.h +++ b/chrome/test/test_tab_strip_model_observer.h @@ -8,8 +8,7 @@ #include "base/compiler_specific.h" #include "chrome/browser/tabs/tab_strip_model_observer.h" -#include "content/common/notification_observer.h" -#include "content/common/notification_registrar.h" +#include "chrome/test/test_navigation_observer.h" class TabStripModel; @@ -20,60 +19,25 @@ class TabStripModel; // javascript before the webui page loads by calling back through the // TestTabStripModelObserver::LoadStartObserver when the new page starts // loading. -class TestTabStripModelObserver : public TabStripModelObserver, - public NotificationObserver { +class TestTabStripModelObserver : public TestNavigationObserver, + public TabStripModelObserver { public: - class LoadStartObserver { - public: - LoadStartObserver(); - virtual ~LoadStartObserver(); - - // Called to indicate page load starting. - virtual void OnLoadStart() = 0; - }; - // Observe the |tab_strip_model|, which may not be NULL. If // |load_start_observer| is non-NULL, notify when the page load starts. - TestTabStripModelObserver(TabStripModel* tab_strip_model, - LoadStartObserver* load_start_observer); + TestTabStripModelObserver( + TabStripModel* tab_strip_model, + JsInjectionReadyObserver* js_injection_ready_observer); virtual ~TestTabStripModelObserver(); - // Run the UI message loop until |done_| becomes true. - void WaitForObservation(); - private: // TabStripModelObserver: virtual void TabInsertedAt(TabContentsWrapper* contents, int index, bool foreground) OVERRIDE; - // NotificationObserver: - virtual void Observe(int type, const NotificationSource& source, - const NotificationDetails& details) OVERRIDE; - - NotificationRegistrar registrar_; - - // If true the navigation has started. - bool navigation_started_; - - // The number of navigations that have been completed. - int navigations_completed_; - - // The number of navigations to wait for. - int number_of_navigations_; - // |tab_strip_model_| is the object this observes. The constructor will // register this as an observer, and the destructor will remove the observer. TabStripModel* tab_strip_model_; - // Observer to take some action when the page load starts. - LoadStartObserver* load_start_observer_; - - // |done_| will get set when this object observes a TabStripModel event. - bool done_; - - // |running_| will be true during WaitForObservation until |done_| is true. - bool running_; - DISALLOW_COPY_AND_ASSIGN(TestTabStripModelObserver); }; diff --git a/chrome/test/ui_test_utils.cc b/chrome/test/ui_test_utils.cc index 8ed4303..d8f8c75 100644 --- a/chrome/test/ui_test_utils.cc +++ b/chrome/test/ui_test_utils.cc @@ -35,6 +35,7 @@ #include "chrome/common/extensions/extension_action.h" #include "chrome/test/automation/javascript_execution_controller.h" #include "chrome/test/bookmark_load_observer.h" +#include "chrome/test/test_navigation_observer.h" #include "content/browser/renderer_host/render_process_host.h" #include "content/browser/renderer_host/render_view_host.h" #include "content/browser/tab_contents/navigation_controller.h" @@ -55,70 +56,6 @@ namespace ui_test_utils { namespace { -// Used to block until a navigation completes. -class NavigationNotificationObserver : public NotificationObserver { - public: - NavigationNotificationObserver(NavigationController* controller, - int number_of_navigations) - : navigation_started_(false), - navigations_completed_(0), - number_of_navigations_(number_of_navigations), - running_(false), - done_(false) { - registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED, - Source<NavigationController>(controller)); - registrar_.Add(this, content::NOTIFICATION_LOAD_START, - Source<NavigationController>(controller)); - registrar_.Add(this, content::NOTIFICATION_LOAD_STOP, - Source<NavigationController>(controller)); - } - - void Run() { - if (!done_) { - running_ = true; - RunMessageLoop(); - } - } - - virtual void Observe(int type, - const NotificationSource& source, - const NotificationDetails& details) { - if (type == content::NOTIFICATION_NAV_ENTRY_COMMITTED || - type == content::NOTIFICATION_LOAD_START) { - navigation_started_ = true; - } else if (type == content::NOTIFICATION_LOAD_STOP) { - if (navigation_started_ && - ++navigations_completed_ == number_of_navigations_) { - navigation_started_ = false; - done_ = true; - if (running_) - MessageLoopForUI::current()->Quit(); - } - } - } - - private: - NotificationRegistrar registrar_; - - // If true the navigation has started. - bool navigation_started_; - - // The number of navigations that have been completed. - int navigations_completed_; - - // The number of navigations to wait for. - int number_of_navigations_; - - // Calls to Observe() can happen early, before the user calls Run(), or - // after. When we've seen all the navigations we're looking for, we set - // done_ to true; then when Run() is called we'll never need to run the - // event loop. Also, we don't need to quit the event loop when we're - // done if we never had to start an event loop. - bool running_; - bool done_; - DISALLOW_COPY_AND_ASSIGN(NavigationNotificationObserver); -}; - class DOMOperationObserver : public NotificationObserver { public: explicit DOMOperationObserver(RenderViewHost* render_view_host) @@ -337,8 +274,8 @@ void WaitForNavigation(NavigationController* controller) { void WaitForNavigations(NavigationController* controller, int number_of_navigations) { - NavigationNotificationObserver observer(controller, number_of_navigations); - observer.Run(); + TestNavigationObserver observer(controller, NULL, number_of_navigations); + observer.WaitForObservation(); } void WaitForNewTab(Browser* browser) { @@ -404,9 +341,9 @@ static void NavigateToURLWithDispositionBlockUntilNavigationsComplete( int number_of_navigations, WindowOpenDisposition disposition, int browser_test_flags) { - NavigationNotificationObserver + TestNavigationObserver same_tab_observer(&browser->GetSelectedTabContents()->controller(), - number_of_navigations); + NULL, number_of_navigations); std::set<Browser*> initial_browsers; for (std::vector<Browser*>::const_iterator iter = BrowserList::begin(); @@ -438,7 +375,7 @@ static void NavigateToURLWithDispositionBlockUntilNavigationsComplete( tab_contents = browser->GetSelectedTabContents(); } if (disposition == CURRENT_TAB) { - same_tab_observer.Run(); + same_tab_observer.WaitForObservation(); return; } else if (tab_contents) { NavigationController* controller = &tab_contents->controller(); diff --git a/chrome/third_party/mock4js/LICENSE b/chrome/third_party/mock4js/LICENSE new file mode 100644 index 0000000..cb92f08 --- /dev/null +++ b/chrome/third_party/mock4js/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2009 by Tung Mac. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/chrome/third_party/mock4js/README.chromium b/chrome/third_party/mock4js/README.chromium new file mode 100644 index 0000000..42696a1 --- /dev/null +++ b/chrome/third_party/mock4js/README.chromium @@ -0,0 +1,8 @@ +Name: mock4js +Short Name: mock4js +URL: http://mock4js.sourceforge.net/ +Version: 0.2 + +Mock4JS is a dynamic-mock library for Javascript. It enables developers to write +tests that exercise and verify the interactions between the objects in the +application. Its syntax is very expressive and is closely based on jMock 1. diff --git a/chrome/third_party/mock4js/examples/PriceService.js b/chrome/third_party/mock4js/examples/PriceService.js new file mode 100644 index 0000000..f20aa93 --- /dev/null +++ b/chrome/third_party/mock4js/examples/PriceService.js @@ -0,0 +1,43 @@ +/**
+ * PriceCache
+ */
+function PriceCache() {
+}
+
+PriceCache.prototype = {
+ getCachedPrice: function(instrumentId) {
+ },
+ setCachedPrice: function(instrumentId, price) {
+ }
+}
+
+/**
+ * PriceFetcher
+ */
+function PriceFetcher() {
+}
+
+PriceFetcher.prototype = {
+ getPriceFromServer: function(instrumentId) {
+ }
+}
+
+
+/**
+ * PriceService
+ */
+function PriceService(priceFetcher, priceCache) {
+ this._priceFetcher = priceFetcher;
+ this._priceCache = priceCache;
+}
+
+PriceService.prototype = {
+ getPrice: function(instrumentId) {
+ var price = this._priceCache.getCachedPrice(instrumentId);
+ if(price==null) {
+ price = this._priceFetcher.getPriceFromServer(instrumentId);
+ this._priceCache.setCachedPrice(instrumentId, price);
+ }
+ return price;
+ }
+}
diff --git a/chrome/third_party/mock4js/examples/PriceServiceTest.html b/chrome/third_party/mock4js/examples/PriceServiceTest.html new file mode 100644 index 0000000..a535aef --- /dev/null +++ b/chrome/third_party/mock4js/examples/PriceServiceTest.html @@ -0,0 +1,60 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+"http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <title>Tests</title>
+ <link rel="stylesheet" type="text/css" href="jsunit/css/jsUnitStyle.css">
+ <script language="JavaScript" type="text/javascript" src="../../jsunit/app/jsUnitCore.js"></script>
+ <script language="JavaScript" type="text/javascript" src="../mock4js.js"></script>
+ <script language="JavaScript" type="text/javascript" src="PriceService.js"></script>
+ <script language="JavaScript" type="text/javascript">
+
+ Mock4JS.addMockSupport(this);
+
+ var mockPriceFetcher;
+ var mockPriceCache;
+ var priceService;
+
+ function setUp() {
+ Mock4JS.clearMocksToVerify();
+ mockPriceFetcher = mock(PriceFetcher);
+ mockPriceCache = mock(PriceCache);
+ priceService = new PriceService(mockPriceFetcher.proxy(), mockPriceCache.proxy());
+ }
+
+ function tearDown() {
+ Mock4JS.verifyAllMocks();
+ }
+
+ function testGetsPriceFromFetcherWhenPriceNotInCache() {
+ mockPriceCache.expects(once()).getCachedPrice("USDGBP").will(returnValue(null));
+ mockPriceFetcher.expects(once()).getPriceFromServer("USDGBP").will(returnValue(123.4));
+ mockPriceCache.expects(once()).setCachedPrice("USDGBP", 123.4);
+
+ var result = priceService.getPrice("USDGBP");
+
+ assertEquals("Should have returned price from server", 123.4, result);
+ }
+
+ function testDoesntGetsPriceFromFetcherWhenPriceAlreadyInCache() {
+ mockPriceCache.expects(once()).getCachedPrice("USDGBP").will(returnValue(123.4));
+ mockPriceCache.expects(never()).setCachedPrice();
+ mockPriceFetcher.expects(never()).getPriceFromServer("USDGBP");
+
+ var result = priceService.getPrice("USDGBP");
+
+ assertEquals("Should have returned price from cache", 123.4, result);
+ }
+
+ </script>
+ </head>
+
+ <body>
+ <h1>JsUnit Tests</h1>
+
+ <p>This page contains some JsUnit tests. To see them, take a look at the source.</p>
+ </body>
+</html>
+
diff --git a/chrome/third_party/mock4js/examples/Publisher.js b/chrome/third_party/mock4js/examples/Publisher.js new file mode 100644 index 0000000..7405a06 --- /dev/null +++ b/chrome/third_party/mock4js/examples/Publisher.js @@ -0,0 +1,29 @@ +/**
+ * Subscriber
+ */
+function Subscriber() {
+}
+
+Subscriber.prototype = {
+ receive: function(message) {
+ }
+}
+
+/**
+ * Publisher
+ */
+function Publisher() {
+ this._subscribers = [];
+}
+
+Publisher.prototype = {
+ publish: function(message) {
+ for(var i=0; i<this._subscribers.length; i++) {
+ var subscriber = this._subscribers[i];
+ subscriber.receive(message);
+ }
+ },
+ add: function(subscriber) {
+ this._subscribers.push(subscriber);
+ }
+}
\ No newline at end of file diff --git a/chrome/third_party/mock4js/examples/PublisherTest.html b/chrome/third_party/mock4js/examples/PublisherTest.html new file mode 100644 index 0000000..6bda220 --- /dev/null +++ b/chrome/third_party/mock4js/examples/PublisherTest.html @@ -0,0 +1,47 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+"http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <title>Tests</title>
+ <link rel="stylesheet" type="text/css" href="jsunit/css/jsUnitStyle.css">
+ <script language="JavaScript" type="text/javascript" src="../../jsunit/app/jsUnitCore.js"></script>
+ <script language="JavaScript" type="text/javascript" src="..//mock4js.js"></script>
+ <script language="JavaScript" type="text/javascript" src="Publisher.js"></script>
+ <script language="JavaScript" type="text/javascript">
+
+ Mock4JS.addMockSupport(this);
+
+ function setUp() {
+ Mock4JS.clearMocksToVerify();
+ }
+
+ function tearDown() {
+ Mock4JS.verifyAllMocks();
+ }
+
+ function testOneSubscriberReceivesAMessage() {
+ // setup
+ var mockSubscriber = mock(Subscriber);
+ var publisher = new Publisher();
+ publisher.add(mockSubscriber.proxy());
+
+ var message = "message";
+
+ // expectations
+ mockSubscriber.expects(once()).receive(message);
+
+ // execute
+ publisher.publish(message);
+ }
+ </script>
+ </head>
+
+ <body>
+ <h1>JsUnit Tests</h1>
+
+ <p>This page contains some JsUnit tests. To see them, take a look at the source.</p>
+ </body>
+</html>
+
diff --git a/chrome/third_party/mock4js/mock4js.js b/chrome/third_party/mock4js/mock4js.js new file mode 100644 index 0000000..933e0a6 --- /dev/null +++ b/chrome/third_party/mock4js/mock4js.js @@ -0,0 +1,625 @@ +/**
+ * Mock4JS 0.2
+ * http://mock4js.sourceforge.net/
+ */
+
+Mock4JS = {
+ _mocksToVerify: [],
+ _convertToConstraint: function(constraintOrValue) {
+ if(constraintOrValue.argumentMatches) {
+ return constraintOrValue; // it's already an ArgumentMatcher
+ } else {
+ return new MatchExactly(constraintOrValue); // default to eq(...)
+ }
+ },
+ addMockSupport: function(object) {
+ // mock creation
+ object.mock = function(mockedType) {
+ if(!mockedType) {
+ throw new Mock4JSException("Cannot create mock: type to mock cannot be found or is null");
+ }
+ var newMock = new Mock(mockedType);
+ Mock4JS._mocksToVerify.push(newMock);
+ return newMock;
+ }
+
+ // syntactic sugar for expects()
+ object.once = function() {
+ return new CallCounter(1);
+ }
+ object.never = function() {
+ return new CallCounter(0);
+ }
+ object.exactly = function(expectedCallCount) {
+ return new CallCounter(expectedCallCount);
+ }
+ object.atLeastOnce = function() {
+ return new InvokeAtLeastOnce();
+ }
+
+ // syntactic sugar for argument expectations
+ object.ANYTHING = new MatchAnything();
+ object.NOT_NULL = new MatchAnythingBut(new MatchExactly(null));
+ object.NOT_UNDEFINED = new MatchAnythingBut(new MatchExactly(undefined));
+ object.eq = function(expectedValue) {
+ return new MatchExactly(expectedValue);
+ }
+ object.not = function(valueNotExpected) {
+ var argConstraint = Mock4JS._convertToConstraint(valueNotExpected);
+ return new MatchAnythingBut(argConstraint);
+ }
+ object.and = function() {
+ var constraints = [];
+ for(var i=0; i<arguments.length; i++) {
+ constraints[i] = Mock4JS._convertToConstraint(arguments[i]);
+ }
+ return new MatchAllOf(constraints);
+ }
+ object.or = function() {
+ var constraints = [];
+ for(var i=0; i<arguments.length; i++) {
+ constraints[i] = Mock4JS._convertToConstraint(arguments[i]);
+ }
+ return new MatchAnyOf(constraints);
+ }
+ object.stringContains = function(substring) {
+ return new MatchStringContaining(substring);
+ }
+
+ // syntactic sugar for will()
+ object.returnValue = function(value) {
+ return new ReturnValueAction(value);
+ }
+ object.throwException = function(exception) {
+ return new ThrowExceptionAction(exception);
+ }
+ },
+ clearMocksToVerify: function() {
+ Mock4JS._mocksToVerify = [];
+ },
+ verifyAllMocks: function() {
+ for(var i=0; i<Mock4JS._mocksToVerify.length; i++) {
+ Mock4JS._mocksToVerify[i].verify();
+ }
+ }
+}
+
+Mock4JSUtil = {
+ hasFunction: function(obj, methodName) {
+ return typeof obj == 'object' && typeof obj[methodName] == 'function';
+ },
+ join: function(list) {
+ var result = "";
+ for(var i=0; i<list.length; i++) {
+ var item = list[i];
+ if(Mock4JSUtil.hasFunction(item, "describe")) {
+ result += item.describe();
+ }
+ else if(typeof list[i] == 'string') {
+ result += "\""+list[i]+"\"";
+ } else {
+ result += list[i];
+ }
+
+ if(i<list.length-1) result += ", ";
+ }
+ return result;
+ }
+}
+
+Mock4JSException = function(message) {
+ this.message = message;
+}
+
+Mock4JSException.prototype = {
+ toString: function() {
+ return this.message;
+ }
+}
+
+/**
+ * Assert function that makes use of the constraint methods
+ */
+assertThat = function(expected, argumentMatcher) {
+ if(!argumentMatcher.argumentMatches(expected)) {
+ fail("Expected '"+expected+"' to be "+argumentMatcher.describe());
+ }
+}
+
+/**
+ * CallCounter
+ */
+function CallCounter(expectedCount) {
+ this._expectedCallCount = expectedCount;
+ this._actualCallCount = 0;
+}
+
+CallCounter.prototype = {
+ addActualCall: function() {
+ this._actualCallCount++;
+ if(this._actualCallCount > this._expectedCallCount) {
+ throw new Mock4JSException("unexpected invocation");
+ }
+ },
+
+ verify: function() {
+ if(this._actualCallCount < this._expectedCallCount) {
+ throw new Mock4JSException("expected method was not invoked the expected number of times");
+ }
+ },
+
+ describe: function() {
+ if(this._expectedCallCount == 0) {
+ return "not expected";
+ } else if(this._expectedCallCount == 1) {
+ var msg = "expected once";
+ if(this._actualCallCount >= 1) {
+ msg += " and has been invoked";
+ }
+ return msg;
+ } else {
+ var msg = "expected "+this._expectedCallCount+" times";
+ if(this._actualCallCount > 0) {
+ msg += ", invoked "+this._actualCallCount + " times";
+ }
+ return msg;
+ }
+ }
+}
+
+function InvokeAtLeastOnce() {
+ this._hasBeenInvoked = false;
+}
+
+InvokeAtLeastOnce.prototype = {
+ addActualCall: function() {
+ this._hasBeenInvoked = true;
+ },
+
+ verify: function() {
+ if(this._hasBeenInvoked === false) {
+ throw new Mock4JSException(describe());
+ }
+ },
+
+ describe: function() {
+ var desc = "expected at least once";
+ if(this._hasBeenInvoked) desc+=" and has been invoked";
+ return desc;
+ }
+}
+
+/**
+ * ArgumentMatchers
+ */
+
+function MatchExactly(expectedValue) {
+ this._expectedValue = expectedValue;
+}
+
+MatchExactly.prototype = {
+ argumentMatches: function(actualArgument) {
+ if(this._expectedValue instanceof Array) {
+ if(!(actualArgument instanceof Array)) return false;
+ if(this._expectedValue.length != actualArgument.length) return false;
+ for(var i=0; i<this._expectedValue.length; i++) {
+ if(this._expectedValue[i] != actualArgument[i]) return false;
+ }
+ return true;
+ } else {
+ return this._expectedValue == actualArgument;
+ }
+ },
+ describe: function() {
+ if(typeof this._expectedValue == "string") {
+ return "eq(\""+this._expectedValue+"\")";
+ } else {
+ return "eq("+this._expectedValue+")";
+ }
+ }
+}
+
+function MatchAnything() {
+}
+
+MatchAnything.prototype = {
+ argumentMatches: function(actualArgument) {
+ return true;
+ },
+ describe: function() {
+ return "ANYTHING";
+ }
+}
+
+function MatchAnythingBut(matcherToNotMatch) {
+ this._matcherToNotMatch = matcherToNotMatch;
+}
+
+MatchAnythingBut.prototype = {
+ argumentMatches: function(actualArgument) {
+ return !this._matcherToNotMatch.argumentMatches(actualArgument);
+ },
+ describe: function() {
+ return "not("+this._matcherToNotMatch.describe()+")";
+ }
+}
+
+function MatchAllOf(constraints) {
+ this._constraints = constraints;
+}
+
+
+MatchAllOf.prototype = {
+ argumentMatches: function(actualArgument) {
+ for(var i=0; i<this._constraints.length; i++) {
+ var constraint = this._constraints[i];
+ if(!constraint.argumentMatches(actualArgument)) return false;
+ }
+ return true;
+ },
+ describe: function() {
+ return "and("+Mock4JSUtil.join(this._constraints)+")";
+ }
+}
+
+function MatchAnyOf(constraints) {
+ this._constraints = constraints;
+}
+
+MatchAnyOf.prototype = {
+ argumentMatches: function(actualArgument) {
+ for(var i=0; i<this._constraints.length; i++) {
+ var constraint = this._constraints[i];
+ if(constraint.argumentMatches(actualArgument)) return true;
+ }
+ return false;
+ },
+ describe: function() {
+ return "or("+Mock4JSUtil.join(this._constraints)+")";
+ }
+}
+
+
+function MatchStringContaining(stringToLookFor) {
+ this._stringToLookFor = stringToLookFor;
+}
+
+MatchStringContaining.prototype = {
+ argumentMatches: function(actualArgument) {
+ if(typeof actualArgument != 'string') throw new Mock4JSException("stringContains() must be given a string, actually got a "+(typeof actualArgument));
+ return (actualArgument.indexOf(this._stringToLookFor) != -1);
+ },
+ describe: function() {
+ return "a string containing \""+this._stringToLookFor+"\"";
+ }
+}
+
+
+/**
+ * StubInvocation
+ */
+function StubInvocation(expectedMethodName, expectedArgs, actionSequence) {
+ this._expectedMethodName = expectedMethodName;
+ this._expectedArgs = expectedArgs;
+ this._actionSequence = actionSequence;
+}
+
+StubInvocation.prototype = {
+ matches: function(invokedMethodName, invokedMethodArgs) {
+ if (invokedMethodName != this._expectedMethodName) {
+ return false;
+ }
+
+ if (invokedMethodArgs.length != this._expectedArgs.length) {
+ return false;
+ }
+
+ for(var i=0; i<invokedMethodArgs.length; i++) {
+ var expectedArg = this._expectedArgs[i];
+ var invokedArg = invokedMethodArgs[i];
+ if(!expectedArg.argumentMatches(invokedArg)) {
+ return false;
+ }
+ }
+
+ return true;
+ },
+
+ invoked: function() {
+ try {
+ return this._actionSequence.invokeNextAction();
+ } catch(e) {
+ if(e instanceof Mock4JSException) {
+ throw new Mock4JSException(this.describeInvocationNameAndArgs()+" - "+e.message);
+ } else {
+ throw e;
+ }
+ }
+ },
+
+ will: function() {
+ this._actionSequence.addAll.apply(this._actionSequence, arguments);
+ },
+
+ describeInvocationNameAndArgs: function() {
+ return this._expectedMethodName+"("+Mock4JSUtil.join(this._expectedArgs)+")";
+ },
+
+ describe: function() {
+ return "stub: "+this.describeInvocationNameAndArgs();
+ },
+
+ verify: function() {
+ }
+}
+
+/**
+ * ExpectedInvocation
+ */
+function ExpectedInvocation(expectedMethodName, expectedArgs, expectedCallCounter) {
+ this._stubInvocation = new StubInvocation(expectedMethodName, expectedArgs, new ActionSequence());
+ this._expectedCallCounter = expectedCallCounter;
+}
+
+ExpectedInvocation.prototype = {
+ matches: function(invokedMethodName, invokedMethodArgs) {
+ try {
+ return this._stubInvocation.matches(invokedMethodName, invokedMethodArgs);
+ } catch(e) {
+ throw new Mock4JSException("method "+this._stubInvocation.describeInvocationNameAndArgs()+": "+e.message);
+ }
+ },
+
+ invoked: function() {
+ try {
+ this._expectedCallCounter.addActualCall();
+ } catch(e) {
+ throw new Mock4JSException(e.message+": "+this._stubInvocation.describeInvocationNameAndArgs());
+ }
+ return this._stubInvocation.invoked();
+ },
+
+ will: function() {
+ this._stubInvocation.will.apply(this._stubInvocation, arguments);
+ },
+
+ describe: function() {
+ return this._expectedCallCounter.describe()+": "+this._stubInvocation.describeInvocationNameAndArgs();
+ },
+
+ verify: function() {
+ try {
+ this._expectedCallCounter.verify();
+ } catch(e) {
+ throw new Mock4JSException(e.message+": "+this._stubInvocation.describeInvocationNameAndArgs());
+ }
+ }
+}
+
+/**
+ * MethodActions
+ */
+function ReturnValueAction(valueToReturn) {
+ this._valueToReturn = valueToReturn;
+}
+
+ReturnValueAction.prototype = {
+ invoke: function() {
+ return this._valueToReturn;
+ },
+ describe: function() {
+ return "returns "+this._valueToReturn;
+ }
+}
+
+function ThrowExceptionAction(exceptionToThrow) {
+ this._exceptionToThrow = exceptionToThrow;
+}
+
+ThrowExceptionAction.prototype = {
+ invoke: function() {
+ throw this._exceptionToThrow;
+ },
+ describe: function() {
+ return "throws "+this._exceptionToThrow;
+ }
+}
+
+function ActionSequence() {
+ this._ACTIONS_NOT_SETUP = "_ACTIONS_NOT_SETUP";
+ this._actionSequence = this._ACTIONS_NOT_SETUP;
+ this._indexOfNextAction = 0;
+}
+
+ActionSequence.prototype = {
+ invokeNextAction: function() {
+ if(this._actionSequence === this._ACTIONS_NOT_SETUP) {
+ return;
+ } else {
+ if(this._indexOfNextAction >= this._actionSequence.length) {
+ throw new Mock4JSException("no more values to return");
+ } else {
+ var action = this._actionSequence[this._indexOfNextAction];
+ this._indexOfNextAction++;
+ return action.invoke();
+ }
+ }
+ },
+
+ addAll: function() {
+ this._actionSequence = [];
+ for(var i=0; i<arguments.length; i++) {
+ if(typeof arguments[i] != 'object' && arguments[i].invoke === undefined) {
+ throw new Error("cannot add a method action that does not have an invoke() method");
+ }
+ this._actionSequence.push(arguments[i]);
+ }
+ }
+}
+
+function StubActionSequence() {
+ this._ACTIONS_NOT_SETUP = "_ACTIONS_NOT_SETUP";
+ this._actionSequence = this._ACTIONS_NOT_SETUP;
+ this._indexOfNextAction = 0;
+}
+
+StubActionSequence.prototype = {
+ invokeNextAction: function() {
+ if(this._actionSequence === this._ACTIONS_NOT_SETUP) {
+ return;
+ } else if(this._actionSequence.length == 1) {
+ // if there is only one method action, keep doing that on every invocation
+ return this._actionSequence[0].invoke();
+ } else {
+ if(this._indexOfNextAction >= this._actionSequence.length) {
+ throw new Mock4JSException("no more values to return");
+ } else {
+ var action = this._actionSequence[this._indexOfNextAction];
+ this._indexOfNextAction++;
+ return action.invoke();
+ }
+ }
+ },
+
+ addAll: function() {
+ this._actionSequence = [];
+ for(var i=0; i<arguments.length; i++) {
+ if(typeof arguments[i] != 'object' && arguments[i].invoke === undefined) {
+ throw new Error("cannot add a method action that does not have an invoke() method");
+ }
+ this._actionSequence.push(arguments[i]);
+ }
+ }
+}
+
+
+/**
+ * Mock
+ */
+function Mock(mockedType) {
+ if(mockedType === undefined || mockedType.prototype === undefined) {
+ throw new Mock4JSException("Unable to create Mock: must create Mock using a class not prototype, eg. 'new Mock(TypeToMock)' or using the convenience method 'mock(TypeToMock)'");
+ }
+ this._mockedType = mockedType.prototype;
+ this._expectedCallCount;
+ this._isRecordingExpectations = false;
+ this._expectedInvocations = [];
+
+ // setup proxy
+ var IntermediateClass = new Function();
+ IntermediateClass.prototype = mockedType.prototype;
+ var ChildClass = new Function();
+ ChildClass.prototype = new IntermediateClass();
+ this._proxy = new ChildClass();
+ this._proxy.mock = this;
+
+ for(property in mockedType.prototype) {
+ if(this._isPublicMethod(mockedType.prototype, property)) {
+ var publicMethodName = property;
+ this._proxy[publicMethodName] = this._createMockedMethod(publicMethodName);
+ this[publicMethodName] = this._createExpectationRecordingMethod(publicMethodName);
+ }
+ }
+}
+
+Mock.prototype = {
+
+ proxy: function() {
+ return this._proxy;
+ },
+
+ expects: function(expectedCallCount) {
+ this._expectedCallCount = expectedCallCount;
+ this._isRecordingExpectations = true;
+ this._isRecordingStubs = false;
+ return this;
+ },
+
+ stubs: function() {
+ this._isRecordingExpectations = false;
+ this._isRecordingStubs = true;
+ return this;
+ },
+
+ verify: function() {
+ for(var i=0; i<this._expectedInvocations.length; i++) {
+ var expectedInvocation = this._expectedInvocations[i];
+ try {
+ expectedInvocation.verify();
+ } catch(e) {
+ var failMsg = e.message+this._describeMockSetup();
+ throw new Mock4JSException(failMsg);
+ }
+ }
+ },
+
+ _isPublicMethod: function(mockedType, property) {
+ try {
+ var isMethod = typeof(mockedType[property]) == 'function';
+ var isPublic = property.charAt(0) != "_";
+ return isMethod && isPublic;
+ } catch(e) {
+ return false;
+ }
+ },
+
+ _createExpectationRecordingMethod: function(methodName) {
+ return function() {
+ // ensure all arguments are instances of ArgumentMatcher
+ var expectedArgs = [];
+ for(var i=0; i<arguments.length; i++) {
+ if(arguments[i] !== null && arguments[i] !== undefined && arguments[i].argumentMatches) {
+ expectedArgs[i] = arguments[i];
+ } else {
+ expectedArgs[i] = new MatchExactly(arguments[i]);
+ }
+ }
+
+ // create stub or expected invocation
+ var expectedInvocation;
+ if(this._isRecordingExpectations) {
+ expectedInvocation = new ExpectedInvocation(methodName, expectedArgs, this._expectedCallCount);
+ } else {
+ expectedInvocation = new StubInvocation(methodName, expectedArgs, new StubActionSequence());
+ }
+
+ this._expectedInvocations.push(expectedInvocation);
+
+ this._isRecordingExpectations = false;
+ this._isRecordingStubs = false;
+ return expectedInvocation;
+ }
+ },
+
+ _createMockedMethod: function(methodName) {
+ return function() {
+ // go through expectation list backwards to ensure later expectations override earlier ones
+ for(var i=this.mock._expectedInvocations.length-1; i>=0; i--) {
+ var expectedInvocation = this.mock._expectedInvocations[i];
+ if(expectedInvocation.matches(methodName, arguments)) {
+ try {
+ return expectedInvocation.invoked();
+ } catch(e) {
+ if(e instanceof Mock4JSException) {
+ throw new Mock4JSException(e.message+this.mock._describeMockSetup());
+ } else {
+ // the user setup the mock to throw a specific error, so don't modify the message
+ throw e;
+ }
+ }
+ }
+ }
+ var failMsg = "unexpected invocation: "+methodName+"("+Mock4JSUtil.join(arguments)+")"+this.mock._describeMockSetup();
+ throw new Mock4JSException(failMsg);
+ };
+ },
+
+ _describeMockSetup: function() {
+ var msg = "\nAllowed:";
+ for(var i=0; i<this._expectedInvocations.length; i++) {
+ var expectedInvocation = this._expectedInvocations[i];
+ msg += "\n" + expectedInvocation.describe();
+ }
+ return msg;
+ }
+}
\ No newline at end of file diff --git a/tools/gypv8sh.py b/tools/gypv8sh.py index ed47649..edb1402 100644 --- a/tools/gypv8sh.py +++ b/tools/gypv8sh.py @@ -7,7 +7,8 @@ argument lists and to generate inlinable tests. Usage: - python tools/gypv8sh.py v8_shell js2webui.js inputfile outputfile + python tools/gypv8sh.py v8_shell mock.js test_api.js js2webui.js \ + inputfile outputfile """ try: @@ -21,17 +22,19 @@ import sys def main (): parser = optparse.OptionParser() - parser.set_usage("%prog v8_shell js2webui.js inputfile outputfile") + parser.set_usage( + "%prog v8_shell mock.js test_api.js js2webui.js inputfile outputfile") parser.add_option('-v', '--verbose', action='store_true') parser.add_option('-n', '--impotent', action='store_true', help="don't execute; just print (as if verbose)") (opts, args) = parser.parse_args() - if len(args) != 4: + if len(args) != 6: parser.error('all arguments are required.') - v8_shell, js2webui, inputfile, outputfile = args + v8_shell, mock_js, test_api, js2webui, inputfile, outputfile = args arguments = [js2webui, inputfile, os.path.basename(inputfile), outputfile] - cmd = [v8_shell, '-e', "arguments=" + json.dumps(arguments), js2webui] + cmd = [v8_shell, '-e', "arguments=" + json.dumps(arguments), mock_js, + test_api, js2webui] if opts.verbose or opts.impotent: print cmd if not opts.impotent: |