Bug 719618 - Craft a no-outside-network mode for peptest. r=ctalbert

This commit is contained in:
Mark Cote 2012-02-14 10:23:25 -05:00
parent 62c6f483e4
commit dd06b27644
19 changed files with 198 additions and 96 deletions

View File

@ -67,12 +67,12 @@ waitForEvents.prototype = {
init : function waitForEvents_init(node, events) {
if (node.getNode != undefined)
node = node.getNode();
this.events = events;
this.node = node;
node.firedEvents = {};
this.registry = {};
for each(e in events) {
var listener = function(event) {
this.firedEvents[event.type] = true;
@ -92,7 +92,7 @@ waitForEvents.prototype = {
utils.waitFor(function() {
return this.node.firedEvents[e] == true;
}, "Timeout happened before event '" + ex +"' was fired.", timeout, interval);
this.node.removeEventListener(e, this.registry[e], true);
}
}
@ -281,10 +281,10 @@ var MozMillController = function (window) {
}
// constructs a MozMillElement from the controller's window
MozMillController.prototype.__defineGetter__("windowElement", function() {
if (this._windowElement == undefined)
this._windowElement = new mozelement.MozMillElement(undefined, undefined, {'element': this.window});
return this._windowElement;
MozMillController.prototype.__defineGetter__("rootElement", function() {
if (this._rootElement == undefined)
this._rootElement = new mozelement.MozMillElement(undefined, undefined, {'element': this.window.document.documentElement});
return this._rootElement;
});
MozMillController.prototype.sleep = utils.sleep;
@ -308,7 +308,7 @@ MozMillController.prototype.open = function(url)
/**
* Take a screenshot of specified node
*
*
* @param {element} node
* the window or DOM element to capture
* @param {string} name
@ -322,7 +322,7 @@ MozMillController.prototype.screenShot = function _screenShot(node, name, save,
if (!node) {
throw new Error("node is undefined");
}
// Unwrap the node and highlights
if ("getNode" in node) node = node.getNode();
if (highlights) {
@ -332,7 +332,7 @@ MozMillController.prototype.screenShot = function _screenShot(node, name, save,
}
}
}
// If save is false, a dataURL is used
// Include both in the report anyway to avoid confusion and make the report easier to parse
var filepath, dataURL;
@ -419,7 +419,7 @@ MozMillController.prototype.startUserShutdown = function (timeout, restart, next
this.window.setTimeout(broker.sendMessage, timeout, 'userShutdown', 0);
}
MozMillController.prototype.restartApplication = function (next, resetProfile)
MozMillController.prototype.restartApplication = function (next, resetProfile)
{
// restart the application via the python runner
// - next : name of the next test function to run after restart
@ -433,7 +433,7 @@ MozMillController.prototype.restartApplication = function (next, resetProfile)
utils.getMethodInWindows('goQuitApplication')();
}
MozMillController.prototype.stopApplication = function (resetProfile)
MozMillController.prototype.stopApplication = function (resetProfile)
{
// stop the application via the python runner
// - resetProfile : whether to reset the profile after shutdown
@ -488,7 +488,7 @@ MozMillController.prototype.assertText = function (el, text) {
//Assert that a specified node exists
MozMillController.prototype.assertNode = function (el) {
logDeprecatedAssert("assertNode");
//this.window.focus();
var element = el.getNode();
if (!element){
@ -502,7 +502,7 @@ MozMillController.prototype.assertNode = function (el) {
// Assert that a specified node doesn't exist
MozMillController.prototype.assertNodeNotExist = function (el) {
logDeprecatedAssert("assertNodeNotExist");
//this.window.focus();
try {
var element = el.getNode();
@ -523,7 +523,7 @@ MozMillController.prototype.assertNodeNotExist = function (el) {
//Assert that a form element contains the expected value
MozMillController.prototype.assertValue = function (el, value) {
logDeprecatedAssert("assertValue");
//this.window.focus();
var n = el.getNode();
@ -550,7 +550,7 @@ MozMillController.prototype.assert = function(callback, message, thisObject)
//Assert that a provided value is selected in a select element
MozMillController.prototype.assertSelected = function (el, value) {
logDeprecatedAssert("assertSelected");
//this.window.focus();
var n = el.getNode();
var validator = value;
@ -566,7 +566,7 @@ MozMillController.prototype.assertSelected = function (el, value) {
//Assert that a provided checkbox is checked
MozMillController.prototype.assertChecked = function (el) {
logDeprecatedAssert("assertChecked");
//this.window.focus();
var element = el.getNode();
@ -581,7 +581,7 @@ MozMillController.prototype.assertChecked = function (el) {
// Assert that a provided checkbox is not checked
MozMillController.prototype.assertNotChecked = function (el) {
logDeprecatedAssert("assertNotChecked");
var element = el.getNode();
if (!element) {
@ -596,7 +596,7 @@ MozMillController.prototype.assertNotChecked = function (el) {
return false;
};
/**
/**
* Assert that an element's javascript property exists or has a particular value
*
* if val is undefined, will return true if the property exists.
@ -604,7 +604,7 @@ MozMillController.prototype.assertNotChecked = function (el) {
*/
MozMillController.prototype.assertJSProperty = function(el, attrib, val) {
logDeprecatedAssert("assertJSProperty");
var element = el.getNode();
if (!element){
throw new Error("could not find element " + el.getInfo());
@ -615,13 +615,13 @@ MozMillController.prototype.assertJSProperty = function(el, attrib, val) {
if (res) {
broker.pass({'function':'Controller.assertJSProperty("' + el.getInfo() + '") : ' + val});
} else {
throw new Error("Controller.assertJSProperty(" + el.getInfo() + ") : " +
throw new Error("Controller.assertJSProperty(" + el.getInfo() + ") : " +
(val === undefined ? "property '" + attrib + "' doesn't exist" : val + " == " + value));
}
return res;
};
/**
/**
* Assert that an element's javascript property doesn't exist or doesn't have a particular value
*
* if val is undefined, will return true if the property doesn't exist.
@ -629,7 +629,7 @@ MozMillController.prototype.assertJSProperty = function(el, attrib, val) {
*/
MozMillController.prototype.assertNotJSProperty = function(el, attrib, val) {
logDeprecatedAssert("assertNotJSProperty");
var element = el.getNode();
if (!element){
throw new Error("could not find element " + el.getInfo());
@ -646,7 +646,7 @@ MozMillController.prototype.assertNotJSProperty = function(el, attrib, val) {
return res;
};
/**
/**
* Assert that an element's dom property exists or has a particular value
*
* if val is undefined, will return true if the property exists.
@ -654,7 +654,7 @@ MozMillController.prototype.assertNotJSProperty = function(el, attrib, val) {
*/
MozMillController.prototype.assertDOMProperty = function(el, attrib, val) {
logDeprecatedAssert("assertDOMProperty");
var element = el.getNode();
if (!element){
throw new Error("could not find element " + el.getInfo());
@ -664,18 +664,18 @@ MozMillController.prototype.assertDOMProperty = function(el, attrib, val) {
if (res && val !== undefined) {
value = element.getAttribute(attrib);
res = (String(value) == String(val));
}
}
if (res) {
broker.pass({'function':'Controller.assertDOMProperty("' + el.getInfo() + '") : ' + val});
} else {
throw new Error("Controller.assertDOMProperty(" + el.getInfo() + ") : " +
throw new Error("Controller.assertDOMProperty(" + el.getInfo() + ") : " +
(val === undefined ? "property '" + attrib + "' doesn't exist" : val + " == " + value));
}
return res;
};
/**
/**
* Assert that an element's dom property doesn't exist or doesn't have a particular value
*
* if val is undefined, will return true if the property doesn't exist.
@ -683,7 +683,7 @@ MozMillController.prototype.assertDOMProperty = function(el, attrib, val) {
*/
MozMillController.prototype.assertNotDOMProperty = function(el, attrib, val) {
logDeprecatedAssert("assertNotDOMProperty");
var element = el.getNode();
if (!element){
throw new Error("could not find element " + el.getInfo());
@ -693,11 +693,11 @@ MozMillController.prototype.assertNotDOMProperty = function(el, attrib, val) {
if (res && val !== undefined) {
value = element.getAttribute(attrib);
res = (String(value) == String(val));
}
}
if (!res) {
broker.pass({'function':'Controller.assertNotDOMProperty("' + el.getInfo() + '") : ' + val});
} else {
throw new Error("Controller.assertNotDOMProperty(" + el.getInfo() + ") : " +
throw new Error("Controller.assertNotDOMProperty(" + el.getInfo() + ") : " +
(val == undefined ? "property '" + attrib + "' exists" : val + " == " + value));
}
return !res;
@ -720,7 +720,7 @@ MozMillController.prototype.assertPropertyNotExist = function(el, attrib) {
// for broken images (in Safari only) but works reliably
MozMillController.prototype.assertImageLoaded = function (el) {
logDeprecatedAssert("assertImageLoaded");
//this.window.focus();
var img = el.getNode();
if (!img || img.tagName != 'IMG') {
@ -959,16 +959,16 @@ controllerAdditions = {
* Use the MozMillElement object instead (https://developer.mozilla.org/en/Mozmill/Mozmill_Element_Object)
*/
MozMillController.prototype.select = function (elem, index, option, value) {
return elem.select(index, option, value);
return elem.select(index, option, value);
};
MozMillController.prototype.keypress = function(aTarget, aKey, aModifiers, aExpectedEvent) {
if (aTarget == null) { aTarget = this.windowElement; }
if (aTarget == null) { aTarget = this.rootElement; }
return aTarget.keypress(aKey, aModifiers, aExpectedEvent);
}
MozMillController.prototype.type = function (aTarget, aText, aExpectedEvent) {
if (aTarget == null) { aTarget = this.windowElement; }
if (aTarget == null) { aTarget = this.rootElement; }
var that = this;
var retval = true;

View File

@ -36,7 +36,7 @@
*
* ***** END LICENSE BLOCK ***** */
var EXPORTED_SYMBOLS = ["Elem", "Selector", "ID", "Link", "XPath", "Name", "Lookup",
var EXPORTED_SYMBOLS = ["Elem", "Selector", "ID", "Link", "XPath", "Name", "Lookup",
"MozMillElement", "MozMillCheckBox", "MozMillRadio", "MozMillDropList",
"MozMillTextBox", "subclasses",
];
@ -55,9 +55,10 @@ var subclasses = [MozMillCheckBox, MozMillRadio, MozMillDropList, MozMillTextBox
* Returns an new instance of a MozMillElement
* The type of the element is automatically determined
*/
function createInstance(locatorType, locator, elem) {
function createInstance(locatorType, locator, elem, document) {
if (elem) {
var args = {"element":elem};
var args = { "element": elem,
"document": document };
for (var i = 0; i < subclasses.length; ++i) {
if (subclasses[i].isType(elem)) {
return new subclasses[i](locatorType, locator, args);
@ -72,28 +73,28 @@ var Elem = function(node) {
return createInstance("Elem", node, node);
};
var Selector = function(_document, selector, index) {
return createInstance("Selector", selector, elementslib.Selector(_document, selector, index));
var Selector = function(document, selector, index) {
return createInstance("Selector", selector, elementslib.Selector(document, selector, index), document);
};
var ID = function(_document, nodeID) {
return createInstance("ID", nodeID, elementslib.ID(_document, nodeID));
var ID = function(document, nodeID) {
return createInstance("ID", nodeID, elementslib.ID(document, nodeID), document);
};
var Link = function(_document, linkName) {
return createInstance("Link", linkName, elementslib.Link(_document, linkName));
var Link = function(document, linkName) {
return createInstance("Link", linkName, elementslib.Link(document, linkName), document);
};
var XPath = function(_document, expr) {
return createInstance("XPath", expr, elementslib.XPath(_document, expr));
var XPath = function(document, expr) {
return createInstance("XPath", expr, elementslib.XPath(document, expr), document);
};
var Name = function(_document, nName) {
return createInstance("Name", nName, elementslib.Name(_document, nName));
var Name = function(document, nName) {
return createInstance("Name", nName, elementslib.Name(document, nName), document);
};
var Lookup = function(_document, expression) {
return createInstance("Lookup", expression, elementslib.Lookup(_document, expression));
var Lookup = function(document, expression) {
return createInstance("Lookup", expression, elementslib.Lookup(document, expression), document);
};
@ -121,7 +122,7 @@ MozMillElement.isType = function(node) {
MozMillElement.prototype.__defineGetter__("element", function() {
if (this._element == undefined) {
if (elementslib[this._locatorType]) {
this._element = elementslib[this._locatorType](this._document, this._locator);
this._element = elementslib[this._locatorType](this._document, this._locator);
} else if (this._locatorType == "Elem") {
this._element = this._locator;
} else {
@ -479,7 +480,7 @@ MozMillRadio.prototype.select = function(index) {
if (!this.element) {
throw new Error("could not find element " + this.getInfo());
}
if (this.element.localName.toLowerCase() == "radiogroup") {
var element = this.element.getElementsByTagName("radio")[index || 0];
new MozMillRadio("Elem", element).click();
@ -487,7 +488,7 @@ MozMillRadio.prototype.select = function(index) {
var element = this.element;
this.click();
}
utils.waitFor(function() {
// If we have a XUL element, unwrap its XPCNativeWrapper
if (element.namespaceURI == "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul") {
@ -581,7 +582,7 @@ MozMillDropList.prototype.select = function (indx, option, value) {
this.element = utils.unwrapNode(this.element);
// Get the list of menuitems
menuitems = this.element.getElementsByTagName("menupopup")[0].getElementsByTagName("menuitem");
var item = null;
if (indx != undefined) {

View File

@ -698,7 +698,7 @@ function synthesizeQuerySelectedText(aWindow)
{
var utils = _getDOMWindowUtils(aWindow);
if (!utils) {
return nsnull;
return null;
}
return utils.sendQueryContentEvent(utils.QUERY_SELECTED_TEXT, 0, 0, 0, 0);
}
@ -718,7 +718,7 @@ function synthesizeQueryTextContent(aOffset, aLength, aWindow)
{
var utils = _getDOMWindowUtils(aWindow);
if (!utils) {
return nsnull;
return null;
}
return utils.sendQueryContentEvent(utils.QUERY_TEXT_CONTENT,
aOffset, aLength, 0, 0);
@ -737,7 +737,7 @@ function synthesizeQueryCaretRect(aOffset, aWindow)
{
var utils = _getDOMWindowUtils(aWindow);
if (!utils) {
return nsnull;
return null;
}
return utils.sendQueryContentEvent(utils.QUERY_CARET_RECT,
aOffset, 0, 0, 0);
@ -758,7 +758,7 @@ function synthesizeQueryTextRect(aOffset, aLength, aWindow)
{
var utils = _getDOMWindowUtils(aWindow);
if (!utils) {
return nsnull;
return null;
}
return utils.sendQueryContentEvent(utils.QUERY_TEXT_RECT,
aOffset, aLength, 0, 0);
@ -775,7 +775,7 @@ function synthesizeQueryEditorRect(aWindow)
{
var utils = _getDOMWindowUtils(aWindow);
if (!utils) {
return nsnull;
return null;
}
return utils.sendQueryContentEvent(utils.QUERY_EDITOR_RECT, 0, 0, 0, 0);
}
@ -792,7 +792,7 @@ function synthesizeCharAtPoint(aX, aY, aWindow)
{
var utils = _getDOMWindowUtils(aWindow);
if (!utils) {
return nsnull;
return null;
}
return utils.sendQueryContentEvent(utils.QUERY_CHARACTER_AT_POINT,
0, 0, aX, aY);

View File

@ -37,38 +37,82 @@
var EXPORTED_SYMBOLS = ['PepAPI'];
var results = {}; Components.utils.import('resource://pep/results.js', results);
var log = {}; Components.utils.import('resource://pep/logger.js', log);
var utils = {}; Components.utils.import('resource://pep/utils.js', utils);
var log = {}; Components.utils.import('resource://pep/logger.js', log);
var utils = {}; Components.utils.import('resource://pep/utils.js', utils);
var mozmill = {}; Components.utils.import('resource://mozmill/driver/mozmill.js', mozmill);
var securableModule = {};
Components.utils.import('resource://mozmill/stdlib/securable-module.js', securableModule);
const wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator);
.getService(Components.interfaces.nsIWindowMediator);
const ios = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
/**
* This is the API exposed to tests
* Any properties of this object will be directly injected into test scope
* Any properties of this object will be injected into test scope
* under the 'pep' namespace.
*/
function PepAPI(test) {
this.test = test;
this.log = new Log(this.test.name);
this.resultHandler = new results.ResultHandler(this.test.name);
this.file = Components.classes["@mozilla.org/file/local;1"]
.createInstance(Components.interfaces.nsILocalFile);
this.file.initWithPath(this.test.path);
}
/**
* Performs an action during which responsiveness is measured
*/
PepAPI.prototype.performAction = function(actionName, func) {
this.resultHandler.startAction(actionName);
func();
this.resultHandler.endAction();
};
/**
* Returns the most recently used window of windowType
*/
PepAPI.prototype.getWindow = function(windowType) {
if (windowType === undefined) {
windowType = "navigator:browser";
}
return wm.getMostRecentWindow(windowType);
};
/**
* Load a file on the local filesystem
* module - path on the local file of the module to load (no extension)
*/
PepAPI.prototype.require = function(module) {
let loader = new securableModule.Loader({
rootPaths: [ios.newFileURI(this.file.parent).spec],
defaultPrincipal: "system",
globals: { Cc: Components.classes,
Ci: Components.interfaces,
Cr: Components.results,
Cu: Components.utils,
// mozmill scopes for compatibility with mozmill shared libraries
// https://developer.mozilla.org/en/Mozmill_Tests/Shared_Modules
mozmill: mozmill,
// quick hack to keep backwards compatibility with mozmill 1.5.x
elementslib: mozmill.findElement,
findElement: mozmill.findElement,
persisted: {},
},
});
return loader.require(module);
};
/**
* Sleep for a number of milliseconds
*/
PepAPI.prototype.sleep = function(milliseconds) {
utils.sleep(milliseconds);
};
// Logging wrapper for tests
/**
* Logging wrapper for tests
*/
function Log(testName) {
this.testName = testName;
}

View File

@ -36,6 +36,7 @@
from optparse import OptionParser
from mozprofile import FirefoxProfile, ThunderbirdProfile, Profile
from mozprofile.permissions import ServerLocations
from mozrunner import FirefoxRunner, ThunderbirdRunner, Runner
from mozhttpd import MozHttpd
from manifestparser import TestManifest
@ -68,8 +69,22 @@ class Peptest():
self.logger = mozlog.getLogger('PEP')
# create the profile
enable_proxy = False
locations = ServerLocations()
if self.options.proxyLocations:
if not self.options.serverPath:
self.logger.warning('Can\'t set up proxy without server path')
else:
enable_proxy = True
locations.read(self.options.proxyLocations, False)
locations.add_host(host='127.0.0.1',
port=self.options.serverPort,
options='primary,privileged')
self.profile = self.profile_class(profile=self.options.profilePath,
addons=[os.path.join(here, 'extension')])
addons=[os.path.join(here, 'extension')],
locations=locations,
proxy=enable_proxy)
# fork a server to serve the test related files
if self.options.serverPath:
@ -143,7 +158,8 @@ class Peptest():
return
self.logger.debug('Starting server on port ' + str(self.options.serverPort))
self.server = MozHttpd(port=self.options.serverPort,
docroot=self.options.serverPath)
docroot=self.options.serverPath,
proxy_host_dirs=self.options.proxyHostDirs)
self.server.start(block=False)
def stop(self):
@ -302,6 +318,20 @@ class PeptestOptions(OptionParser):
help="path to the profile to use. "
"If none specified, a temporary profile is created")
self.add_option("--proxy",
action="store", type="string", dest="proxyLocations",
default=None,
help="path to a server-location file specifying "
"domains to proxy. --server-path must also be "
"specified.")
self.add_option("--proxy-host-dirs",
action="store_true", dest="proxyHostDirs",
default=False,
help="proxied requests are served from directories "
"named by requested host. --proxy must also be "
"specified.")
self.add_option("--server-port",
action="store", type="int", dest="serverPort",
default=8888,

View File

@ -1,7 +0,0 @@
[test_contextMenu.js]
[test_openBlankTab.js]
[test_openBookmarksMenu.js]
failThreshold = 40
[test_searchGoogle.js]
[test_openWindow.js]
[test_resizeWindow.js]

View File

@ -1,2 +1,6 @@
# All Firefox tests - include other manifests here
[include:examples/example_tests.ini]
[test_contextMenu.js]
[test_openBlankTab.js]
[test_openBookmarksMenu.js]
failThreshold = 40
[test_openWindow.js]
[test_resizeWindow.js]

View File

@ -0,0 +1,3 @@
http://mozilla.org privileged
http://mozillians.org privileged

View File

@ -0,0 +1,6 @@
<html>
<body>
This is a big test!
</body>
</html>

View File

@ -0,0 +1,6 @@
<html>
<body id="home">
Test mozilla home page.
</body>
</html>

View File

@ -0,0 +1,8 @@
<html>
<body id="home">
<div id="header">
Test mozilla home page.
</div>
</body>
</html>

View File

@ -0,0 +1,6 @@
<html>
<body>
Test mozillians home page.
</body>
</html>

View File

@ -41,7 +41,6 @@
* on various context menus in both content and chrome.
*/
// Import mozmill and initialize a controller
Components.utils.import("resource://mozmill/driver/mozmill.js");
let c = getBrowserController();
@ -49,29 +48,29 @@ let c = getBrowserController();
c.open("http://mozilla.org");
c.waitForPageLoad();
// Grab reference to element on page (this is the <body> element in this case)
let page = findElement.ID(c.tabs.activeTab, 'header');
// Perform our first action, reload.
// It is very important to only place things that we
// are interested in testing inside of a performAction call
pep.performAction('content_reload', function() {
page.rightClick();
page.keypress('r');
// controller.rootElement is the global window object
// wrapped inside of a MozMillElement
c.rootElement.rightClick();
c.rootElement.keypress('r');
});
c.waitForPageLoad();
c.open("http://google.com");
c.open("http://mozillians.org");
c.waitForPageLoad();
page = findElement.ID(c.tabs.activeTab, 'main');
// Perform our second action, go back
pep.performAction('content_back', function() {
page.rightClick();
page.keypress('b');
c.rootElement.rightClick();
c.rootElement.keypress('b');
});
// Bug 699400 - waitForPageLoad times out when pressing back button
c.sleep(100);
c.sleep(500);
// get a reference to the element with id 'home'
page = findElement.ID(c.tabs.activeTab, 'home');
// Perform our third action, scroll through context menu
pep.performAction('content_scroll', function() {

View File

@ -43,11 +43,10 @@ c.open('http://mozilla.org');
c.waitForPageLoad();
// Only put things you want to test for responsiveness inside a perfom action call
let page = findElement.ID(c.tabs.activeTab, "home");
pep.performAction('open_blank_tab', function() {
page.keypress('t', {'ctrlKey': true});
c.rootElement.keypress('t', {'ctrlKey': true});
});
pep.performAction('close_blank_tab', function() {
page.keypress('w', {'ctrlKey': true});
c.rootElement.keypress('w', {'ctrlKey': true});
});

View File

@ -35,7 +35,6 @@
*
* ***** END LICENSE BLOCK ***** */
// Import mozmill and initialize a controller
Components.utils.import("resource://mozmill/driver/mozmill.js");
let controller = getBrowserController();

View File

@ -39,12 +39,12 @@
Components.utils.import("resource://mozmill/driver/mozmill.js");
let controller = getBrowserController();
controller.open("http://google.ca");
controller.open("http://google.com");
controller.waitForPageLoad();
let textbox = findElement.ID(controller.tabs.activeTab, 'lst-ib');
let button = findElement.Name(controller.tabs.activeTab, 'btnK');
pep.performAction('enterText', function() {
pep.performAction('enter_text', function() {
textbox.sendKeys('foobar');
button.click();
});

View File

@ -271,8 +271,12 @@ xpcshell-tests-remote:
# Runs peptest, for usage see: https://developer.mozilla.org/en/Peptest#Running_Tests
RUN_PEPTEST = \
rm -f ./$@.log && \
$(PYTHON) _tests/peptest/runtests.py --binary=$(browser_path) $(PEPTEST_PATH_ARG) \
--log-file=./$@.log $(SYMBOLS_PATH) $(EXTRA_TEST_ARGS)
$(PYTHON) _tests/peptest/runtests.py --binary=$(browser_path) \
$(PEPTEST_PATH_ARG) \
--proxy=_tests/peptest/tests/firefox/server-locations.txt \
--proxy-host-dirs \
--server-path=_tests/peptest/tests/firefox/server \
--log-file=./$@.log $(SYMBOLS_PATH) $(EXTRA_TEST_ARGS)
peptest:
$(RUN_PEPTEST)