| Index: chrome/resources/inspector/tests.js
 | 
| ===================================================================
 | 
| --- chrome/resources/inspector/tests.js	(revision 0)
 | 
| +++ chrome/resources/inspector/tests.js	(revision 0)
 | 
| @@ -0,0 +1,1657 @@
 | 
| +// Copyright (c) 2009 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.
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * @fileoverview This file contains small testing framework along with the
 | 
| + * test suite for the frontend. These tests are a part of the continues build
 | 
| + * and are executed by the devtools_sanity_unittest.cc as a part of the
 | 
| + * Interactive UI Test suite.
 | 
| + */
 | 
| +
 | 
| +if (window.domAutomationController) {
 | 
| +
 | 
| +var ___interactiveUiTestsMode = true;
 | 
| +
 | 
| +/**
 | 
| + * Test suite for interactive UI tests.
 | 
| + * @constructor
 | 
| + */
 | 
| +TestSuite = function() {
 | 
| +  this.controlTaken_ = false;
 | 
| +  this.timerId_ = -1;
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Reports test failure.
 | 
| + * @param {string} message Failure description.
 | 
| + */
 | 
| +TestSuite.prototype.fail = function(message) {
 | 
| +  if (this.controlTaken_) {
 | 
| +    this.reportFailure_(message);
 | 
| +  } else {
 | 
| +    throw message;
 | 
| +  }
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Equals assertion tests that expected == actual.
 | 
| + * @param {Object} expected Expected object.
 | 
| + * @param {Object} actual Actual object.
 | 
| + * @param {string} opt_message User message to print if the test fails.
 | 
| + */
 | 
| +TestSuite.prototype.assertEquals = function(expected, actual, opt_message) {
 | 
| +  if (expected != actual) {
 | 
| +    var message = 'Expected: "' + expected + '", but was "' + actual + '"';
 | 
| +    if (opt_message) {
 | 
| +      message = opt_message + '(' + message + ')';
 | 
| +    }
 | 
| +    this.fail(message);
 | 
| +  }
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * True assertion tests that value == true.
 | 
| + * @param {Object} value Actual object.
 | 
| + * @param {string} opt_message User message to print if the test fails.
 | 
| + */
 | 
| +TestSuite.prototype.assertTrue = function(value, opt_message) {
 | 
| +  this.assertEquals(true, !!value, opt_message);
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Contains assertion tests that string contains substring.
 | 
| + * @param {string} string Outer.
 | 
| + * @param {string} substring Inner.
 | 
| + */
 | 
| +TestSuite.prototype.assertContains = function(string, substring) {
 | 
| +  if (string.indexOf(substring) == -1) {
 | 
| +    this.fail('Expected to: "' + string + '" to contain "' + substring + '"');
 | 
| +  }
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Takes control over execution.
 | 
| + */
 | 
| +TestSuite.prototype.takeControl = function() {
 | 
| +  this.controlTaken_ = true;
 | 
| +  // Set up guard timer.
 | 
| +  var self = this;
 | 
| +  this.timerId_ = setTimeout(function() {
 | 
| +    self.reportFailure_('Timeout exceeded: 20 sec');
 | 
| +  }, 20000);
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Releases control over execution.
 | 
| + */
 | 
| +TestSuite.prototype.releaseControl = function() {
 | 
| +  if (this.timerId_ != -1) {
 | 
| +    clearTimeout(this.timerId_);
 | 
| +    this.timerId_ = -1;
 | 
| +  }
 | 
| +  this.reportOk_();
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Async tests use this one to report that they are completed.
 | 
| + */
 | 
| +TestSuite.prototype.reportOk_ = function() {
 | 
| +  window.domAutomationController.send('[OK]');
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Async tests use this one to report failures.
 | 
| + */
 | 
| +TestSuite.prototype.reportFailure_ = function(error) {
 | 
| +  if (this.timerId_ != -1) {
 | 
| +    clearTimeout(this.timerId_);
 | 
| +    this.timerId_ = -1;
 | 
| +  }
 | 
| +  window.domAutomationController.send('[FAILED] ' + error);
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Runs all global functions starting with 'test' as unit tests.
 | 
| + */
 | 
| +TestSuite.prototype.runTest = function(testName) {
 | 
| +  try {
 | 
| +    this[testName]();
 | 
| +    if (!this.controlTaken_) {
 | 
| +      this.reportOk_();
 | 
| +    }
 | 
| +  } catch (e) {
 | 
| +    this.reportFailure_(e);
 | 
| +  }
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * @param {string} panelName Name of the panel to show.
 | 
| + */
 | 
| +TestSuite.prototype.showPanel = function(panelName) {
 | 
| +  // Open Scripts panel.
 | 
| +  var toolbar = document.getElementById('toolbar');
 | 
| +  var button = toolbar.getElementsByClassName(panelName)[0];
 | 
| +  button.click();
 | 
| +  this.assertEquals(WebInspector.panels[panelName],
 | 
| +      WebInspector.currentPanel);
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Overrides the method with specified name until it's called first time.
 | 
| + * @param {Object} receiver An object whose method to override.
 | 
| + * @param {string} methodName Name of the method to override.
 | 
| + * @param {Function} override A function that should be called right after the
 | 
| + *     overriden method returns.
 | 
| + * @param {boolean} opt_sticky Whether restore original method after first run
 | 
| + *     or not.
 | 
| + */
 | 
| +TestSuite.prototype.addSniffer = function(receiver, methodName, override,
 | 
| +                                          opt_sticky) {
 | 
| +  var orig = receiver[methodName];
 | 
| +  if (typeof orig != 'function') {
 | 
| +    this.fail('Cannot find method to override: ' + methodName);
 | 
| +  }
 | 
| +  var test = this;
 | 
| +  receiver[methodName] = function(var_args) {
 | 
| +    try {
 | 
| +      var result = orig.apply(this, arguments);
 | 
| +    } finally {
 | 
| +      if (!opt_sticky) {
 | 
| +        receiver[methodName] = orig;
 | 
| +      }
 | 
| +    }
 | 
| +    // In case of exception the override won't be called.
 | 
| +    try {
 | 
| +      override.apply(this, arguments);
 | 
| +    } catch (e) {
 | 
| +      test.fail('Exception in overriden method "' + methodName + '": ' + e);
 | 
| +    }
 | 
| +    return result;
 | 
| +  };
 | 
| +};
 | 
| +
 | 
| +
 | 
| +// UI Tests
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Tests that the real injected host is present in the context.
 | 
| + */
 | 
| +TestSuite.prototype.testHostIsPresent = function() {
 | 
| +  this.assertTrue(typeof DevToolsHost == 'object' && !DevToolsHost.isStub);
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Tests elements tree has an 'HTML' root.
 | 
| + */
 | 
| +TestSuite.prototype.testElementsTreeRoot = function() {
 | 
| +  var doc = WebInspector.domAgent.document;
 | 
| +  this.assertEquals('HTML', doc.documentElement.nodeName);
 | 
| +  this.assertTrue(doc.documentElement.hasChildNodes());
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Tests that main resource is present in the system and that it is
 | 
| + * the only resource.
 | 
| + */
 | 
| +TestSuite.prototype.testMainResource = function() {
 | 
| +  var tokens = [];
 | 
| +  var resources = WebInspector.resources;
 | 
| +  for (var id in resources) {
 | 
| +    tokens.push(resources[id].lastPathComponent);
 | 
| +  }
 | 
| +  this.assertEquals('simple_page.html', tokens.join(','));
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Tests that resources tab is enabled when corresponding item is selected.
 | 
| + */
 | 
| +TestSuite.prototype.testEnableResourcesTab = function() {
 | 
| +  this.showPanel('resources');
 | 
| +
 | 
| +  var test = this;
 | 
| +  this.addSniffer(WebInspector, 'addResource',
 | 
| +      function(identifier, payload) {
 | 
| +        test.assertEquals('simple_page.html', payload.lastPathComponent);
 | 
| +        WebInspector.panels.resources.refresh();
 | 
| +        WebInspector.resources[identifier]._resourcesTreeElement.select();
 | 
| +
 | 
| +        test.releaseControl();
 | 
| +      });
 | 
| +
 | 
| +  // Following call should lead to reload that we capture in the
 | 
| +  // addResource override.
 | 
| +  WebInspector.panels.resources._enableResourceTracking();
 | 
| +
 | 
| +  // We now have some time to report results to controller.
 | 
| +  this.takeControl();
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Tests resource headers.
 | 
| + */
 | 
| +TestSuite.prototype.testResourceHeaders = function() {
 | 
| +  this.showPanel('resources');
 | 
| +
 | 
| +  var test = this;
 | 
| +
 | 
| +  var requestOk = false;
 | 
| +  var responseOk = false;
 | 
| +  var timingOk = false;
 | 
| +
 | 
| +  this.addSniffer(WebInspector, 'addResource',
 | 
| +      function(identifier, payload) {
 | 
| +        var resource = this.resources[identifier];
 | 
| +        if (resource.mainResource) {
 | 
| +          // We are only interested in secondary resources in this test.
 | 
| +          return;
 | 
| +        }
 | 
| +
 | 
| +        var requestHeaders = JSON.stringify(resource.requestHeaders);
 | 
| +        test.assertContains(requestHeaders, 'Accept');
 | 
| +        requestOk = true;
 | 
| +      }, true);
 | 
| +
 | 
| +  this.addSniffer(WebInspector, 'updateResource',
 | 
| +      function(identifier, payload) {
 | 
| +        var resource = this.resources[identifier];
 | 
| +        if (resource.mainResource) {
 | 
| +          // We are only interested in secondary resources in this test.
 | 
| +          return;
 | 
| +        }
 | 
| +
 | 
| +        if (payload.didResponseChange) {
 | 
| +          var responseHeaders = JSON.stringify(resource.responseHeaders);
 | 
| +          test.assertContains(responseHeaders, 'Content-type');
 | 
| +          test.assertContains(responseHeaders, 'Content-Length');
 | 
| +          test.assertTrue(typeof resource.responseReceivedTime != 'undefnied');
 | 
| +          responseOk = true;
 | 
| +        }
 | 
| +
 | 
| +        if (payload.didTimingChange) {
 | 
| +          test.assertTrue(typeof resource.startTime != 'undefnied');
 | 
| +          timingOk = true;
 | 
| +        }
 | 
| +
 | 
| +        if (payload.didCompletionChange) {
 | 
| +          test.assertTrue(requestOk);
 | 
| +          test.assertTrue(responseOk);
 | 
| +          test.assertTrue(timingOk);
 | 
| +          test.assertTrue(typeof resource.endTime != 'undefnied');
 | 
| +          test.releaseControl();
 | 
| +        }
 | 
| +      }, true);
 | 
| +
 | 
| +  WebInspector.panels.resources._enableResourceTracking();
 | 
| +  this.takeControl();
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Test that profiler works.
 | 
| + */
 | 
| +TestSuite.prototype.testProfilerTab = function() {
 | 
| +  this.showPanel('profiles');
 | 
| +
 | 
| +  var test = this;
 | 
| +  this.addSniffer(WebInspector, 'addProfileHeader',
 | 
| +      function(type, profile) {
 | 
| +        var panel = WebInspector.panels.profiles;
 | 
| +        panel.showProfile(profile);
 | 
| +        var node = panel.visibleView.profileDataGridTree.children[0];
 | 
| +        // Iterate over displayed functions and search for a function
 | 
| +        // that is called 'fib' or 'eternal_fib'. If found, it will mean
 | 
| +        // that we actually have profiled page's code.
 | 
| +        while (node) {
 | 
| +          if (node.functionName.indexOf('fib') != -1) {
 | 
| +            test.releaseControl();
 | 
| +          }
 | 
| +          node = node.traverseNextNode(true, null, true);
 | 
| +        }
 | 
| +
 | 
| +        test.fail();
 | 
| +      });
 | 
| +  var ticksCount = 0;
 | 
| +  var tickRecord = '\nt,';
 | 
| +  this.addSniffer(RemoteDebuggerAgent, 'DidGetNextLogLines',
 | 
| +      function(log) {
 | 
| +        var pos = 0;
 | 
| +        while ((pos = log.indexOf(tickRecord, pos)) != -1) {
 | 
| +          pos += tickRecord.length;
 | 
| +          ticksCount++;
 | 
| +        }
 | 
| +        if (ticksCount > 100) {
 | 
| +          InspectorController.stopProfiling();
 | 
| +        }
 | 
| +      }, true);
 | 
| +
 | 
| +  InspectorController.startProfiling();
 | 
| +  this.takeControl();
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Tests that scripts tab can be open and populated with inspected scripts.
 | 
| + */
 | 
| +TestSuite.prototype.testShowScriptsTab = function() {
 | 
| +  var parsedDebuggerTestPageHtml = false;
 | 
| +
 | 
| +  // Intercept parsedScriptSource calls to check that all expected scripts are
 | 
| +  // added to the debugger.
 | 
| +  var test = this;
 | 
| +  var receivedConsoleApiSource = false;
 | 
| +  this.addSniffer(WebInspector, 'parsedScriptSource',
 | 
| +      function(sourceID, sourceURL, source, startingLine) {
 | 
| +        if (sourceURL == undefined) {
 | 
| +          if (receivedConsoleApiSource) {
 | 
| +            test.fail('Unexpected script without URL');
 | 
| +          } else {
 | 
| +            receivedConsoleApiSource = true;
 | 
| +          }
 | 
| +        } else if (sourceURL.search(/debugger_test_page.html$/) != -1) {
 | 
| +          if (parsedDebuggerTestPageHtml) {
 | 
| +            test.fail('Unexpected parse event: ' + sourceURL);
 | 
| +          }
 | 
| +          parsedDebuggerTestPageHtml = true;
 | 
| +        } else {
 | 
| +          test.fail('Unexpected script URL: ' + sourceURL);
 | 
| +        }
 | 
| +
 | 
| +        if (!WebInspector.panels.scripts.visibleView) {
 | 
| +          test.fail('No visible script view: ' + sourceURL);
 | 
| +        }
 | 
| +
 | 
| +        // There should be two scripts: one for the main page and another
 | 
| +        // one which is source of console API(see
 | 
| +        // InjectedScript._ensureCommandLineAPIInstalled).
 | 
| +        if (parsedDebuggerTestPageHtml && receivedConsoleApiSource) {
 | 
| +           test.releaseControl();
 | 
| +        }
 | 
| +      }, true /* sticky */);
 | 
| +
 | 
| +  this.showPanel('scripts');
 | 
| +
 | 
| +  // Wait until all scripts are added to the debugger.
 | 
| +  this.takeControl();
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Tests that scripts list contains content scripts.
 | 
| + */
 | 
| +TestSuite.prototype.testContentScriptIsPresent = function() {
 | 
| +  this.showPanel('scripts');
 | 
| +  var test = this;
 | 
| +
 | 
| +  test._waitUntilScriptsAreParsed(
 | 
| +      ['page_with_content_script.html$', 'simple_content_script.js$'],
 | 
| +      function() {
 | 
| +        test.releaseControl();
 | 
| +      });
 | 
| +
 | 
| +  // Wait until all scripts are added to the debugger.
 | 
| +  this.takeControl();
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Tests that scripts are not duplicaed on Scripts tab switch.
 | 
| + */
 | 
| +TestSuite.prototype.testNoScriptDuplicatesOnPanelSwitch = function() {
 | 
| +  var test = this;
 | 
| +
 | 
| +  // There should be two scripts: one for the main page and another
 | 
| +  // one which is source of console API(see
 | 
| +  // InjectedScript._ensureCommandLineAPIInstalled).
 | 
| +  var expectedScriptsCount = 2;
 | 
| +  var parsedScripts = [];
 | 
| +
 | 
| +
 | 
| +  function switchToElementsTab() {
 | 
| +    test.showPanel('elements');
 | 
| +    setTimeout(switchToScriptsTab, 0);
 | 
| +  }
 | 
| +
 | 
| +  function switchToScriptsTab() {
 | 
| +    test.showPanel('scripts');
 | 
| +    setTimeout(checkScriptsPanel, 0);
 | 
| +  }
 | 
| +
 | 
| +  function checkScriptsPanel() {
 | 
| +    test.assertTrue(!!WebInspector.panels.scripts.visibleView,
 | 
| +                    'No visible script view.');
 | 
| +    var select = WebInspector.panels.scripts.filesSelectElement;
 | 
| +    test.assertEquals(expectedScriptsCount, select.options.length,
 | 
| +                      'Unexpected options count');
 | 
| +    test.releaseControl();
 | 
| +  }
 | 
| +
 | 
| +  this.addSniffer(WebInspector, 'parsedScriptSource',
 | 
| +      function(sourceID, sourceURL, source, startingLine) {
 | 
| +        test.assertTrue(
 | 
| +            parsedScripts.indexOf(sourceURL) == -1,
 | 
| +            'Duplicated script: ' + sourceURL);
 | 
| +        test.assertTrue(
 | 
| +           parsedScripts.length < expectedScriptsCount,
 | 
| +           'Too many scripts: ' + sourceURL);
 | 
| +        parsedScripts.push(sourceURL);
 | 
| +
 | 
| +        if (parsedScripts.length == expectedScriptsCount) {
 | 
| +          setTimeout(switchToElementsTab, 0);
 | 
| +        }
 | 
| +      }, true /* sticky */);
 | 
| +
 | 
| +  this.showPanel('scripts');
 | 
| +
 | 
| +  // Wait until all scripts are added to the debugger.
 | 
| +  this.takeControl();
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Tests that a breakpoint can be set.
 | 
| + */
 | 
| +TestSuite.prototype.testSetBreakpoint = function() {
 | 
| +  var parsedDebuggerTestPageHtml = false;
 | 
| +  var parsedDebuggerTestJs = false;
 | 
| +
 | 
| +  this.showPanel('scripts');
 | 
| +
 | 
| +  var scriptUrl = null;
 | 
| +  var breakpointLine = 12;
 | 
| +
 | 
| +  var test = this;
 | 
| +  this.addSniffer(devtools.DebuggerAgent.prototype, 'handleScriptsResponse_',
 | 
| +      function(msg) {
 | 
| +        var scriptSelect = document.getElementById('scripts-files');
 | 
| +        var options = scriptSelect.options;
 | 
| +
 | 
| +        // There should be console API source (see
 | 
| +        // InjectedScript._ensureCommandLineAPIInstalled) and the page script.
 | 
| +        test.assertEquals(2, options.length, 'Unexpected number of scripts(' +
 | 
| +            test.optionsToString_(options) + ')');
 | 
| +
 | 
| +        test.showMainPageScriptSource_(
 | 
| +            'debugger_test_page.html',
 | 
| +            function(view, url) {
 | 
| +              view._addBreakpoint(breakpointLine);
 | 
| +              // Force v8 execution.
 | 
| +              RemoteToolsAgent.ExecuteVoidJavaScript();
 | 
| +              test.waitForSetBreakpointResponse_(url, breakpointLine,
 | 
| +                  function() {
 | 
| +                    test.releaseControl();
 | 
| +                  });
 | 
| +            });
 | 
| +      });
 | 
| +
 | 
| +
 | 
| +  this.takeControl();
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Serializes options collection to string.
 | 
| + * @param {HTMLOptionsCollection} options
 | 
| + * @return {string}
 | 
| + */
 | 
| +TestSuite.prototype.optionsToString_ = function(options) {
 | 
| +  var names = [];
 | 
| +  for (var i = 0; i < options.length; i++) {
 | 
| +    names.push('"' + options[i].text + '"');
 | 
| +  }
 | 
| +  return names.join(',');
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Ensures that main HTML resource is selected in Scripts panel and that its
 | 
| + * source frame is setup. Invokes the callback when the condition is satisfied.
 | 
| + * @param {HTMLOptionsCollection} options
 | 
| + * @param {function(WebInspector.SourceView,string)} callback
 | 
| + */
 | 
| +TestSuite.prototype.showMainPageScriptSource_ = function(scriptName, callback) {
 | 
| +  var test = this;
 | 
| +
 | 
| +  var scriptSelect = document.getElementById('scripts-files');
 | 
| +  var options = scriptSelect.options;
 | 
| +
 | 
| +  // There should be console API source (see
 | 
| +  // InjectedScript._ensureCommandLineAPIInstalled) and the page script.
 | 
| +  test.assertEquals(2, options.length,
 | 
| +      'Unexpected number of scripts(' + test.optionsToString_(options) + ')');
 | 
| +
 | 
| +  // Select page's script if it's not current option.
 | 
| +  var scriptResource;
 | 
| +  if (options[scriptSelect.selectedIndex].text === scriptName) {
 | 
| +    scriptResource = options[scriptSelect.selectedIndex].representedObject;
 | 
| +  } else {
 | 
| +    var pageScriptIndex = -1;
 | 
| +    for (var i = 0; i < options.length; i++) {
 | 
| +      if (options[i].text === scriptName) {
 | 
| +        pageScriptIndex = i;
 | 
| +        break;
 | 
| +      }
 | 
| +    }
 | 
| +    test.assertTrue(-1 !== pageScriptIndex,
 | 
| +                    'Script with url ' + scriptName + ' not found among ' +
 | 
| +                        test.optionsToString_(options));
 | 
| +    scriptResource = options[pageScriptIndex].representedObject;
 | 
| +
 | 
| +    // Current panel is 'Scripts'.
 | 
| +    WebInspector.currentPanel._showScriptOrResource(scriptResource);
 | 
| +    test.assertEquals(pageScriptIndex, scriptSelect.selectedIndex,
 | 
| +                      'Unexpected selected option index.');
 | 
| +  }
 | 
| +
 | 
| +  test.assertTrue(scriptResource instanceof WebInspector.Resource,
 | 
| +                  'Unexpected resource class.');
 | 
| +  test.assertTrue(!!scriptResource.url, 'Resource URL is null.');
 | 
| +  test.assertTrue(
 | 
| +      scriptResource.url.search(scriptName + '$') != -1,
 | 
| +      'Main HTML resource should be selected.');
 | 
| +
 | 
| +  var scriptsPanel = WebInspector.panels.scripts;
 | 
| +
 | 
| +  var view = scriptsPanel.visibleView;
 | 
| +  test.assertTrue(view instanceof WebInspector.SourceView);
 | 
| +
 | 
| +  if (!view.sourceFrame._isContentLoaded()) {
 | 
| +    test.addSniffer(view, '_sourceFrameSetupFinished', function(event) {
 | 
| +      callback(view, scriptResource.url);
 | 
| +    });
 | 
| +  } else {
 | 
| +    callback(view, scriptResource.url);
 | 
| +  }
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/*
 | 
| + * Evaluates the code in the console as if user typed it manually and invokes
 | 
| + * the callback when the result message is received and added to the console.
 | 
| + * @param {string} code
 | 
| + * @param {function(string)} callback
 | 
| + */
 | 
| +TestSuite.prototype.evaluateInConsole_ = function(code, callback) {
 | 
| +  WebInspector.console.visible = true;
 | 
| +  WebInspector.console.prompt.text = code;
 | 
| +  WebInspector.console.promptElement.handleKeyEvent(
 | 
| +      new TestSuite.KeyEvent('Enter'));
 | 
| +
 | 
| +  this.addSniffer(WebInspector.ConsoleView.prototype, 'addMessage',
 | 
| +      function(commandResult) {
 | 
| +        callback(commandResult.toMessageElement().textContent);
 | 
| +      });
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/*
 | 
| + * Waits for 'setbreakpoint' response, checks that corresponding breakpoint
 | 
| + * was successfully set and invokes the callback if it was.
 | 
| + * @param {string} scriptUrl
 | 
| + * @param {number} breakpointLine
 | 
| + * @param {function()} callback
 | 
| + */
 | 
| +TestSuite.prototype.waitForSetBreakpointResponse_ = function(scriptUrl,
 | 
| +                                                             breakpointLine,
 | 
| +                                                             callback) {
 | 
| +  var test = this;
 | 
| +  test.addSniffer(
 | 
| +      devtools.DebuggerAgent.prototype,
 | 
| +      'handleSetBreakpointResponse_',
 | 
| +      function(msg) {
 | 
| +        var bps = this.urlToBreakpoints_[scriptUrl];
 | 
| +        test.assertTrue(!!bps, 'No breakpoints for line ' + breakpointLine);
 | 
| +        var line = devtools.DebuggerAgent.webkitToV8LineNumber_(breakpointLine);
 | 
| +        test.assertTrue(!!bps[line].getV8Id(),
 | 
| +                        'Breakpoint id was not assigned.');
 | 
| +        callback();
 | 
| +      });
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Tests eval on call frame.
 | 
| + */
 | 
| +TestSuite.prototype.testEvalOnCallFrame = function() {
 | 
| +  this.showPanel('scripts');
 | 
| +
 | 
| +  var breakpointLine = 16;
 | 
| +
 | 
| +  var test = this;
 | 
| +  this.addSniffer(devtools.DebuggerAgent.prototype, 'handleScriptsResponse_',
 | 
| +      function(msg) {
 | 
| +        test.showMainPageScriptSource_(
 | 
| +            'debugger_test_page.html',
 | 
| +            function(view, url) {
 | 
| +              view._addBreakpoint(breakpointLine);
 | 
| +              // Force v8 execution.
 | 
| +              RemoteToolsAgent.ExecuteVoidJavaScript();
 | 
| +              test.waitForSetBreakpointResponse_(url, breakpointLine,
 | 
| +                                                 setBreakpointCallback);
 | 
| +            });
 | 
| +      });
 | 
| +
 | 
| +  function setBreakpointCallback() {
 | 
| +    // Since breakpoints are ignored in evals' calculate() function is
 | 
| +    // execute after zero-timeout so that the breakpoint is hit.
 | 
| +    test.evaluateInConsole_(
 | 
| +        'setTimeout("calculate(123)" , 0)',
 | 
| +        function(resultText) {
 | 
| +          test.assertTrue(!isNaN(resultText),
 | 
| +                          'Failed to get timer id: ' + resultText);
 | 
| +          waitForBreakpointHit();
 | 
| +        });
 | 
| +  }
 | 
| +
 | 
| +  function waitForBreakpointHit() {
 | 
| +    test.addSniffer(
 | 
| +        devtools.DebuggerAgent.prototype,
 | 
| +        'handleBacktraceResponse_',
 | 
| +        function(msg) {
 | 
| +          test.assertEquals(2, this.callFrames_.length,
 | 
| +                            'Unexpected stack depth on the breakpoint. ' +
 | 
| +                                JSON.stringify(msg));
 | 
| +          test.assertEquals('calculate', this.callFrames_[0].functionName,
 | 
| +                            'Unexpected top frame function.');
 | 
| +          // Evaluate 'e+1' where 'e' is an argument of 'calculate' function.
 | 
| +          test.evaluateInConsole_(
 | 
| +              'e+1',
 | 
| +              function(resultText) {
 | 
| +                test.assertEquals('124', resultText, 'Unexpected "e+1" value.');
 | 
| +                test.releaseControl();
 | 
| +              });
 | 
| +        });
 | 
| +  }
 | 
| +
 | 
| +  this.takeControl();
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Tests that console auto completion works when script execution is paused.
 | 
| + */
 | 
| +TestSuite.prototype.testCompletionOnPause = function() {
 | 
| +  this.showPanel('scripts');
 | 
| +  var test = this;
 | 
| +  this._executeCodeWhenScriptsAreParsed(
 | 
| +      'handleClick()',
 | 
| +      ['completion_on_pause.html$']);
 | 
| +
 | 
| +  this._waitForScriptPause(
 | 
| +      {
 | 
| +        functionsOnStack: ['innerFunction', 'handleClick',
 | 
| +                           '(anonymous function)'],
 | 
| +        lineNumber: 9,
 | 
| +        lineText: '    debugger;'
 | 
| +      },
 | 
| +      showConsole);
 | 
| +
 | 
| +  function showConsole() {
 | 
| +    test.addSniffer(WebInspector.console, 'afterShow', testLocalsCompletion);
 | 
| +    WebInspector.showConsole();
 | 
| +  }
 | 
| +
 | 
| +  function testLocalsCompletion() {
 | 
| +    checkCompletions(
 | 
| +        'th',
 | 
| +        ['parameter1', 'closureLocal', 'p', 'createClosureLocal'],
 | 
| +        testThisCompletion);
 | 
| +  }
 | 
| +
 | 
| +  function testThisCompletion() {
 | 
| +    checkCompletions(
 | 
| +        'this.',
 | 
| +        ['field1', 'field2', 'm'],
 | 
| +        testFieldCompletion);
 | 
| +  }
 | 
| +
 | 
| +  function testFieldCompletion() {
 | 
| +    checkCompletions(
 | 
| +        'this.field1.',
 | 
| +        ['id', 'name'],
 | 
| +        function() {
 | 
| +          test.releaseControl();
 | 
| +        });
 | 
| +  }
 | 
| +
 | 
| +  function checkCompletions(expression, expectedProperties, callback) {
 | 
| +      test.addSniffer(WebInspector.console, '_reportCompletions',
 | 
| +        function(bestMatchOnly, completionsReadyCallback, dotNotation,
 | 
| +            bracketNotation, prefix, result, isException) {
 | 
| +          test.assertTrue(!isException,
 | 
| +                          'Exception while collecting completions');
 | 
| +          for (var i = 0; i < expectedProperties.length; i++) {
 | 
| +            var name = expectedProperties[i];
 | 
| +            test.assertTrue(result[name], 'Name ' + name +
 | 
| +                ' not found among the completions: ' +
 | 
| +                JSON.stringify(result));
 | 
| +          }
 | 
| +          setTimeout(callback, 0);
 | 
| +        });
 | 
| +    WebInspector.console.prompt.text = expression;
 | 
| +    WebInspector.console.prompt.autoCompleteSoon();
 | 
| +  }
 | 
| +
 | 
| +  this.takeControl();
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Tests that inspected page doesn't hang on reload if it contains a syntax
 | 
| + * error and DevTools window is open.
 | 
| + */
 | 
| +TestSuite.prototype.testAutoContinueOnSyntaxError = function() {
 | 
| +  this.showPanel('scripts');
 | 
| +  var test = this;
 | 
| +
 | 
| +  function checkScriptsList() {
 | 
| +    var scriptSelect = document.getElementById('scripts-files');
 | 
| +    var options = scriptSelect.options;
 | 
| +    // There should be only console API source (see
 | 
| +    // InjectedScript._ensureCommandLineAPIInstalled) since the page script
 | 
| +    // contains a syntax error.
 | 
| +    for (var i = 0 ; i < options.length; i++) {
 | 
| +      if (options[i].text.search('script_syntax_error.html$') != -1) {
 | 
| +        test.fail('Script with syntax error should not be in the list of ' +
 | 
| +                  'parsed scripts.');
 | 
| +      }
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  this.addSniffer(devtools.DebuggerAgent.prototype, 'handleScriptsResponse_',
 | 
| +      function(msg) {
 | 
| +        checkScriptsList();
 | 
| +
 | 
| +        // Reload inspected page.
 | 
| +        test.evaluateInConsole_(
 | 
| +            'window.location.reload(true);',
 | 
| +            function(resultText) {
 | 
| +              test.assertEquals('undefined', resultText,
 | 
| +                                'Unexpected result of reload().');
 | 
| +              waitForExceptionEvent();
 | 
| +            });
 | 
| +      });
 | 
| +
 | 
| +  function waitForExceptionEvent() {
 | 
| +    var exceptionCount = 0;
 | 
| +    test.addSniffer(
 | 
| +        devtools.DebuggerAgent.prototype,
 | 
| +        'handleExceptionEvent_',
 | 
| +        function(msg) {
 | 
| +          exceptionCount++;
 | 
| +          test.assertEquals(1, exceptionCount, 'Too many exceptions.');
 | 
| +          test.assertEquals(undefined, msg.getBody().script,
 | 
| +                            'Unexpected exception: ' + JSON.stringify(msg));
 | 
| +          test.releaseControl();
 | 
| +        });
 | 
| +
 | 
| +    // Check that the script is not paused on parse error.
 | 
| +    test.addSniffer(
 | 
| +        WebInspector,
 | 
| +        'pausedScript',
 | 
| +        function(callFrames) {
 | 
| +          test.fail('Script execution should not pause on syntax error.');
 | 
| +        });
 | 
| +  }
 | 
| +
 | 
| +  this.takeControl();
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Checks current execution line against expectations.
 | 
| + * @param {WebInspector.SourceFrame} sourceFrame
 | 
| + * @param {number} lineNumber Expected line number
 | 
| + * @param {string} lineContent Expected line text
 | 
| + */
 | 
| +TestSuite.prototype._checkExecutionLine = function(sourceFrame, lineNumber,
 | 
| +                                                   lineContent) {
 | 
| +  var sourceRow = sourceFrame.sourceRow(lineNumber);
 | 
| +
 | 
| +  var line = sourceRow.getElementsByClassName('webkit-line-content')[0];
 | 
| +  this.assertEquals(lineNumber, sourceFrame.executionLine,
 | 
| +                    'Unexpected execution line number.');
 | 
| +  this.assertEquals(lineContent, line.textContent,
 | 
| +                    'Unexpected execution line text.');
 | 
| +
 | 
| +  this.assertTrue(!!sourceRow, 'Execution line not found');
 | 
| +  this.assertTrue(sourceRow.hasStyleClass('webkit-execution-line'),
 | 
| +      'Execution line ' + lineNumber + ' is not highlighted. Class: ' +
 | 
| +          sourceRow.className);
 | 
| +}
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Checks that all expected scripts are present in the scripts list
 | 
| + * in the Scripts panel.
 | 
| + * @param {Array.<string>} expected Regular expressions describing
 | 
| + *     expected script names.
 | 
| + * @return {boolean} Whether all the scripts are in 'scripts-files' select
 | 
| + *     box
 | 
| + */
 | 
| +TestSuite.prototype._scriptsAreParsed = function(expected) {
 | 
| +  var scriptSelect = document.getElementById('scripts-files');
 | 
| +  var options = scriptSelect.options;
 | 
| +
 | 
| +  // Check that at least all the expected scripts are present.
 | 
| +  var missing = expected.slice(0);
 | 
| +  for (var i = 0 ; i < options.length; i++) {
 | 
| +    for (var j = 0; j < missing.length; j++) {
 | 
| +      if (options[i].text.search(missing[j]) != -1) {
 | 
| +        missing.splice(j, 1);
 | 
| +        break;
 | 
| +      }
 | 
| +    }
 | 
| +  }
 | 
| +  return missing.length == 0;
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Waits for script pause, checks expectations, and invokes the callback.
 | 
| + * @param {Object} expectations  Dictionary of expectations
 | 
| + * @param {function():void} callback
 | 
| + */
 | 
| +TestSuite.prototype._waitForScriptPause = function(expectations, callback) {
 | 
| +  var test = this;
 | 
| +  // Wait until script is paused.
 | 
| +  test.addSniffer(
 | 
| +      WebInspector,
 | 
| +      'pausedScript',
 | 
| +      function(callFrames) {
 | 
| +        test.assertEquals(expectations.functionsOnStack.length,
 | 
| +                          callFrames.length,
 | 
| +                          'Unexpected stack depth');
 | 
| +
 | 
| +        var functionsOnStack = [];
 | 
| +        for (var i = 0; i < callFrames.length; i++) {
 | 
| +          functionsOnStack.push(callFrames[i].functionName);
 | 
| +        }
 | 
| +        test.assertEquals(
 | 
| +            expectations.functionsOnStack.join(','),
 | 
| +            functionsOnStack.join(','), 'Unexpected stack.');
 | 
| +
 | 
| +        checkSourceFrameWhenLoaded();
 | 
| +      });
 | 
| +
 | 
| +  // Check that execution line where the script is paused is expected one.
 | 
| +  function checkSourceFrameWhenLoaded() {
 | 
| +    var frame = WebInspector.currentPanel.visibleView.sourceFrame;
 | 
| +    if (frame._isContentLoaded()) {
 | 
| +      checkExecLine();
 | 
| +    } else {
 | 
| +      frame.addEventListener('content loaded', checkExecLine);
 | 
| +    }
 | 
| +    function checkExecLine() {
 | 
| +      test._checkExecutionLine(frame, expectations.lineNumber,
 | 
| +                               expectations.lineText);
 | 
| +      // Make sure we don't listen anymore.
 | 
| +      frame.removeEventListener('content loaded', checkExecLine);
 | 
| +      callback();
 | 
| +    }
 | 
| +  }
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Performs sequence of steps.
 | 
| + * @param {Array.<Object|Function>} Array [expectations1,action1,expectations2,
 | 
| + *     action2,...,actionN].
 | 
| + */
 | 
| +TestSuite.prototype._performSteps = function(actions) {
 | 
| +  var test = this;
 | 
| +  var i = 0;
 | 
| +  function doNextAction() {
 | 
| +    if (i > 0) {
 | 
| +      actions[i++]();
 | 
| +    }
 | 
| +    if (i < actions.length - 1) {
 | 
| +      test._waitForScriptPause(actions[i++], doNextAction);
 | 
| +    }
 | 
| +  }
 | 
| +  doNextAction();
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Waits until all the scripts are parsed and asynchronously executes the code
 | 
| + * in the inspected page.
 | 
| + */
 | 
| +TestSuite.prototype._executeCodeWhenScriptsAreParsed = function(
 | 
| +    code, expectedScripts) {
 | 
| +  var test = this;
 | 
| +
 | 
| +  function executeFunctionInInspectedPage() {
 | 
| +    // Since breakpoints are ignored in evals' calculate() function is
 | 
| +    // execute after zero-timeout so that the breakpoint is hit.
 | 
| +    test.evaluateInConsole_(
 | 
| +        'setTimeout("' + code + '" , 0)',
 | 
| +        function(resultText) {
 | 
| +          test.assertTrue(!isNaN(resultText),
 | 
| +                          'Failed to get timer id: ' + resultText);
 | 
| +        });
 | 
| +  }
 | 
| +
 | 
| +  test._waitUntilScriptsAreParsed(
 | 
| +      expectedScripts, executeFunctionInInspectedPage);
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Waits until all the scripts are parsed and invokes the callback.
 | 
| + */
 | 
| +TestSuite.prototype._waitUntilScriptsAreParsed = function(
 | 
| +    expectedScripts, callback) {
 | 
| +  var test = this;
 | 
| +
 | 
| +  function waitForAllScripts() {
 | 
| +    if (test._scriptsAreParsed(expectedScripts)) {
 | 
| +      callback();
 | 
| +    } else {
 | 
| +      test.addSniffer(WebInspector, 'parsedScriptSource', waitForAllScripts);
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  waitForAllScripts();
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Waits until all debugger scripts are parsed and executes 'a()' in the
 | 
| + * inspected page.
 | 
| + */
 | 
| +TestSuite.prototype._executeFunctionForStepTest = function() {
 | 
| +  this._executeCodeWhenScriptsAreParsed(
 | 
| +      'a()',
 | 
| +      ['debugger_step.html$', 'debugger_step.js$']);
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Tests step over in the debugger.
 | 
| + */
 | 
| +TestSuite.prototype.testStepOver = function() {
 | 
| +  this.showPanel('scripts');
 | 
| +  var test = this;
 | 
| +
 | 
| +  this._executeFunctionForStepTest();
 | 
| +
 | 
| +  this._performSteps([
 | 
| +      {
 | 
| +        functionsOnStack: ['d','a','(anonymous function)'],
 | 
| +        lineNumber: 3,
 | 
| +        lineText: '    debugger;'
 | 
| +      },
 | 
| +      function() {
 | 
| +        document.getElementById('scripts-step-over').click();
 | 
| +      },
 | 
| +      {
 | 
| +        functionsOnStack: ['d','a','(anonymous function)'],
 | 
| +        lineNumber: 5,
 | 
| +        lineText: '  var y = fact(10);'
 | 
| +      },
 | 
| +      function() {
 | 
| +        document.getElementById('scripts-step-over').click();
 | 
| +      },
 | 
| +      {
 | 
| +        functionsOnStack: ['d','a','(anonymous function)'],
 | 
| +        lineNumber: 6,
 | 
| +        lineText: '  return y;'
 | 
| +      },
 | 
| +      function() {
 | 
| +        test.releaseControl();
 | 
| +      }
 | 
| +  ]);
 | 
| +
 | 
| +  test.takeControl();
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Tests step out in the debugger.
 | 
| + */
 | 
| +TestSuite.prototype.testStepOut = function() {
 | 
| +  this.showPanel('scripts');
 | 
| +  var test = this;
 | 
| +
 | 
| +  this._executeFunctionForStepTest();
 | 
| +
 | 
| +  this._performSteps([
 | 
| +      {
 | 
| +        functionsOnStack: ['d','a','(anonymous function)'],
 | 
| +        lineNumber: 3,
 | 
| +        lineText: '    debugger;'
 | 
| +      },
 | 
| +      function() {
 | 
| +        document.getElementById('scripts-step-out').click();
 | 
| +      },
 | 
| +      {
 | 
| +        functionsOnStack: ['a','(anonymous function)'],
 | 
| +        lineNumber: 8,
 | 
| +        lineText: '  printResult(result);'
 | 
| +      },
 | 
| +      function() {
 | 
| +        test.releaseControl();
 | 
| +      }
 | 
| +  ]);
 | 
| +
 | 
| +  test.takeControl();
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Tests step in in the debugger.
 | 
| + */
 | 
| +TestSuite.prototype.testStepIn = function() {
 | 
| +  this.showPanel('scripts');
 | 
| +  var test = this;
 | 
| +
 | 
| +  this._executeFunctionForStepTest();
 | 
| +
 | 
| +  this._performSteps([
 | 
| +      {
 | 
| +        functionsOnStack: ['d','a','(anonymous function)'],
 | 
| +        lineNumber: 3,
 | 
| +        lineText: '    debugger;'
 | 
| +      },
 | 
| +      function() {
 | 
| +        document.getElementById('scripts-step-over').click();
 | 
| +      },
 | 
| +      {
 | 
| +        functionsOnStack: ['d','a','(anonymous function)'],
 | 
| +        lineNumber: 5,
 | 
| +        lineText: '  var y = fact(10);'
 | 
| +      },
 | 
| +      function() {
 | 
| +        document.getElementById('scripts-step-into').click();
 | 
| +      },
 | 
| +      {
 | 
| +        functionsOnStack: ['fact','d','a','(anonymous function)'],
 | 
| +        lineNumber: 15,
 | 
| +        lineText: '  return r;'
 | 
| +      },
 | 
| +      function() {
 | 
| +        test.releaseControl();
 | 
| +      }
 | 
| +  ]);
 | 
| +
 | 
| +  test.takeControl();
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Gets a XPathResult matching given xpath.
 | 
| + * @param {string} xpath
 | 
| + * @param {number} resultType
 | 
| + * @param {Node} opt_ancestor Context node. If not specified documentElement
 | 
| + *     will be used
 | 
| + * @return {XPathResult} Type of returned value is determined by 'resultType' parameter
 | 
| + */
 | 
| +
 | 
| +TestSuite.prototype._evaluateXpath = function(
 | 
| +    xpath, resultType, opt_ancestor) {
 | 
| +  if (!opt_ancestor) {
 | 
| +    opt_ancestor = document.documentElement;
 | 
| +  }
 | 
| +  try {
 | 
| +    return document.evaluate(xpath, opt_ancestor, null, resultType, null);
 | 
| +  } catch(e) {
 | 
| +    this.fail('Error in expression: "' + xpath + '".' + e);
 | 
| +  }
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Gets first Node matching given xpath.
 | 
| + * @param {string} xpath
 | 
| + * @param {Node} opt_ancestor Context node. If not specified documentElement
 | 
| + *     will be used
 | 
| + * @return {?Node}
 | 
| + */
 | 
| +TestSuite.prototype._findNode = function(xpath, opt_ancestor) {
 | 
| +  var result = this._evaluateXpath(xpath, XPathResult.FIRST_ORDERED_NODE_TYPE,
 | 
| +      opt_ancestor).singleNodeValue;
 | 
| +  this.assertTrue(!!result, 'Cannot find node on path: ' + xpath);
 | 
| +  return result;
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Gets a text matching given xpath.
 | 
| + * @param {string} xpath
 | 
| + * @param {Node} opt_ancestor Context node. If not specified documentElement
 | 
| + *     will be used
 | 
| + * @return {?string}
 | 
| + */
 | 
| +TestSuite.prototype._findText = function(xpath, opt_ancestor) {
 | 
| +  var result = this._evaluateXpath(xpath, XPathResult.STRING_TYPE,
 | 
| +      opt_ancestor).stringValue;
 | 
| +  this.assertTrue(!!result, 'Cannot find text on path: ' + xpath);
 | 
| +  return result;
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Gets an iterator over nodes matching given xpath.
 | 
| + * @param {string} xpath
 | 
| + * @param {Node} opt_ancestor Context node. If not specified, documentElement
 | 
| + *     will be used
 | 
| + * @return {XPathResult} Iterator over the nodes
 | 
| + */
 | 
| +TestSuite.prototype._nodeIterator = function(xpath, opt_ancestor) {
 | 
| +  return this._evaluateXpath(xpath, XPathResult.ORDERED_NODE_ITERATOR_TYPE,
 | 
| +      opt_ancestor);
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Checks the scopeSectionDiv against the expectations.
 | 
| + * @param {Node} scopeSectionDiv The section div
 | 
| + * @param {Object} expectations Expectations dictionary
 | 
| + */
 | 
| +TestSuite.prototype._checkScopeSectionDiv = function(
 | 
| +    scopeSectionDiv, expectations) {
 | 
| +  var scopeTitle = this._findText(
 | 
| +      './div[@class="header"]/div[@class="title"]/text()', scopeSectionDiv);
 | 
| +  this.assertEquals(expectations.title, scopeTitle,
 | 
| +      'Unexpected scope section title.');
 | 
| +  if (!expectations.properties) {
 | 
| +    return;
 | 
| +  }
 | 
| +  this.assertTrue(scopeSectionDiv.hasStyleClass('expanded'), 'Section "' +
 | 
| +      scopeTitle + '" is collapsed.');
 | 
| +
 | 
| +  var propertyIt = this._nodeIterator('./ol[@class="properties"]/li',
 | 
| +                                      scopeSectionDiv);
 | 
| +  var propertyLi;
 | 
| +  var foundProps = [];
 | 
| +  while (propertyLi = propertyIt.iterateNext()) {
 | 
| +    var name = this._findText('./span[@class="name"]/text()', propertyLi);
 | 
| +    var value = this._findText('./span[@class="value"]/text()', propertyLi);
 | 
| +    this.assertTrue(!!name, 'Invalid variable name: "' + name + '"');
 | 
| +    this.assertTrue(name in expectations.properties,
 | 
| +                    'Unexpected property: ' + name);
 | 
| +    this.assertEquals(expectations.properties[name], value,
 | 
| +                      'Unexpected "' + name + '" property value.');
 | 
| +    delete expectations.properties[name];
 | 
| +    foundProps.push(name + ' = ' + value);
 | 
| +  }
 | 
| +
 | 
| +  // Check that all expected properties were found.
 | 
| +  for (var p in expectations.properties) {
 | 
| +    this.fail('Property "' + p + '" was not found in scope "' + scopeTitle +
 | 
| +        '". Found properties: "' + foundProps.join(',') + '"');
 | 
| +  }
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Expands scope sections matching the filter and invokes the callback on
 | 
| + * success.
 | 
| + * @param {function(WebInspector.ObjectPropertiesSection, number):boolean}
 | 
| + *     filter
 | 
| + * @param {Function} callback
 | 
| + */
 | 
| +TestSuite.prototype._expandScopeSections = function(filter, callback) {
 | 
| +  var sections = WebInspector.currentPanel.sidebarPanes.scopechain.sections;
 | 
| +
 | 
| +  var toBeUpdatedCount = 0;
 | 
| +  function updateListener() {
 | 
| +    --toBeUpdatedCount;
 | 
| +    if (toBeUpdatedCount == 0) {
 | 
| +      // Report when all scopes are expanded and populated.
 | 
| +      callback();
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  // Global scope is always the last one.
 | 
| +  for (var i = 0; i < sections.length - 1; i++) {
 | 
| +    var section = sections[i];
 | 
| +    if (!filter(sections, i)) {
 | 
| +      continue;
 | 
| +    }
 | 
| +    ++toBeUpdatedCount;
 | 
| +    var populated = section.populated;
 | 
| +
 | 
| +    this._hookGetPropertiesCallback(updateListener,
 | 
| +      function() {
 | 
| +        section.expand();
 | 
| +        if (populated) {
 | 
| +          // Make sure 'updateProperties' callback will be called at least once
 | 
| +          // after it was overridden.
 | 
| +          section.update();
 | 
| +        }
 | 
| +      });
 | 
| +  }
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Tests that scopes can be expanded and contain expected data.
 | 
| + */
 | 
| +TestSuite.prototype.testExpandScope = function() {
 | 
| +  this.showPanel('scripts');
 | 
| +  var test = this;
 | 
| +
 | 
| +  this._executeCodeWhenScriptsAreParsed(
 | 
| +      'handleClick()',
 | 
| +      ['debugger_closure.html$']);
 | 
| +
 | 
| +  this._waitForScriptPause(
 | 
| +      {
 | 
| +        functionsOnStack: ['innerFunction', 'handleClick',
 | 
| +                           '(anonymous function)'],
 | 
| +        lineNumber: 8,
 | 
| +        lineText: '    debugger;'
 | 
| +      },
 | 
| +      expandAllSectionsExceptGlobal);
 | 
| +
 | 
| +  // Expanding Global scope takes for too long so we skeep it.
 | 
| +  function expandAllSectionsExceptGlobal() {
 | 
| +      test._expandScopeSections(function(sections, i) {
 | 
| +        return i < sections.length - 1;
 | 
| +      },
 | 
| +      examineScopes /* When all scopes are expanded and populated check
 | 
| +          them. */);
 | 
| +  }
 | 
| +
 | 
| +  // Check scope sections contents.
 | 
| +  function examineScopes() {
 | 
| +    var scopeVariablesSection = test._findNode('//div[@id="scripts-sidebar"]/' +
 | 
| +        'div[div[@class="title"]/text()="Scope Variables"]');
 | 
| +    var expectedScopes = [
 | 
| +      {
 | 
| +        title: 'Local',
 | 
| +        properties: {
 | 
| +          x:2009,
 | 
| +          innerFunctionLocalVar:2011,
 | 
| +          'this': 'global',
 | 
| +        }
 | 
| +      },
 | 
| +      {
 | 
| +        title: 'Closure',
 | 
| +        properties: {
 | 
| +          n:'TextParam',
 | 
| +          makeClosureLocalVar:'local.TextParam',
 | 
| +        }
 | 
| +      },
 | 
| +      {
 | 
| +        title: 'Global',
 | 
| +      },
 | 
| +    ];
 | 
| +    var it = test._nodeIterator('./div[@class="body"]/div',
 | 
| +                                scopeVariablesSection);
 | 
| +    var scopeIndex = 0;
 | 
| +    var scopeDiv;
 | 
| +    while (scopeDiv = it.iterateNext()) {
 | 
| +       test.assertTrue(scopeIndex < expectedScopes.length,
 | 
| +                       'Too many scopes.');
 | 
| +       test._checkScopeSectionDiv(scopeDiv, expectedScopes[scopeIndex]);
 | 
| +       ++scopeIndex;
 | 
| +    }
 | 
| +    test.assertEquals(expectedScopes.length, scopeIndex,
 | 
| +                      'Unexpected number of scopes.');
 | 
| +
 | 
| +    test.releaseControl();
 | 
| +  }
 | 
| +
 | 
| +  test.takeControl();
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Returns child tree element for a property with given name.
 | 
| + * @param {TreeElement} parent Parent tree element.
 | 
| + * @param {string} childName
 | 
| + * @param {string} objectPath Path to the object. Will be printed in the case
 | 
| + *     of failure.
 | 
| + * @return {TreeElement}
 | 
| + */
 | 
| +TestSuite.prototype._findChildProperty = function(
 | 
| +    parent, childName, objectPath) {
 | 
| +  var children = parent.children;
 | 
| +  for (var i = 0; i < children.length; i++) {
 | 
| +    var treeElement = children[i];
 | 
| +    var property = treeElement.property;
 | 
| +    if (property.name == childName) {
 | 
| +      return treeElement;
 | 
| +    }
 | 
| +  }
 | 
| +  this.fail('Cannot find property "' + childName + '" in ' + objectPath);
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Executes the 'code' with InjectedScriptAccess.getProperties overriden
 | 
| + * so that all callbacks passed to InjectedScriptAccess.getProperties are
 | 
| + * extended with the 'hook'.
 | 
| + * @param {Function} hook The hook function.
 | 
| + * @param {Function} code A code snippet to be executed.
 | 
| + */
 | 
| +TestSuite.prototype._hookGetPropertiesCallback = function(hook, code) {
 | 
| +  var orig = InjectedScriptAccess.getProperties;
 | 
| +  InjectedScriptAccess.getProperties = function(objectProxy,
 | 
| +      ignoreHasOwnProperty, callback) {
 | 
| +    orig.call(InjectedScriptAccess, objectProxy, ignoreHasOwnProperty,
 | 
| +        function() {
 | 
| +          callback.apply(this, arguments);
 | 
| +          hook();
 | 
| +        });
 | 
| +  };
 | 
| +  try {
 | 
| +    code();
 | 
| +  } finally {
 | 
| +    InjectedScriptAccess.getProperties = orig;
 | 
| +  }
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Tests that all elements in prototype chain of an object have expected
 | 
| + * intrinic proprties(__proto__, constructor, prototype).
 | 
| + */
 | 
| +TestSuite.prototype.testDebugIntrinsicProperties = function() {
 | 
| +  this.showPanel('scripts');
 | 
| +  var test = this;
 | 
| +
 | 
| +  this._executeCodeWhenScriptsAreParsed(
 | 
| +      'handleClick()',
 | 
| +      ['debugger_intrinsic_properties.html$']);
 | 
| +
 | 
| +  this._waitForScriptPause(
 | 
| +      {
 | 
| +        functionsOnStack: ['callDebugger', 'handleClick',
 | 
| +                           '(anonymous function)'],
 | 
| +        lineNumber: 29,
 | 
| +        lineText: '  debugger;'
 | 
| +      },
 | 
| +      expandLocalScope);
 | 
| +
 | 
| +  var localScopeSection = null;
 | 
| +  function expandLocalScope() {
 | 
| +    test._expandScopeSections(function(sections, i) {
 | 
| +        if (i == 0) {
 | 
| +          test.assertTrue(sections[i].object.isLocal, 'Scope #0 is not Local.');
 | 
| +          localScopeSection = sections[i];
 | 
| +          return true;
 | 
| +        }
 | 
| +        return false;
 | 
| +      },
 | 
| +      examineLocalScope);
 | 
| +  }
 | 
| +
 | 
| +  function examineLocalScope() {
 | 
| +    var scopeExpectations = [
 | 
| +      'a', 'Object', [
 | 
| +        'constructor', 'function Child()', [
 | 
| +          'constructor', 'function Function()', null,
 | 
| +          'name', 'Child', null,
 | 
| +          'prototype', 'Object', [
 | 
| +            'childProtoField', '21', null
 | 
| +          ]
 | 
| +        ],
 | 
| +
 | 
| +        '__proto__', 'Object', [
 | 
| +          '__proto__', 'Object', [
 | 
| +            '__proto__', 'Object', [
 | 
| +              '__proto__', 'null', null,
 | 
| +              'constructor', 'function Object()', null,
 | 
| +            ],
 | 
| +            'constructor', 'function Parent()', [
 | 
| +              'name', 'Parent', null,
 | 
| +              'prototype', 'Object', [
 | 
| +                'parentProtoField', '11', null,
 | 
| +              ]
 | 
| +            ],
 | 
| +            'parentProtoField', '11', null,
 | 
| +          ],
 | 
| +          'constructor', 'function Child()', null,
 | 
| +          'childProtoField', '21', null,
 | 
| +        ],
 | 
| +
 | 
| +        'parentField', '10', null,
 | 
| +        'childField', '20', null,
 | 
| +      ]
 | 
| +    ];
 | 
| +
 | 
| +    checkProperty(
 | 
| +        localScopeSection.propertiesTreeOutline,
 | 
| +        '<Local Scope>',
 | 
| +        scopeExpectations);
 | 
| +  }
 | 
| +
 | 
| +  var propQueue = [];
 | 
| +  var index = 0;
 | 
| +  var expectedFinalIndex = 8;
 | 
| +
 | 
| +  function expandAndCheckNextProperty() {
 | 
| +    if (index == propQueue.length) {
 | 
| +      test.assertEquals(expectedFinalIndex, index,
 | 
| +          'Unexpected number of expanded objects.');
 | 
| +      test.releaseControl();
 | 
| +      return;
 | 
| +    }
 | 
| +
 | 
| +    // Read next property data from the queue.
 | 
| +    var treeElement = propQueue[index].treeElement;
 | 
| +    var path = propQueue[index].path;
 | 
| +    var expectations = propQueue[index].expectations;
 | 
| +    index++;
 | 
| +
 | 
| +    // Expand the property.
 | 
| +    test._hookGetPropertiesCallback(function() {
 | 
| +          checkProperty(treeElement, path, expectations);
 | 
| +        },
 | 
| +        function() {
 | 
| +          treeElement.expand();
 | 
| +        });
 | 
| +  }
 | 
| +
 | 
| +  function checkProperty(treeElement, path, expectations) {
 | 
| +    for (var i = 0; i < expectations.length; i += 3) {
 | 
| +      var name = expectations[i];
 | 
| +      var description = expectations[i+1];
 | 
| +      var value = expectations[i+2];
 | 
| +
 | 
| +      var propertyPath = path + '.' + name;
 | 
| +      var propertyTreeElement = test._findChildProperty(
 | 
| +          treeElement, name, path);
 | 
| +      test.assertTrue(propertyTreeElement,
 | 
| +          'Property "' + propertyPath + '" not found.');
 | 
| +      test.assertEquals(description,
 | 
| +          propertyTreeElement.property.value.description,
 | 
| +          'Unexpected "' + propertyPath + '" description.');
 | 
| +      if (value) {
 | 
| +        // Schedule property content check.
 | 
| +        propQueue.push({
 | 
| +          treeElement: propertyTreeElement,
 | 
| +          path: propertyPath,
 | 
| +          expectations: value,
 | 
| +        });
 | 
| +      }
 | 
| +    }
 | 
| +    // Check next property in the queue.
 | 
| +    expandAndCheckNextProperty();
 | 
| +  }
 | 
| +
 | 
| +  test.takeControl();
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Tests 'Pause' button will pause debugger when a snippet is evaluated.
 | 
| + */
 | 
| +TestSuite.prototype.testPauseInEval = function() {
 | 
| +  this.showPanel('scripts');
 | 
| +
 | 
| +  var test = this;
 | 
| +
 | 
| +  var pauseButton = document.getElementById('scripts-pause');
 | 
| +  pauseButton.click();
 | 
| +
 | 
| +  devtools.tools.evaluateJavaScript('fib(10)');
 | 
| +
 | 
| +  this.addSniffer(WebInspector, 'pausedScript',
 | 
| +      function() {
 | 
| +        test.releaseControl();
 | 
| +      });
 | 
| +
 | 
| +  test.takeControl();
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Key event with given key identifier.
 | 
| + */
 | 
| +TestSuite.KeyEvent = function(key) {
 | 
| +  this.keyIdentifier = key;
 | 
| +};
 | 
| +TestSuite.KeyEvent.prototype.preventDefault = function() {};
 | 
| +TestSuite.KeyEvent.prototype.stopPropagation = function() {};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Tests console eval.
 | 
| + */
 | 
| +TestSuite.prototype.testConsoleEval = function() {
 | 
| +  var test = this;
 | 
| +  this.evaluateInConsole_('123',
 | 
| +      function(resultText) {
 | 
| +        test.assertEquals('123', resultText);
 | 
| +        test.releaseControl();
 | 
| +      });
 | 
| +
 | 
| +  this.takeControl();
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Tests console log.
 | 
| + */
 | 
| +TestSuite.prototype.testConsoleLog = function() {
 | 
| +  WebInspector.console.visible = true;
 | 
| +  var messages = WebInspector.console.messages;
 | 
| +  var index = 0;
 | 
| +
 | 
| +  var test = this;
 | 
| +  var assertNext = function(line, message, opt_class, opt_count, opt_substr) {
 | 
| +    var elem = messages[index++].toMessageElement();
 | 
| +    var clazz = elem.getAttribute('class');
 | 
| +    var expectation = (opt_count || '') + 'console_test_page.html:' +
 | 
| +        line + message;
 | 
| +    if (opt_substr) {
 | 
| +      test.assertContains(elem.textContent, expectation);
 | 
| +    } else {
 | 
| +      test.assertEquals(expectation, elem.textContent);
 | 
| +    }
 | 
| +    if (opt_class) {
 | 
| +      test.assertContains(clazz, 'console-' + opt_class);
 | 
| +    }
 | 
| +  };
 | 
| +
 | 
| +  assertNext('5', 'log', 'log-level');
 | 
| +  assertNext('7', 'debug', 'log-level');
 | 
| +  assertNext('9', 'info', 'log-level');
 | 
| +  assertNext('11', 'warn', 'warning-level');
 | 
| +  assertNext('13', 'error', 'error-level');
 | 
| +  assertNext('15', 'Message format number 1, 2 and 3.5');
 | 
| +  assertNext('17', 'Message format for string');
 | 
| +  assertNext('19', 'Object Object');
 | 
| +  assertNext('22', 'repeated', 'log-level', 5);
 | 
| +  assertNext('26', 'count: 1');
 | 
| +  assertNext('26', 'count: 2');
 | 
| +  assertNext('29', 'group', 'group-title');
 | 
| +  index++;
 | 
| +  assertNext('33', 'timer:', 'log-level', '', true);
 | 
| +  assertNext('35', '1 2 3', 'log-level');
 | 
| +  assertNext('37', 'HTMLDocument', 'log-level');
 | 
| +  assertNext('39', '<html>', 'log-level', '', true);
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Tests eval of global objects.
 | 
| + */
 | 
| +TestSuite.prototype.testEvalGlobal = function() {
 | 
| +  WebInspector.console.visible = true;
 | 
| +
 | 
| +  var inputs = ['foo', 'foobar'];
 | 
| +  var expectations = ['foo', 'fooValue',
 | 
| +                      'foobar', 'ReferenceError: foobar is not defined'];
 | 
| +
 | 
| +  // Do not change code below - simply add inputs and expectations above.
 | 
| +  var initEval = function(input) {
 | 
| +    WebInspector.console.prompt.text = input;
 | 
| +    WebInspector.console.promptElement.handleKeyEvent(
 | 
| +        new TestSuite.KeyEvent('Enter'));
 | 
| +  };
 | 
| +  var test = this;
 | 
| +  var messagesCount = 0;
 | 
| +  var inputIndex = 0;
 | 
| +  this.addSniffer(WebInspector.ConsoleView.prototype, 'addMessage',
 | 
| +      function(commandResult) {
 | 
| +        messagesCount++;
 | 
| +        if (messagesCount == expectations.length) {
 | 
| +          var messages = WebInspector.console.messages;
 | 
| +          for (var i = 0; i < expectations; ++i) {
 | 
| +            var elem = messages[i++].toMessageElement();
 | 
| +            test.assertEquals(elem.textContent, expectations[i]);
 | 
| +          }
 | 
| +          test.releaseControl();
 | 
| +        } else if (messagesCount % 2 == 0) {
 | 
| +          initEval(inputs[inputIndex++]);
 | 
| +        }
 | 
| +      }, true);
 | 
| +
 | 
| +  initEval(inputs[inputIndex++]);
 | 
| +  this.takeControl();
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Test runner for the test suite.
 | 
| + */
 | 
| +var uiTests = {};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Run each test from the test suit on a fresh instance of the suite.
 | 
| + */
 | 
| +uiTests.runAllTests = function() {
 | 
| +  // For debugging purposes.
 | 
| +  for (var name in TestSuite.prototype) {
 | 
| +    if (name.substring(0, 4) == 'test' &&
 | 
| +        typeof TestSuite.prototype[name] == 'function') {
 | 
| +      uiTests.runTest(name);
 | 
| +    }
 | 
| +  }
 | 
| +};
 | 
| +
 | 
| +
 | 
| +/**
 | 
| + * Run specified test on a fresh instance of the test suite.
 | 
| + * @param {string} name Name of a test method from TestSuite class.
 | 
| + */
 | 
| +uiTests.runTest = function(name) {
 | 
| +  new TestSuite().runTest(name);
 | 
| +};
 | 
| +
 | 
| +
 | 
| +}
 | 
| 
 | 
| Property changes on: chrome/resources/inspector/tests.js
 | 
| ___________________________________________________________________
 | 
| Added: svn:executable
 | 
|    + *
 | 
| 
 | 
| 
 |