Home | History | Annotate | Download | only in debugger
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/command_line.h"
      6 #include "base/path_service.h"
      7 #include "base/string_util.h"
      8 #include "base/utf_string_conversions.h"
      9 #include "chrome/browser/debugger/devtools_client_host.h"
     10 #include "chrome/browser/debugger/devtools_manager.h"
     11 #include "chrome/browser/debugger/devtools_window.h"
     12 #include "chrome/browser/extensions/extension_host.h"
     13 #include "chrome/browser/extensions/extension_service.h"
     14 #include "chrome/browser/profiles/profile.h"
     15 #include "chrome/browser/ui/browser.h"
     16 #include "chrome/common/chrome_paths.h"
     17 #include "chrome/test/in_process_browser_test.h"
     18 #include "chrome/test/ui_test_utils.h"
     19 #include "content/browser/renderer_host/render_view_host.h"
     20 #include "content/browser/tab_contents/tab_contents.h"
     21 #include "content/common/notification_registrar.h"
     22 #include "content/common/notification_service.h"
     23 #include "net/test/test_server.h"
     24 
     25 namespace {
     26 
     27 // Used to block until a dev tools client window's browser is closed.
     28 class BrowserClosedObserver : public NotificationObserver {
     29  public:
     30   explicit BrowserClosedObserver(Browser* browser) {
     31     registrar_.Add(this, NotificationType::BROWSER_CLOSED,
     32                    Source<Browser>(browser));
     33     ui_test_utils::RunMessageLoop();
     34   }
     35 
     36   virtual void Observe(NotificationType type,
     37                        const NotificationSource& source,
     38                        const NotificationDetails& details) {
     39     MessageLoopForUI::current()->Quit();
     40   }
     41 
     42  private:
     43   NotificationRegistrar registrar_;
     44   DISALLOW_COPY_AND_ASSIGN(BrowserClosedObserver);
     45 };
     46 
     47 // The delay waited in some cases where we don't have a notifications for an
     48 // action we take.
     49 const int kActionDelayMs = 500;
     50 
     51 const char kDebuggerTestPage[] = "files/devtools/debugger_test_page.html";
     52 const char kHeapProfilerPage[] = "files/devtools/heap_profiler.html";
     53 const char kPauseWhenLoadingDevTools[] =
     54     "files/devtools/pause_when_loading_devtools.html";
     55 const char kPauseWhenScriptIsRunning[] =
     56     "files/devtools/pause_when_script_is_running.html";
     57 const char kPageWithContentScript[] =
     58     "files/devtools/page_with_content_script.html";
     59 
     60 
     61 class DevToolsSanityTest : public InProcessBrowserTest {
     62  public:
     63   DevToolsSanityTest() {
     64     set_show_window(true);
     65     EnableDOMAutomation();
     66   }
     67 
     68  protected:
     69   void RunTest(const std::string& test_name, const std::string& test_page) {
     70     OpenDevToolsWindow(test_page);
     71     std::string result;
     72 
     73     // At first check that JavaScript part of the front-end is loaded by
     74     // checking that global variable uiTests exists(it's created after all js
     75     // files have been loaded) and has runTest method.
     76     ASSERT_TRUE(
     77         ui_test_utils::ExecuteJavaScriptAndExtractString(
     78             client_contents_->render_view_host(),
     79             L"",
     80             L"window.domAutomationController.send("
     81             L"'' + (window.uiTests && (typeof uiTests.runTest)));",
     82             &result));
     83 
     84     if (result == "function") {
     85       ASSERT_TRUE(
     86           ui_test_utils::ExecuteJavaScriptAndExtractString(
     87               client_contents_->render_view_host(),
     88               L"",
     89               UTF8ToWide(StringPrintf("uiTests.runTest('%s')",
     90                                       test_name.c_str())),
     91               &result));
     92       EXPECT_EQ("[OK]", result);
     93     } else {
     94       FAIL() << "DevTools front-end is broken.";
     95     }
     96     CloseDevToolsWindow();
     97   }
     98 
     99   void OpenDevToolsWindow(const std::string& test_page) {
    100     ASSERT_TRUE(test_server()->Start());
    101     GURL url = test_server()->GetURL(test_page);
    102     ui_test_utils::NavigateToURL(browser(), url);
    103 
    104     inspected_rvh_ = GetInspectedTab()->render_view_host();
    105     DevToolsManager* devtools_manager = DevToolsManager::GetInstance();
    106     devtools_manager->OpenDevToolsWindow(inspected_rvh_);
    107 
    108     DevToolsClientHost* client_host =
    109         devtools_manager->GetDevToolsClientHostFor(inspected_rvh_);
    110     window_ = client_host->AsDevToolsWindow();
    111     RenderViewHost* client_rvh = window_->GetRenderViewHost();
    112     client_contents_ = client_rvh->delegate()->GetAsTabContents();
    113     ui_test_utils::WaitForNavigation(&client_contents_->controller());
    114   }
    115 
    116   TabContents* GetInspectedTab() {
    117     return browser()->GetTabContentsAt(0);
    118   }
    119 
    120   void CloseDevToolsWindow() {
    121     DevToolsManager* devtools_manager = DevToolsManager::GetInstance();
    122     // UnregisterDevToolsClientHostFor may destroy window_ so store the browser
    123     // first.
    124     Browser* browser = window_->browser();
    125     devtools_manager->UnregisterDevToolsClientHostFor(inspected_rvh_);
    126 
    127     // Wait only when DevToolsWindow has a browser. For docked DevTools, this
    128     // is NULL and we skip the wait.
    129     if (browser)
    130       BrowserClosedObserver close_observer(browser);
    131   }
    132 
    133   TabContents* client_contents_;
    134   DevToolsWindow* window_;
    135   RenderViewHost* inspected_rvh_;
    136 };
    137 
    138 
    139 class CancelableQuitTask : public Task {
    140  public:
    141   explicit CancelableQuitTask(const std::string& timeout_message)
    142       : timeout_message_(timeout_message),
    143         cancelled_(false) {
    144   }
    145 
    146   void cancel() {
    147     cancelled_ = true;
    148   }
    149 
    150   virtual void Run() {
    151     if (cancelled_) {
    152       return;
    153     }
    154     FAIL() << timeout_message_;
    155     MessageLoop::current()->Quit();
    156   }
    157 
    158  private:
    159   std::string timeout_message_;
    160   bool cancelled_;
    161 };
    162 
    163 
    164 // Base class for DevTools tests that test devtools functionality for
    165 // extensions and content scripts.
    166 class DevToolsExtensionDebugTest : public DevToolsSanityTest,
    167                                    public NotificationObserver {
    168  public:
    169   DevToolsExtensionDebugTest() : DevToolsSanityTest() {
    170     PathService::Get(chrome::DIR_TEST_DATA, &test_extensions_dir_);
    171     test_extensions_dir_ = test_extensions_dir_.AppendASCII("devtools");
    172     test_extensions_dir_ = test_extensions_dir_.AppendASCII("extensions");
    173   }
    174 
    175  protected:
    176   // Load an extention from test\data\devtools\extensions\<extension_name>
    177   void LoadExtension(const char* extension_name) {
    178     FilePath path = test_extensions_dir_.AppendASCII(extension_name);
    179     ASSERT_TRUE(LoadExtensionFromPath(path)) << "Failed to load extension.";
    180   }
    181 
    182  private:
    183   bool LoadExtensionFromPath(const FilePath& path) {
    184     ExtensionService* service = browser()->profile()->GetExtensionService();
    185     size_t num_before = service->extensions()->size();
    186     {
    187       NotificationRegistrar registrar;
    188       registrar.Add(this, NotificationType::EXTENSION_LOADED,
    189                     NotificationService::AllSources());
    190       CancelableQuitTask* delayed_quit =
    191           new CancelableQuitTask("Extension load timed out.");
    192       MessageLoop::current()->PostDelayedTask(FROM_HERE, delayed_quit,
    193           4*1000);
    194       service->LoadExtension(path);
    195       ui_test_utils::RunMessageLoop();
    196       delayed_quit->cancel();
    197     }
    198     size_t num_after = service->extensions()->size();
    199     if (num_after != (num_before + 1))
    200       return false;
    201 
    202     return WaitForExtensionHostsToLoad();
    203   }
    204 
    205   bool WaitForExtensionHostsToLoad() {
    206     // Wait for all the extension hosts that exist to finish loading.
    207     // NOTE: This assumes that the extension host list is not changing while
    208     // this method is running.
    209 
    210     NotificationRegistrar registrar;
    211     registrar.Add(this, NotificationType::EXTENSION_HOST_DID_STOP_LOADING,
    212                   NotificationService::AllSources());
    213     CancelableQuitTask* delayed_quit =
    214         new CancelableQuitTask("Extension host load timed out.");
    215     MessageLoop::current()->PostDelayedTask(FROM_HERE, delayed_quit,
    216         4*1000);
    217 
    218     ExtensionProcessManager* manager =
    219           browser()->profile()->GetExtensionProcessManager();
    220     for (ExtensionProcessManager::const_iterator iter = manager->begin();
    221          iter != manager->end();) {
    222       if ((*iter)->did_stop_loading())
    223         ++iter;
    224       else
    225         ui_test_utils::RunMessageLoop();
    226     }
    227 
    228     delayed_quit->cancel();
    229     return true;
    230   }
    231 
    232   void Observe(NotificationType type,
    233                const NotificationSource& source,
    234                const NotificationDetails& details) {
    235     switch (type.value) {
    236       case NotificationType::EXTENSION_LOADED:
    237       case NotificationType::EXTENSION_HOST_DID_STOP_LOADING:
    238         MessageLoopForUI::current()->Quit();
    239         break;
    240       default:
    241         NOTREACHED();
    242         break;
    243     }
    244   }
    245 
    246   FilePath test_extensions_dir_;
    247 };
    248 
    249 // Tests heap profiler.
    250 IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, FAILS_TestHeapProfiler) {
    251   RunTest("testHeapProfiler", kHeapProfilerPage);
    252 }
    253 
    254 // Tests scripts panel showing.
    255 IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestShowScriptsTab) {
    256   RunTest("testShowScriptsTab", kDebuggerTestPage);
    257 }
    258 
    259 // Tests that scripts tab is populated with inspected scripts even if it
    260 // hadn't been shown by the moment inspected paged refreshed.
    261 // @see http://crbug.com/26312
    262 IN_PROC_BROWSER_TEST_F(DevToolsSanityTest,
    263                        TestScriptsTabIsPopulatedOnInspectedPageRefresh) {
    264   // Clear inspector settings to ensure that Elements will be
    265   // current panel when DevTools window is open.
    266   GetInspectedTab()->render_view_host()->delegate()->ClearInspectorSettings();
    267   RunTest("testScriptsTabIsPopulatedOnInspectedPageRefresh",
    268           kDebuggerTestPage);
    269 }
    270 
    271 // Tests that a content script is in the scripts list.
    272 // This test is disabled, see bug 28961.
    273 IN_PROC_BROWSER_TEST_F(DevToolsExtensionDebugTest,
    274                        TestContentScriptIsPresent) {
    275   LoadExtension("simple_content_script");
    276   RunTest("testContentScriptIsPresent", kPageWithContentScript);
    277 }
    278 
    279 // Tests that scripts are not duplicated after Scripts Panel switch.
    280 IN_PROC_BROWSER_TEST_F(DevToolsSanityTest,
    281                        TestNoScriptDuplicatesOnPanelSwitch) {
    282   RunTest("testNoScriptDuplicatesOnPanelSwitch", kDebuggerTestPage);
    283 }
    284 
    285 // Tests that debugger works correctly if pause event occurs when DevTools
    286 // frontend is being loaded.
    287 IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestPauseWhenLoadingDevTools) {
    288   RunTest("testPauseWhenLoadingDevTools", kPauseWhenLoadingDevTools);
    289 }
    290 
    291 // Tests that pressing 'Pause' will pause script execution if the script
    292 // is already running.
    293 IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestPauseWhenScriptIsRunning) {
    294   RunTest("testPauseWhenScriptIsRunning", kPauseWhenScriptIsRunning);
    295 }
    296 
    297 }  // namespace
    298