Merge the last PGO-green inbound changeset to m-c.

This commit is contained in:
Ryan VanderMeulen 2012-07-02 22:00:17 -04:00
commit fa9c462002
67 changed files with 823 additions and 433 deletions

View File

@ -70,9 +70,9 @@ libs:: $(_TEST_FILES)
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
check::
PYTHONDONTWRITEBYTECODE=1 $(PYTHON_PATH) $(PLY_INCLUDE) \
$(srcdir)/../parser/runtests.py
PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \
$(PLY_INCLUDE) $(srcdir)/../parser/runtests.py
check-interactive:
PYTHONDONTWRITEBYTECODE=1 $(PYTHON_PATH) $(PLY_INCLUDE) \
$(srcdir)/../parser/runtests.py -q
PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \
$(PLY_INCLUDE) $(srcdir)/../parser/runtests.py -q

View File

@ -14,7 +14,7 @@ if (DEBUG) {
debug = function (s) {}
}
const Cu = Components.utils;
const Cu = Components.utils;
const Cc = Components.classes;
const Ci = Components.interfaces;
@ -76,7 +76,7 @@ ContactDB.prototype = {
} else if (currVersion == 1) {
debug("upgrade 1");
// Create a new scheme for the tel field. We move from an array of tel-numbers to an array of
// Create a new scheme for the tel field. We move from an array of tel-numbers to an array of
// ContactTelephone.
if (!objectStore) {
objectStore = aTransaction.objectStore(STORE_NAME);
@ -85,7 +85,7 @@ ContactDB.prototype = {
objectStore.deleteIndex("tel");
// Upgrade existing tel field in the DB.
objectStore.openCursor().onsuccess = function(event) {
objectStore.openCursor().onsuccess = function(event) {
let cursor = event.target.result;
if (cursor) {
debug("upgrade tel1: " + JSON.stringify(cursor.value));
@ -95,7 +95,7 @@ ContactDB.prototype = {
cursor.update(cursor.value);
debug("upgrade tel2: " + JSON.stringify(cursor.value));
cursor.continue();
}
}
};
// Create new searchable indexes.
@ -155,7 +155,7 @@ ContactDB.prototype = {
for (let i = 0; i <= aContact.properties[field].length; i++) {
if (aContact.properties[field][i]) {
if (field == "tel") {
// Special case telephone number.
// Special case telephone number.
// "+1-234-567" should also be found with 1234, 234-56, 23456
// Chop off the first characters
@ -308,21 +308,21 @@ ContactDB.prototype = {
let request;
if (key == "id") {
// store.get would return an object and not an array
request = store.getAll(options.filterValue);
request = store.mozGetAll(options.filterValue);
} else if (key == "category") {
let index = store.index(key);
request = index.getAll(options.filterValue, limit);
request = index.mozGetAll(options.filterValue, limit);
} else if (options.filterOp == "equals") {
debug("Getting index: " + key);
// case sensitive
let index = store.index(key);
request = index.getAll(options.filterValue, limit);
request = index.mozGetAll(options.filterValue, limit);
} else {
// not case sensitive
let tmp = options.filterValue.toLowerCase();
let range = this._global.IDBKeyRange.bound(tmp, tmp + "\uFFFF");
let index = store.index(key + "LowerCase");
request = index.getAll(range, limit);
request = index.mozGetAll(range, limit);
}
if (!txn.result)
txn.result = {};
@ -341,7 +341,7 @@ ContactDB.prototype = {
txn.result = {};
// Sorting functions takes care of limit if set.
let limit = options.sortBy === 'undefined' ? options.filterLimit : null;
store.getAll(null, limit).onsuccess = function (event) {
store.mozGetAll(null, limit).onsuccess = function (event) {
debug("Request successful. Record count:", event.target.result.length);
for (let i in event.target.result)
txn.result[event.target.result[i].id] = this.makeExport(event.target.result[i]);

View File

@ -379,7 +379,7 @@ SmsDatabaseService.prototype = {
requestId, Ci.nsISmsRequestManager.INTERNAL_ERROR);
return;
}
let request = store.getAll(messageId);
let request = store.mozGetAll(messageId);
txn.oncomplete = function oncomplete() {
if (DEBUG) debug("Transaction " + txn + " completed.");

View File

@ -238,17 +238,6 @@ ArrayBufferObject::allocateSlots(JSContext *cx, uint32_t size, uint8_t *contents
return true;
}
static JSObject *
DelegateObject(JSContext *cx, HandleObject obj)
{
if (!obj->getPrivate()) {
JSObject *delegate = NewObjectWithGivenProto(cx, &ObjectClass, obj->getProto(), NULL);
obj->setPrivate(delegate);
return delegate;
}
return static_cast<JSObject*>(obj->getPrivate());
}
JSObject *
ArrayBufferObject::create(JSContext *cx, uint32_t nbytes, uint8_t *contents)
{
@ -318,7 +307,7 @@ ArrayBufferObject::obj_lookupGeneric(JSContext *cx, HandleObject obj, HandleId i
return true;
}
RootedObject delegate(cx, DelegateObject(cx, obj));
RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
if (!delegate)
return false;
@ -360,7 +349,7 @@ JSBool
ArrayBufferObject::obj_lookupElement(JSContext *cx, HandleObject obj, uint32_t index,
JSObject **objp, JSProperty **propp)
{
RootedObject delegate(cx, DelegateObject(cx, obj));
RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
if (!delegate)
return false;
@ -404,7 +393,7 @@ ArrayBufferObject::obj_defineGeneric(JSContext *cx, HandleObject obj, HandleId i
AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
RootedObject delegate(cx, DelegateObject(cx, obj));
RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
if (!delegate)
return false;
return baseops::DefineGeneric(cx, delegate, id, v, getter, setter, attrs);
@ -425,7 +414,7 @@ ArrayBufferObject::obj_defineElement(JSContext *cx, HandleObject obj, uint32_t i
{
AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
RootedObject delegate(cx, DelegateObject(cx, obj));
RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
if (!delegate)
return false;
return baseops::DefineElement(cx, delegate, index, v, getter, setter, attrs);
@ -451,7 +440,7 @@ ArrayBufferObject::obj_getGeneric(JSContext *cx, HandleObject obj, HandleObject
return true;
}
nobj = DelegateObject(cx, nobj);
nobj = ArrayBufferDelegate(cx, nobj);
if (!nobj)
return false;
return baseops::GetProperty(cx, nobj, receiver, id, vp);
@ -477,7 +466,7 @@ ArrayBufferObject::obj_getProperty(JSContext *cx, HandleObject obj,
return true;
}
nobj = DelegateObject(cx, nobj);
nobj = ArrayBufferDelegate(cx, nobj);
if (!nobj)
return false;
Rooted<jsid> id(cx, NameToId(name));
@ -489,7 +478,7 @@ ArrayBufferObject::obj_getElement(JSContext *cx, HandleObject obj,
HandleObject receiver, uint32_t index, Value *vp)
{
RootedObject buffer(cx, getArrayBuffer(obj));
RootedObject delegate(cx, DelegateObject(cx, buffer));
RootedObject delegate(cx, ArrayBufferDelegate(cx, buffer));
if (!delegate)
return false;
return baseops::GetElement(cx, delegate, receiver, index, vp);
@ -500,7 +489,7 @@ ArrayBufferObject::obj_getElementIfPresent(JSContext *cx, HandleObject obj, Hand
uint32_t index, Value *vp, bool *present)
{
RootedObject buffer(cx, getArrayBuffer(obj));
RootedObject delegate(cx, DelegateObject(cx, buffer));
RootedObject delegate(cx, ArrayBufferDelegate(cx, buffer));
if (!delegate)
return false;
return delegate->getElementIfPresent(cx, receiver, index, vp, present);
@ -520,7 +509,7 @@ ArrayBufferObject::obj_setGeneric(JSContext *cx, HandleObject obj, HandleId id,
if (JSID_IS_ATOM(id, cx->runtime->atomState.byteLengthAtom))
return true;
RootedObject delegate(cx, DelegateObject(cx, obj));
RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
if (!delegate)
return false;
@ -582,7 +571,7 @@ JSBool
ArrayBufferObject::obj_setElement(JSContext *cx, HandleObject obj,
uint32_t index, Value *vp, JSBool strict)
{
RootedObject delegate(cx, DelegateObject(cx, obj));
RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
if (!delegate)
return false;
@ -606,7 +595,7 @@ ArrayBufferObject::obj_getGenericAttributes(JSContext *cx, HandleObject obj,
return true;
}
RootedObject delegate(cx, DelegateObject(cx, obj));
RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
if (!delegate)
return false;
return baseops::GetAttributes(cx, delegate, id, attrsp);
@ -624,7 +613,7 @@ JSBool
ArrayBufferObject::obj_getElementAttributes(JSContext *cx, HandleObject obj,
uint32_t index, unsigned *attrsp)
{
RootedObject delegate(cx, DelegateObject(cx, obj));
RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
if (!delegate)
return false;
return baseops::GetElementAttributes(cx, delegate, index, attrsp);
@ -648,7 +637,7 @@ ArrayBufferObject::obj_setGenericAttributes(JSContext *cx, HandleObject obj,
return false;
}
RootedObject delegate(cx, DelegateObject(cx, obj));
RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
if (!delegate)
return false;
return baseops::SetAttributes(cx, delegate, id, attrsp);
@ -666,7 +655,7 @@ JSBool
ArrayBufferObject::obj_setElementAttributes(JSContext *cx, HandleObject obj,
uint32_t index, unsigned *attrsp)
{
RootedObject delegate(cx, DelegateObject(cx, obj));
RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
if (!delegate)
return false;
return baseops::SetElementAttributes(cx, delegate, index, attrsp);
@ -689,7 +678,7 @@ ArrayBufferObject::obj_deleteProperty(JSContext *cx, HandleObject obj,
return true;
}
RootedObject delegate(cx, DelegateObject(cx, obj));
RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
if (!delegate)
return false;
return baseops::DeleteProperty(cx, delegate, name, rval, strict);
@ -699,7 +688,7 @@ JSBool
ArrayBufferObject::obj_deleteElement(JSContext *cx, HandleObject obj,
uint32_t index, Value *rval, JSBool strict)
{
RootedObject delegate(cx, DelegateObject(cx, obj));
RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
if (!delegate)
return false;
return baseops::DeleteElement(cx, delegate, index, rval, strict);
@ -709,7 +698,7 @@ JSBool
ArrayBufferObject::obj_deleteSpecial(JSContext *cx, HandleObject obj,
HandleSpecialId sid, Value *rval, JSBool strict)
{
RootedObject delegate(cx, DelegateObject(cx, obj));
RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
if (!delegate)
return false;
return baseops::DeleteSpecial(cx, delegate, sid, rval, strict);

View File

@ -449,8 +449,8 @@ DenseElementsHeader::defineElement(JSContext *cx, Handle<ObjectImpl*> obj, uint3
return true;
}
static JSObject *
ArrayBufferDelegate(JSContext *cx, Handle<ObjectImpl*> obj)
JSObject *
js::ArrayBufferDelegate(JSContext *cx, Handle<ObjectImpl*> obj)
{
MOZ_ASSERT(obj->hasClass(&ArrayBufferClass));
if (obj->getPrivate())

View File

@ -1320,6 +1320,9 @@ Downcast(Handle<ObjectImpl*> obj)
return Handle<JSObject*>::fromMarkedLocation(reinterpret_cast<JSObject* const*>(obj.address()));
}
extern JSObject *
ArrayBufferDelegate(JSContext *cx, Handle<ObjectImpl*> obj);
/* Generic [[GetOwnProperty]] method. */
bool
GetOwnElement(JSContext *cx, Handle<ObjectImpl*> obj, uint32_t index, unsigned resolveFlags,

View File

@ -25,7 +25,7 @@ namespace js {
* and saved versions. If deserialization fails, the data should be
* invalidated if possible.
*/
static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 118);
static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 119);
class XDRBuffer {
public:

View File

@ -246,6 +246,8 @@ public class Tabs implements GeckoEventListener {
selectTab(tab.getId());
if (message.getBoolean("delayLoad"))
tab.setState(Tab.STATE_DELAYED);
if (message.getBoolean("desktopMode"))
tab.setDesktopMode(true);
} else if (event.equals("Tab:Close")) {
Tab tab = getTab(message.getInt("tabID"));
closeTab(tab);

View File

@ -2083,7 +2083,6 @@ function Tab(aURL, aParams) {
this.browser = null;
this.id = 0;
this.showProgress = true;
this.create(aURL, aParams);
this._zoom = 1.0;
this._drawZoom = 1.0;
this.userScrollPos = { x: 0, y: 0 };
@ -2093,6 +2092,8 @@ function Tab(aURL, aParams) {
this.clickToPlayPluginsActivated = false;
this.desktopMode = false;
this.originalURI = null;
this.create(aURL, aParams);
}
Tab.prototype = {
@ -2122,6 +2123,7 @@ Tab.prototype = {
} catch (e) {}
this.id = ++gTabIDFactory;
this.desktopMode = ("desktopMode" in aParams) ? aParams.desktopMode : false;
let message = {
gecko: {
@ -2132,7 +2134,8 @@ Tab.prototype = {
external: ("external" in aParams) ? aParams.external : false,
selected: ("selected" in aParams) ? aParams.selected : true,
title: aParams.title || aURL,
delayLoad: aParams.delayLoad || false
delayLoad: aParams.delayLoad || false,
desktopMode: this.desktopMode
}
};
sendMessageToJava(message);

View File

@ -405,7 +405,7 @@ SessionStore.prototype = {
let data = { entries: entries, index: index };
delete aBrowser.__SS_data;
this._collectTabData(aBrowser, data);
this._collectTabData(aWindow, aBrowser, data);
this.saveStateNow();
}
@ -478,7 +478,7 @@ SessionStore.prototype = {
return data;
},
_collectTabData: function ss__collectTabData(aBrowser, aHistory) {
_collectTabData: function ss__collectTabData(aWindow, aBrowser, aHistory) {
// If this browser is being restored, skip any session save activity
if (aBrowser.__SS_restore)
return;
@ -489,6 +489,7 @@ SessionStore.prototype = {
tabData.entries = aHistory.entries;
tabData.index = aHistory.index;
tabData.attributes = { image: aBrowser.mIconURL };
tabData.desktopMode = aWindow.BrowserApp.getTabForBrowser(aBrowser).desktopMode;
aBrowser.__SS_data = tabData;
},
@ -948,7 +949,12 @@ SessionStore.prototype = {
let entry = tabData.entries[tabData.index - 1];
// Add a tab, but don't load the URL until we need to
let params = { selected: isSelected, delayLoad: true, title: entry.title };
let params = {
selected: isSelected,
delayLoad: true,
title: entry.title,
desktopMode: tabData.desktopMode == true
};
let tab = window.BrowserApp.addTab(entry.url, params);
if (isSelected) {

View File

@ -1 +1 @@
http://hg.mozilla.org/projects/addon-sdk/archive/8d6150ab4903.tar.bz2
http://hg.mozilla.org/projects/addon-sdk/archive/ff6864587606.tar.bz2

View File

@ -1,7 +1,3 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
Universal manifests for Mozilla test harnesses
# What is ManifestDestiny?

View File

@ -1,5 +1,5 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
from manifestparser import *

View File

@ -1,7 +1,8 @@
#!/usr/bin/env python
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
"""
Mozilla universal manifest parser
@ -695,8 +696,8 @@ class TestManifest(ManifestParser):
def filter(self, values, tests):
"""
filter on a specific list tag, e.g.:
run-if = os == 'win' || os == 'linux'
skip-if = os == 'mac'
run-if.os = win linux
skip-if.os = mac
"""
# tags:

View File

@ -1,7 +1,6 @@
#!/usr/bin/env python
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
# The real details are in manifestparser.py; this is just a front-end
# BUT use this file when you want to distribute to python!

View File

@ -1,8 +1,6 @@
#!/usr/bin/env python
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
"""tests for ManifestDestiny"""

View File

@ -36,8 +36,6 @@ Test equality::
True
>>> parse("false == false")
True
>>> parse("false == false")
True
>>> parse("1 == 1")
True
>>> parse("100 == 100")

View File

@ -1,5 +1 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
basic python webserver, tested with talos

View File

@ -1,6 +1,6 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
from mozhttpd import MozHttpd, Request, RequestHandler, main
from handlers import json_response

View File

@ -1,6 +1,6 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
try:
import json

View File

@ -1,6 +1,6 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
import os
import socket

View File

@ -1,8 +1,8 @@
#!/usr/bin/env python
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
import BaseHTTPServer
import SimpleHTTPServer
@ -16,6 +16,7 @@ import os
import urllib
import urlparse
import re
import iface
from SocketServer import ThreadingMixIn
class EasyServer(ThreadingMixIn, BaseHTTPServer.HTTPServer):
@ -224,27 +225,35 @@ class MozHttpd(object):
def main(args=sys.argv[1:]):
# parse command line options
from optparse import OptionParser
parser = OptionParser()
parser.add_option('-p', '--port', dest='port',
parser.add_option('-p', '--port', dest='port',
type="int", default=8888,
help="port to run the server on [DEFAULT: %default]")
parser.add_option('-H', '--host', dest='host',
default='127.0.0.1',
help="host [DEFAULT: %default]")
parser.add_option('-i', '--external-ip', action="store_true",
dest='external_ip', default=False,
help="find and use external ip for host")
parser.add_option('-d', '--docroot', dest='docroot',
default=os.getcwd(),
help="directory to serve files from [DEFAULT: %default]")
options, args = parser.parse_args(args)
if args:
parser.error("mozhttpd does not take any arguments")
parser.print_help()
parser.exit()
if options.external_ip:
host = iface.get_lan_ip()
else:
host = options.host
# create the server
kwargs = options.__dict__.copy()
server = MozHttpd(**kwargs)
server = MozHttpd(host=host, port=options.port, docroot=options.docroot)
print "Serving '%s' at %s:%s" % (server.docroot, server.host, server.port)
server.start(block=True)

View File

@ -1,6 +1,6 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
import os
from setuptools import setup, find_packages
@ -11,7 +11,7 @@ try:
except IOError:
description = None
version = '0.2'
version = '0.3'
deps = []

View File

@ -1,8 +1,6 @@
#!/usr/bin/env python
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
import mozhttpd
import urllib2

View File

@ -1,8 +1,6 @@
#!/usr/bin/env python
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
import mozhttpd
import urllib2

View File

@ -1,7 +1,3 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
Throughout [mozmill](https://developer.mozilla.org/en/Mozmill)
and other Mozilla python code, checking the underlying
platform is done in many different ways. The various checks needed

View File

@ -1,5 +1,5 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
from mozinfo import *

View File

@ -1,8 +1,8 @@
#!/usr/bin/env python
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
"""
file for interface to transform introspected system information to a format

View File

@ -1,6 +1,6 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
import os

View File

@ -1,37 +1,58 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
[Mozinstall](https://github.com/mozilla/mozbase/tree/master/mozinstall)
is a python package for installing Mozilla applications on various platforms.
[Mozinstall](https://github.com/mozilla/mozbase/tree/master/mozinstall) is a
python package for installing and uninstalling Mozilla applications on
various platforms.
For example, depending on the platform, Firefox can be distributed as a
zip, tar.bz2, exe or dmg file or cloned from a repository. Mozinstall takes the
hassle out of extracting and/or running these files and for convenience returns
the full path to the application's binary in the install directory. In the case
that mozinstall is invoked from the command line, the binary path will be
printed to stdout.
zip, tar.bz2, exe, or dmg file or cloned from a repository. Mozinstall takes
the hassle out of extracting and/or running these files and for convenience
returns the full path to the install directory. In the case that mozinstall
is invoked from the command line, the binary path will be printed to stdout.
To remove an installed application the uninstaller can be used. It requires
the installation path of the application and will remove all the installed
files. On Windows the uninstaller will be tried first.
# Usage
Mozinstall can be used as API or via the CLI commands.
For command line options run mozinstall --help
Mozinstall's main function is the install method
## API
An application can be installed by running the commands below. The install
method will return the installation path of the application.
import mozinstall
mozinstall.install('path_to_install_file', dest='path_to_install_folder')
path = mozinstall.install(%installer%, %install_folder%)
The dest parameter defaults to the directory in which the install file is located.
The install method accepts a third parameter called apps which tells mozinstall which
binary to search for. By default it will search for 'firefox', 'thunderbird' and 'fennec'
so unless you are installing a different application, this parameter is unnecessary.
To retrieve the real binary call get_binary with the path and
the application name as arguments:
mozinstall.get_binary(path, 'firefox')
If the application is not needed anymore the uninstaller will remove all
traces from the system:
mozinstall.uninstall(path)
## CLI
The installer can also be used as a command line tool:
$ mozinstall -d firefox %installer%
Whereby the directory option is optional and will default to the current
working directory. If the installation was successful the path to the
binary will be printed to stdout.
Also the uninstaller can be called via the command line:
$ mozuninstall %install_path%
# Error Handling
Mozinstall throws two different types of exceptions:
Mozinstall throws different types of exceptions:
- mozinstall.InvalidSource is thrown when the source is not a recognized file type (zip, exe, tar.bz2, tar.gz, dmg)
- mozinstall.InstallError is thrown when the installation fails for any reason. A traceback is provided.
- mozinstall.InvalidBinary is thrown when the binary cannot be found.
- mozinstall.InvalidSource is thrown when the source is not a recognized file type (zip, exe, tar.bz2, tar.gz, dmg).
# Dependencies

View File

@ -1,4 +1,5 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
from mozinstall import *

View File

@ -1,201 +1,370 @@
#!/usr/bin/env python
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
from optparse import OptionParser
"""Module to handle the installation and uninstallation of Gecko based
applications across platforms.
"""
import mozinfo
import subprocess
import zipfile
import tarfile
import sys
from optparse import OptionParser
import os
import shutil
import subprocess
import sys
import tarfile
import time
import zipfile
_default_apps = ["firefox",
"thunderbird",
"fennec"]
if mozinfo.isMac:
from plistlib import readPlist
TIMEOUT_UNINSTALL = 60
class InstallError(Exception):
"""Thrown when installation fails. Includes traceback if available."""
class InvalidBinary(Exception):
"""Thrown when the binary cannot be found after the installation."""
class InvalidSource(Exception):
"""Thrown when the specified source is not a recognized file type.
Supported types:
Linux: tar.gz, tar.bz2
Mac: dmg
Windows: zip, exe
def install(src, dest=None, apps=_default_apps):
"""
Installs a zip, exe, tar.gz, tar.bz2 or dmg file
src - the path to the install file
dest - the path to install to [default is os.path.dirname(src)]
returns - the full path to the binary in the installed folder
or None if the binary cannot be found
class UninstallError(Exception):
"""Thrown when uninstallation fails. Includes traceback if available."""
def get_binary(path, app_name):
"""Find the binary in the specified path, and return its path. If binary is
not found throw an InvalidBinary exception.
Arguments:
path -- the path within to search for the binary
app_name -- application binary without file extension to look for
"""
binary = None
# On OS X we can get the real binary from the app bundle
if mozinfo.isMac:
plist = '%s/Contents/Info.plist' % path
assert os.path.isfile(plist), '"%s" has not been found.' % plist
binary = os.path.join(path, 'Contents/MacOS/',
readPlist(plist)['CFBundleExecutable'])
else:
app_name = app_name.lower()
if mozinfo.isWin:
app_name = app_name + '.exe'
for root, dirs, files in os.walk(path):
for filename in files:
# os.access evaluates to False for some reason, so not using it
if filename.lower() == app_name:
binary = os.path.realpath(os.path.join(root, filename))
break
if not binary:
# The expected binary has not been found. Make sure we clean the
# install folder to remove any traces
shutil.rmtree(path)
raise InvalidBinary('"%s" does not contain a valid binary.' % path)
return binary
def install(src, dest):
"""Install a zip, exe, tar.gz, tar.bz2 or dmg file, and return the path of
the installation folder.
Arguments:
src -- the path to the install file
dest -- the path to install to (to ensure we do not overwrite any existent
files the folder should not exist yet)
"""
src = os.path.realpath(src)
assert(os.path.isfile(src))
if not dest:
dest = os.path.dirname(src)
dest = os.path.realpath(dest)
if not is_installer(src):
raise InvalidSource(src + ' is not a recognized file type ' +
'(zip, exe, tar.gz, tar.bz2 or dmg)')
if not os.path.exists(dest):
os.makedirs(dest)
trbk = None
try:
install_dir = None
if zipfile.is_zipfile(src) or tarfile.is_tarfile(src):
install_dir = _extract(src, dest)[0]
elif mozinfo.isMac and src.lower().endswith(".dmg"):
elif src.lower().endswith('.dmg'):
install_dir = _install_dmg(src, dest)
elif mozinfo.isWin and os.access(src, os.X_OK):
elif src.lower().endswith('.exe'):
install_dir = _install_exe(src, dest)
else:
raise InvalidSource(src + " is not a recognized file type " +
"(zip, exe, tar.gz, tar.bz2 or dmg)")
except InvalidSource, e:
raise
return install_dir
except Exception, e:
cls, exc, trbk = sys.exc_info()
install_error = InstallError("Failed to install %s" % src)
raise install_error.__class__, install_error, trbk
error = InstallError('Failed to install "%s"' % src)
raise InstallError, error, trbk
finally:
# trbk won't get GC'ed due to circular reference
# http://docs.python.org/library/sys.html#sys.exc_info
del trbk
if install_dir:
return get_binary(install_dir, apps=apps)
def get_binary(path, apps=_default_apps):
def is_installer(src):
"""Tests if the given file is a valid installer package.
Supported types:
Linux: tar.gz, tar.bz2
Mac: dmg
Windows: zip, exe
Arguments:
src -- the path to the install file
"""
Finds the binary in the specified path
path - the path within which to search for the binary
returns - the full path to the binary in the folder
or None if the binary cannot be found
src = os.path.realpath(src)
if not os.path.isfile(src):
return False
if mozinfo.isLinux:
return tarfile.is_tarfile(src)
elif mozinfo.isMac:
return src.lower().endswith('.dmg')
elif mozinfo.isWin:
return src.lower().endswith('.exe') or zipfile.is_zipfile(src)
def uninstall(install_folder):
"""Uninstalls the application in the specified path. If it has been
installed via an installer on Windows, use the uninstaller first.
Arguments:
install_folder -- the path of the installation folder
"""
install_folder = os.path.realpath(install_folder)
assert os.path.isdir(install_folder), \
'installation folder "%s" exists.' % install_folder
# On Windows we have to use the uninstaller. If it's not available fallback
# to the directory removal code
if mozinfo.isWin:
apps = [app + ".exe" for app in apps]
for root, dirs, files in os.walk(path):
for filename in files:
# os.access evaluates to False for some reason, so not using it
if filename in apps:
return os.path.realpath(os.path.join(root, filename))
uninstall_folder = '%s\uninstall' % install_folder
log_file = '%s\uninstall.log' % uninstall_folder
if os.path.isfile(log_file):
trbk = None
try:
cmdArgs = ['%s\uninstall\helper.exe' % install_folder, '/S']
result = subprocess.call(cmdArgs)
if not result is 0:
raise Exception('Execution of uninstaller failed.')
# The uninstaller spawns another process so the subprocess call
# returns immediately. We have to wait until the uninstall
# folder has been removed or until we run into a timeout.
end_time = time.time() + TIMEOUT_UNINSTALL
while os.path.exists(uninstall_folder):
time.sleep(1)
if time.time() > end_time:
raise Exception('Failure removing uninstall folder.')
except Exception, e:
cls, exc, trbk = sys.exc_info()
error = UninstallError('Failed to uninstall %s' % install_folder)
raise UninstallError, error, trbk
finally:
# trbk won't get GC'ed due to circular reference
# http://docs.python.org/library/sys.html#sys.exc_info
del trbk
# Ensure that we remove any trace of the installation. Even the uninstaller
# on Windows leaves files behind we have to explicitely remove.
shutil.rmtree(install_folder)
def _extract(src, dest):
"""Extract a tar or zip file into the destination folder and return the
application folder.
Arguments:
src -- archive which has to be extracted
dest -- the path to extract to
def _extract(path, extdir=None, delete=False):
"""
Takes in a tar or zip file and extracts it to extdir
If extdir is not specified, extracts to os.path.dirname(path)
If delete is set to True, deletes the bundle at path
Returns the list of top level files that were extracted
"""
assert not os.path.isfile(extdir), "extdir cannot be a file"
if extdir is None:
extdir = os.path.dirname(path)
elif not os.path.isdir(extdir):
os.makedirs(extdir)
if zipfile.is_zipfile(path):
bundle = zipfile.ZipFile(path)
if zipfile.is_zipfile(src):
bundle = zipfile.ZipFile(src)
namelist = bundle.namelist()
if hasattr(bundle, 'extractall'):
bundle.extractall(path=extdir)
# zipfile.extractall doesn't exist in Python 2.5
# zipfile.extractall doesn't exist in Python 2.5
bundle.extractall(path=dest)
else:
for name in namelist:
filename = os.path.realpath(os.path.join(extdir, name))
if name.endswith("/"):
filename = os.path.realpath(os.path.join(dest, name))
if name.endswith('/'):
os.makedirs(filename)
else:
path = os.path.dirname(filename)
if not os.path.isdir(path):
os.makedirs(path)
dest = open(filename, "wb")
dest = open(filename, 'wb')
dest.write(bundle.read(name))
dest.close()
elif tarfile.is_tarfile(path):
bundle = tarfile.open(path)
elif tarfile.is_tarfile(src):
bundle = tarfile.open(src)
namelist = bundle.getnames()
if hasattr(bundle, 'extractall'):
bundle.extractall(path=extdir)
# tarfile.extractall doesn't exist in Python 2.4
# tarfile.extractall doesn't exist in Python 2.4
bundle.extractall(path=dest)
else:
for name in namelist:
bundle.extract(name, path=extdir)
bundle.extract(name, path=dest)
else:
return
bundle.close()
if delete:
os.remove(path)
# namelist returns paths with forward slashes even in windows
top_level_files = [os.path.join(extdir, name) for name in namelist
top_level_files = [os.path.join(dest, name) for name in namelist
if len(name.rstrip('/').split('/')) == 1]
# namelist doesn't include folders, append these to the list
for name in namelist:
root = os.path.join(extdir, name[:name.find('/')])
root = os.path.join(dest, name[:name.find('/')])
if root not in top_level_files:
top_level_files.append(root)
return top_level_files
def _install_dmg(src, dest):
proc = subprocess.Popen("hdiutil attach " + src,
shell=True,
stdout=subprocess.PIPE)
"""Extract a dmg file into the destination folder and return the
application folder.
Arguments:
src -- DMG image which has to be extracted
dest -- the path to extract to
"""
try:
proc = subprocess.Popen('hdiutil attach %s' % src,
shell=True,
stdout=subprocess.PIPE)
for data in proc.communicate()[0].split():
if data.find("/Volumes/") != -1:
if data.find('/Volumes/') != -1:
appDir = data
break
for appFile in os.listdir(appDir):
if appFile.endswith(".app"):
appName = appFile
break
if appFile.endswith('.app'):
appName = appFile
break
mounted_path = os.path.join(appDir, appName)
dest = os.path.join(dest, appName)
assert not os.path.isfile(dest)
if not os.path.isdir(dest):
os.makedirs(dest)
subprocess.call("cp -r " +
os.path.join(appDir,appName, "*") + " " + dest,
shell=True)
# copytree() would fail if dest already exists.
if os.path.exists(dest):
raise InstallError('App bundle "%s" already exists.' % dest)
shutil.copytree(mounted_path, dest, False)
finally:
subprocess.call("hdiutil detach " + appDir + " -quiet",
subprocess.call('hdiutil detach %s -quiet' % appDir,
shell=True)
return dest
def _install_exe(src, dest):
"""Run the MSI installer to silently install the application into the
destination folder. Return the folder path.
Arguments:
src -- MSI installer to be executed
dest -- the path to install to
"""
# The installer doesn't automatically create a sub folder. Lets guess the
# best name from the src file name
filename = os.path.basename(src)
dest = os.path.join(dest, filename.split('.')[0])
# possibly gets around UAC in vista (still need to run as administrator)
os.environ['__compat_layer'] = "RunAsInvoker"
cmd = [src, "/S", "/D=" + os.path.realpath(dest)]
subprocess.call(cmd)
os.environ['__compat_layer'] = 'RunAsInvoker'
cmd = [src, '/S', '/D=%s' % os.path.realpath(dest)]
# As long as we support Python 2.4 check_call will not be available.
result = subprocess.call(cmd)
if not result is 0:
raise Exception('Execution of installer failed.')
return dest
def cli(argv=sys.argv[1:]):
parser = OptionParser()
parser.add_option("-s", "--source",
dest="src",
help="Path to installation file. "
"Accepts: zip, exe, tar.bz2, tar.gz, and dmg")
parser.add_option("-d", "--destination",
dest="dest",
default=None,
help="[optional] Directory to install application into")
parser.add_option("--app", dest="app",
action="append",
default=_default_apps,
help="[optional] Application being installed. "
"Should be lowercase, e.g: "
"firefox, fennec, thunderbird, etc.")
def install_cli(argv=sys.argv[1:]):
parser = OptionParser(usage="usage: %prog [options] installer")
parser.add_option('-d', '--destination',
dest='dest',
default=os.getcwd(),
help='Directory to install application into. '
'[default: "%default"]')
parser.add_option('--app', dest='app',
default='firefox',
help='Application being installed. [default: %default]')
(options, args) = parser.parse_args(argv)
if not options.src or not os.path.exists(options.src):
print "Error: must specify valid source"
return 2
if not len(args) == 1:
parser.error('An installer file has to be specified.')
src = args[0]
# Run it
if os.path.isdir(options.src):
binary = get_binary(options.src, apps=options.app)
if os.path.isdir(src):
binary = get_binary(src, app_name=options.app)
else:
binary = install(options.src, dest=options.dest, apps=options.app)
install_path = install(src, options.dest)
binary = get_binary(install_path, app_name=options.app)
print binary
class InvalidSource(Exception):
"""
Thrown when the specified source is not a recognized
file type (zip, exe, tar.gz, tar.bz2 or dmg)
"""
class InstallError(Exception):
"""
Thrown when the installation fails. Includes traceback
if available.
"""
def uninstall_cli(argv=sys.argv[1:]):
parser = OptionParser(usage="usage: %prog install_path")
(options, args) = parser.parse_args(argv)
if not len(args) == 1:
parser.error('An installation path has to be specified.')
# Run it
uninstall(argv[0])
if __name__ == "__main__":
sys.exit(cli())

View File

@ -1,6 +1,6 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
import os
from setuptools import setup, find_packages
@ -11,27 +11,29 @@ try:
except IOError:
description = None
version = '0.3'
version = '1.1'
deps = ['mozinfo']
deps = ['mozinfo==0.3.3']
setup(name='mozInstall',
version=version,
description="This is a utility package for installing Mozilla applications on various platforms.",
description="This is a utility package for installing and uninstalling "
"Mozilla applications on various platforms.",
long_description=description,
# Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
classifiers=['Environment :: Console',
'Intended Audience :: Developers',
'License :: OSI Approved :: Mozilla Public License 1.1 (MPL 1.1)',
'License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)',
'Natural Language :: English',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Topic :: Software Development :: Libraries :: Python Modules',
], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
],
keywords='mozilla',
author='mdas',
author_email='mdas@mozilla.com',
author='Mozilla Automation and Tools team',
author_email='tools@lists.mozilla.org',
url='https://github.com/mozilla/mozbase',
license='MPL',
license='MPL 2.0',
packages=find_packages(exclude=['legacy']),
include_package_data=True,
zip_safe=False,
@ -39,6 +41,7 @@ setup(name='mozInstall',
entry_points="""
# -*- Entry points: -*-
[console_scripts]
mozinstall = mozinstall:cli
mozinstall = mozinstall:install_cli
mozuninstall = mozinstall:uninstall_cli
""",
)

View File

@ -1,7 +1,3 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
[Mozlog](https://github.com/mozilla/mozbase/tree/master/mozlog)
is a python package intended to simplify and standardize logs in the Mozilla universe.
It wraps around python's [logging](http://docs.python.org/library/logging.html)

View File

@ -1,4 +1,5 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
from logger import *

View File

@ -1,12 +1,13 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
from logging import getLogger as getSysLogger
from logging import *
# Some of the build slave environments don't see the following when doing
# 'from logging import *'
# see https://bugzilla.mozilla.org/show_bug.cgi?id=700415#c35
from logging import getLoggerClass, addLevelName, setLoggerClass
from logging import getLoggerClass, addLevelName, setLoggerClass, shutdown
_default_level = INFO
_LoggerClass = getLoggerClass()

View File

@ -1,13 +1,13 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
import os
import sys
from setuptools import setup, find_packages
PACKAGE_NAME = "mozlog"
PACKAGE_VERSION = "1.0"
PACKAGE_VERSION = "1.1"
desc = """Robust log handling specialized for logging in the Mozilla universe"""
# take description from README

View File

@ -1,7 +1,3 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
[mozprocess](https://github.com/mozilla/mozbase/tree/master/mozprocess)
provides python process management via an operating system
and platform transparent interface to Mozilla platforms of interest.

View File

@ -1,5 +1,5 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
from processhandler import *

View File

@ -1,8 +1,8 @@
#!/usr/bin/env python
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
import os
import mozinfo

View File

@ -1,6 +1,6 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
import logging
import mozinfo
@ -111,14 +111,15 @@ class ProcessHandlerMixin(object):
except BaseException, e:
if getattr(e, "errno", None) != 3:
# Error 3 is "no such process", which is ok
print >> sys.stderr, "Could not kill process, could not find pid: %s" % self.pid
print >> sys.stdout, "Could not kill process, could not find pid: %s, assuming it's already dead" % self.pid
else:
os.kill(self.pid, signal.SIGKILL)
if self.returncode is None:
self.returncode = subprocess.Popen._internal_poll(self)
self._cleanup()
return self.returncode
return self.returncode
def wait(self):
""" Popen.wait
@ -365,7 +366,8 @@ falling back to not using job objects for managing child processes"""
except:
err = "IO Completion Port failed to signal process shutdown"
# Either way, let's try to get this code
self.returncode = winprocess.GetExitCodeProcess(self._handle)
if self._handle:
self.returncode = winprocess.GetExitCodeProcess(self._handle)
self._cleanup()
if err is not None:
@ -482,7 +484,7 @@ falling back to not using job objects for managing child processes"""
cmd,
args=None,
cwd=None,
env=os.environ.copy(),
env=None,
ignore_children = False,
processOutputLine=(),
onTimeout=(),
@ -507,11 +509,14 @@ falling back to not using job objects for managing child processes"""
self.cmd = cmd
self.args = args
self.cwd = cwd
self.env = env
self.didTimeout = False
self._ignore_children = ignore_children
self.keywordargs = kwargs
if env is None:
env = os.environ.copy()
self.env = env
# handlers
self.processOutputLineHandlers = list(processOutputLine)
self.onTimeoutHandlers = list(onTimeout)
@ -623,7 +628,6 @@ falling back to not using job objects for managing child processes"""
lineReadTimeout = timeout - (datetime.now() - self.startTime).seconds
(line, self.didTimeout) = self.readWithTimeout(logsource, lineReadTimeout)
if self.didTimeout:
self.proc.kill()
self.onTimeout()
@ -668,7 +672,7 @@ falling back to not using job objects for managing child processes"""
try:
(r, w, e) = select.select([f], [], [], timeout)
except:
# TODO: return a blank line?
# return a blank line
return ('', True)
if len(r) == 0:
@ -708,6 +712,7 @@ class LogOutput(object):
if self.file is not None:
self.file.close()
### front end class with the default handlers
class ProcessHandler(ProcessHandlerMixin):
@ -721,7 +726,11 @@ class ProcessHandler(ProcessHandlerMixin):
appended to the given file.
"""
kwargs.setdefault('processOutputLine', []).append(print_output)
kwargs.setdefault('processOutputLine', [])
# Print to standard output only if no outputline provided
if not kwargs['processOutputLine']:
kwargs['processOutputLine'].append(print_output)
if logfile:
logoutput = LogOutput(logfile)

View File

@ -1,6 +1,6 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
from ctypes import c_void_p, POINTER, sizeof, Structure, windll, WinError, WINFUNCTYPE, addressof, c_size_t, c_ulong
from ctypes.wintypes import BOOL, BYTE, DWORD, HANDLE, LARGE_INTEGER

View File

@ -1,7 +1,6 @@
#!/usr/bin/env python
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
from ctypes import sizeof, windll, addressof, c_wchar, create_unicode_buffer
from ctypes.wintypes import DWORD, HANDLE

View File

@ -1,11 +1,11 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
import os
from setuptools import setup, find_packages
version = '0.1b2'
PACKAGE_VERSION = '0.3'
# take description from README
here = os.path.dirname(os.path.abspath(__file__))
@ -15,15 +15,22 @@ except (OSError, IOError):
description = ''
setup(name='mozprocess',
version=version,
version=PACKAGE_VERSION,
description="Mozilla-authored process handling",
long_description=description,
classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
keywords='',
author='Mozilla Automation and Testing Team',
classifiers=['Environment :: Console',
'Intended Audience :: Developers',
'License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)',
'Natural Language :: English',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Topic :: Software Development :: Libraries :: Python Modules',
],
keywords='mozilla',
author='Mozilla Automation and Tools team',
author_email='tools@lists.mozilla.com',
url='https://github.com/mozilla/mozbase/tree/master/mozprocess',
license='MPL',
license='MPL 2.0',
packages=find_packages(exclude=['ez_setup', 'examples', 'tests']),
include_package_data=True,
zip_safe=False,

View File

@ -1,8 +1,6 @@
#!/usr/bin/env python
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
import os
import subprocess

View File

@ -1,8 +1,6 @@
#!/usr/bin/env python
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
import os
import subprocess

View File

@ -1,6 +1,6 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <stdio.h>
#include <stdlib.h>

View File

@ -1,7 +1,3 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
[Mozprofile](https://github.com/mozilla/mozbase/tree/master/mozprofile)
is a python tool for creating and managing profiles for Mozilla's
applications (Firefox, Thunderbird, etc.). In addition to creating profiles,

View File

@ -1,6 +1,7 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
from profile import *
from addons import *
from cli import *

View File

@ -1,10 +1,9 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
import os
import shutil
import sys
import tempfile
import urllib2
import zipfile
@ -25,11 +24,14 @@ class AddonManager(object):
profile - the path to the profile for which we install addons
"""
self.profile = profile
self.installed_addons = []
# keeps track of addons and manifests that were passed to install_addons
self.addons = []
self.manifests = []
# information needed for profile reset:
# https://github.com/mozilla/mozbase/blob/270a857328b130860d1b1b512e23899557a3c8f7/mozprofile/mozprofile/profile.py#L93
self.installed_addons = []
self.installed_manifests = []
# addons that we've installed; needed for cleanup
self._addon_dirs = []
def install_addons(self, addons=None, manifests=None):
"""
@ -41,6 +43,7 @@ class AddonManager(object):
if addons:
if isinstance(addons, basestring):
addons = [addons]
self.installed_addons.extend(addons)
for addon in addons:
self.install_from_path(addon)
# install addon manifests
@ -49,14 +52,13 @@ class AddonManager(object):
manifests = [manifests]
for manifest in manifests:
self.install_from_manifest(manifest)
self.installed_manifests.extended(manifests)
def install_from_manifest(self, filepath):
"""
Installs addons from a manifest
filepath - path to the manifest of addons to install
"""
self.manifests.append(filepath)
manifest = ManifestParser()
manifest.read(filepath)
addons = manifest.get()
@ -155,7 +157,6 @@ class AddonManager(object):
- path: url, path to .xpi, or directory of addons
- unpack: whether to unpack unless specified otherwise in the install.rdf
"""
self.addons.append(path)
# if the addon is a url, download it
# note that this won't work with protocols urllib2 doesn't support
@ -208,7 +209,7 @@ class AddonManager(object):
shutil.copy(xpifile, addon_path + '.xpi')
else:
dir_util.copy_tree(addon, addon_path, preserve_symlinks=1)
self.installed_addons.append(addon_path)
self._addon_dirs.append(addon_path)
# remove the temporary directory, if any
if tmpdir:
@ -220,6 +221,6 @@ class AddonManager(object):
def clean_addons(self):
"""Cleans up addons in the profile."""
for addon in self.installed_addons:
for addon in self._addon_dirs:
if os.path.isdir(addon):
dir_util.remove_tree(addon)

View File

@ -1,6 +1,8 @@
#!/usr/bin/env python
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
"""
Creates and/or modifies a Firefox profile.

View File

@ -1,6 +1,6 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
"""
@ -349,8 +349,12 @@ function FindProxyForURL(url, host)
def clean_db(self):
"""Removed permissions added by mozprofile."""
sqlite_file = os.path.join(self._profileDir, "permissions.sqlite")
if not os.path.exists(sqlite_file):
return
# Open database and create table
permDB = sqlite3.connect(os.path.join(self._profileDir, "permissions.sqlite"))
permDB = sqlite3.connect(sqlite_file)
cursor = permDB.cursor();
# TODO: only delete values that we add, this would require sending in the full permissions object

View File

@ -1,7 +1,6 @@
#!/usr/bin/env python
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
"""
user preferences

View File

@ -1,6 +1,6 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
__all__ = ['Profile', 'FirefoxProfile', 'ThunderbirdProfile']
@ -91,8 +91,8 @@ class Profile(object):
else:
profile = self.profile
self.__init__(profile=profile,
addons=self.addon_manager.addons,
addon_manifests=self.addon_manager.manifests,
addons=self.addon_manager.installed_addons,
addon_manifests=self.addon_manager.installed_manifests,
preferences=self._preferences,
locations=self._locations,
proxy = self._proxy)
@ -170,6 +170,9 @@ class Profile(object):
def clean_preferences(self):
"""Removed preferences added by mozrunner."""
for filename in self.written_prefs:
if not os.path.exists(os.path.join(self.profile, filename)):
# file has been deleted
break
while True:
if not self.pop_preferences(filename):
break
@ -181,6 +184,7 @@ class Profile(object):
process has not yet relinquished handles on files, so we do a wait/try
construct and timeout if we can't get a clear road to deletion
"""
try:
from exceptions import WindowsError
from time import sleep
@ -202,7 +206,6 @@ class Profile(object):
# We can't re-raise an error, so we'll hope the stuff above us will throw
pass
def cleanup(self):
"""Cleanup operations for the profile."""
if self.restore:

View File

@ -1,12 +1,12 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
import os
import sys
from setuptools import setup, find_packages
version = '0.1'
version = '0.4'
# we only support python 2 right now
assert sys.version_info[0] == 2
@ -32,14 +32,21 @@ except (OSError, IOError):
setup(name='mozprofile',
version=version,
description="handling of Mozilla XUL app profiles",
description="Handling of Mozilla Gecko based application profiles",
long_description=description,
classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
keywords='',
author='Mozilla Automation + Testing Team',
author_email='mozmill-dev@googlegroups.com',
url='http://github.com/mozautomation/mozmill',
license='MPL',
classifiers=['Environment :: Console',
'Intended Audience :: Developers',
'License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)',
'Natural Language :: English',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Topic :: Software Development :: Libraries :: Python Modules',
],
keywords='mozilla',
author='Mozilla Automation and Tools team',
author_email='tools@lists.mozilla.com',
url='https://github.com/mozilla/mozbase/tree/master/mozprofile',
license='MPL 2.0',
packages=find_packages(exclude=['ez_setup', 'examples', 'tests']),
include_package_data=True,
zip_safe=False,
@ -49,4 +56,4 @@ setup(name='mozprofile',
[console_scripts]
mozprofile = mozprofile:cli
""",
)
)

View File

@ -0,0 +1,46 @@
#!/usr/bin/env python
import mozprofile
import os
import shutil
import tempfile
import unittest
here = os.path.dirname(os.path.abspath(__file__))
class Bug758250(unittest.TestCase):
"""
use of --profile in mozrunner just blows away addon sources:
https://bugzilla.mozilla.org/show_bug.cgi?id=758250
"""
def test_profile_addon_cleanup(self):
# sanity check: the empty addon should be here
empty = os.path.join(here, 'empty')
self.assertTrue(os.path.exists(empty))
self.assertTrue(os.path.isdir(empty))
self.assertTrue(os.path.exists(os.path.join(empty, 'install.rdf')))
# because we are testing data loss, let's make sure we make a copy
tmpdir = tempfile.mktemp()
shutil.copytree(empty, tmpdir)
self.assertTrue(os.path.exists(os.path.join(tmpdir, 'install.rdf')))
# make a starter profile
profile = mozprofile.FirefoxProfile()
path = profile.profile
# make a new profile based on the old
newprofile = mozprofile.FirefoxProfile(profile=path, addons=[tmpdir])
newprofile.cleanup()
# the source addon *should* still exist
self.assertTrue(os.path.exists(tmpdir))
self.assertTrue(os.path.exists(os.path.join(tmpdir, 'install.rdf')))
# remove vestiges
shutil.rmtree(tmpdir)
if __name__ == '__main__':
unittest.main()

View File

@ -0,0 +1,20 @@
<?xml version="1.0"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>test-empty@quality.mozilla.org</em:id>
<em:version>0.1</em:version>
<em:name>Test Extension (empty)</em:name>
<em:creator>Mozilla QA</em:creator>
<em:homepageURL>http://quality.mozilla.org</em:homepageURL>
<em:type>2</em:type>
<!-- Firefox -->
<em:targetApplication>
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
<em:minVersion>3.5.*</em:minVersion>
<em:maxVersion>*</em:maxVersion>
</Description>
</em:targetApplication>
</Description>
</RDF>

View File

@ -2,3 +2,4 @@
[server_locations.py]
[test_preferences.py]
[permissions.py]
[bug758250.py]

View File

@ -85,6 +85,31 @@ browser.startup.homepage = http://github.com/
# cleanup
os.remove(name)
def test_reset_should_remove_added_prefs(self):
"""Check that when we call reset the items we expect are updated"""
profile = Profile()
prefs_file = os.path.join(profile.profile, 'user.js')
# we shouldn't have any initial preferences
initial_prefs = Preferences.read_prefs(prefs_file)
self.assertFalse(initial_prefs)
initial_prefs = file(prefs_file).read().strip()
self.assertFalse(initial_prefs)
# add some preferences
prefs1 = [("mr.t.quotes", "i aint getting on no plane!")]
profile.set_preferences(prefs1)
self.assertEqual(prefs1, Preferences.read_prefs(prefs_file))
lines = file(prefs_file).read().strip().splitlines()
self.assertTrue('#MozRunner Prefs Start' in lines)
self.assertTrue('#MozRunner Prefs End' in lines)
profile.reset()
self.assertNotEqual(prefs1, \
Preferences.read_prefs(os.path.join(profile.profile, 'user.js')),\
"I pity the fool who left my pref")
def test_magic_markers(self):
"""ensure our magic markers are working"""

View File

@ -1,13 +1,9 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
[mozrunner](https://github.com/mozilla/mozbase/tree/master/mozrunner)
is a [python package](http://pypi.python.org/pypi/mozrunner)
which handles running of Mozilla applications.
mozrunner utilizes [mozprofile](/en/Mozprofile)
mozrunner utilizes [mozprofile](https://github.com/mozilla/mozbase/tree/master/mozprofile)
for managing application profiles
and [mozprocess](/en/Mozprocess) for robust process control.
and [mozprocess](https://github.com/mozilla/mozbase/tree/master/mozprocess) for robust process control.
mozrunner may be used from the command line or programmatically as an API.
@ -18,7 +14,7 @@ The `mozrunner` command will launch the application (specified by
`--app`) from a binary specified with `-b` or as located on the `PATH`.
mozrunner takes the command line options from
[mozprofile](/en/Mozprofile) for constructing the profile to be used by
[mozprofile](https://github.com/mozilla/mozbase/tree/master/mozprofile) for constructing the profile to be used by
the application.
Run `mozrunner --help` for detailed information on the command line

View File

@ -1,5 +1,5 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
from runner import *

View File

@ -1,6 +1,8 @@
#!/usr/bin/env python
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
__all__ = ['Runner', 'ThunderbirdRunner', 'FirefoxRunner', 'runners', 'CLI', 'cli', 'package_metadata']
@ -8,24 +10,64 @@ import mozinfo
import optparse
import os
import platform
import subprocess
import sys
import ConfigParser
from threading import Thread
from utils import get_metadata_from_egg
from utils import findInPath
from mozprofile import *
from mozprocess.processhandler import ProcessHandler
if mozinfo.isMac:
from plistlib import readPlist
package_metadata = get_metadata_from_egg('mozrunner')
# Map of debugging programs to information about them
# from http://mxr.mozilla.org/mozilla-central/source/build/automationutils.py#59
debuggers = {'gdb': {'interactive': True,
'args': ['-q', '--args'],},
'valgrind': {'interactive': False,
'args': ['--leak-check=full']}
}
def debugger_arguments(debugger, arguments=None, interactive=None):
"""
finds debugger arguments from debugger given and defaults
* debugger : debugger name or path to debugger
* arguments : arguments to the debugger, or None to use defaults
* interactive : whether the debugger should be run in interactive mode, or None to use default
"""
# find debugger executable if not a file
executable = debugger
if not os.path.exists(executable):
executable = findInPath(debugger)
if executable is None:
raise Exception("Path to '%s' not found" % debugger)
# if debugger not in dictionary of knowns return defaults
dirname, debugger = os.path.split(debugger)
if debugger not in debuggers:
return ([executable] + (arguments or []), bool(interactive))
# otherwise use the dictionary values for arguments unless specified
if arguments is None:
arguments = debuggers[debugger].get('args', [])
if interactive is None:
interactive = debuggers[debugger].get('interactive', False)
return ([executable] + arguments, interactive)
class Runner(object):
"""Handles all running operations. Finds bins, runs and kills the process."""
profile_class = Profile # profile class to use by default
@classmethod
def create(cls, binary, cmdargs=None, env=None, kp_kwargs=None, profile_args=None,
clean_profile=True, process_class=ProcessHandler):
def create(cls, binary=None, cmdargs=None, env=None, kp_kwargs=None, profile_args=None,
clean_profile=True, process_class=ProcessHandler):
profile = cls.profile_class(**(profile_args or {}))
return cls(profile, binary=binary, cmdargs=cmdargs, env=env, kp_kwargs=kp_kwargs,
clean_profile=clean_profile, process_class=process_class)
@ -44,6 +86,13 @@ class Runner(object):
if not os.path.exists(self.binary):
raise OSError("Binary path does not exist: %s" % self.binary)
# allow Mac binaries to be specified as an app bundle
plist = '%s/Contents/Info.plist' % self.binary
if mozinfo.isMac and os.path.exists(plist):
info = readPlist(plist)
self.binary = os.path.join(self.binary, "Contents/MacOS/",
info['CFBundleExecutable'])
self.cmdargs = cmdargs or []
_cmdargs = [i for i in self.cmdargs
if i != '-foreground']
@ -102,8 +151,11 @@ class Runner(object):
def is_running(self):
return self.process_handler is not None
def start(self):
"""Run self.command in the proper environment."""
def start(self, debug_args=None, interactive=False):
"""
Run self.command in the proper environment.
- debug_args: arguments for the debugger
"""
# ensure you are stopped
self.stop()
@ -111,17 +163,35 @@ class Runner(object):
# ensure the profile exists
if not self.profile.exists():
self.profile.reset()
assert self.profile.exists(), "%s : failure to reset profile" % self.__class__.__name__
cmd = self._wrap_command(self.command+self.cmdargs)
# this run uses the managed processhandler
self.process_handler = self.process_class(cmd, env=self.env, **self.kp_kwargs)
self.process_handler.run()
# attach a debugger, if specified
if debug_args:
cmd = list(debug_args) + cmd
#
if interactive:
self.process_handler = subprocess.Popen(cmd, env=self.env)
# TODO: other arguments
else:
# this run uses the managed processhandler
self.process_handler = self.process_class(cmd, env=self.env, **self.kp_kwargs)
self.process_handler.run()
# Spin a thread to handle reading the output
self.outThread = OutputThread(self.process_handler)
self.outThread.start()
def wait(self, timeout=None, outputTimeout=None):
"""Wait for the app to exit."""
if self.process_handler is None:
return
self.process_handler.waitForFinish(timeout=timeout, outputTimeout=outputTimeout)
if isinstance(self.process_handler, subprocess.Popen):
self.process_handler.wait()
else:
self.process_handler.waitForFinish(timeout=timeout, outputTimeout=outputTimeout)
self.process_handler = None
def stop(self):
@ -152,7 +222,7 @@ class Runner(object):
if mozinfo.isMac and hasattr(platform, 'mac_ver') and \
platform.mac_ver()[0][:4] < '10.6':
return ["arch", "-arch", "i386"] + cmd
return cmd
return cmd
__del__ = cleanup
@ -170,21 +240,6 @@ class FirefoxRunner(Runner):
Runner.__init__(self, profile, binary, **kwargs)
# Find application version number
appdir = os.path.dirname(os.path.realpath(self.binary))
appini = ConfigParser.RawConfigParser()
appini.read(os.path.join(appdir, 'application.ini'))
# Version needs to be of the form 3.6 or 4.0b and not the whole string
version = appini.get('App', 'Version').rstrip('0123456789pre').rstrip('.')
# Disable compatibility check. See:
# - http://kb.mozillazine.org/Extensions.checkCompatibility
# - https://bugzilla.mozilla.org/show_bug.cgi?id=659048
preference = {'extensions.checkCompatibility.' + version: False,
'extensions.checkCompatibility.nightly': False}
self.profile.set_preferences(preference)
class ThunderbirdRunner(Runner):
"""Specialized Runner subclass for running Thunderbird"""
profile_class = ThunderbirdProfile
@ -192,6 +247,13 @@ class ThunderbirdRunner(Runner):
runners = {'firefox': FirefoxRunner,
'thunderbird': ThunderbirdRunner}
class OutputThread(Thread):
def __init__(self, prochandler):
Thread.__init__(self)
self.ph = prochandler
def run(self):
self.ph.waitForFinish()
class CLI(MozProfileCLI):
"""Command line interface."""
@ -239,6 +301,14 @@ class CLI(MozProfileCLI):
parser.add_option('--app-arg', dest='appArgs',
default=[], action='append',
help="provides an argument to the test application")
parser.add_option('--debugger', dest='debugger',
help="run under a debugger, e.g. gdb or valgrind")
parser.add_option('--debugger-args', dest='debugger_args',
action='append', default=None,
help="arguments to the debugger")
parser.add_option('--interactive', dest='interactive',
action='store_true',
help="run the program interactively")
if self.metadata:
parser.add_option("--info", dest="info", default=False,
action="store_true",
@ -284,10 +354,24 @@ class CLI(MozProfileCLI):
self.start(runner)
runner.cleanup()
def debugger_arguments(self):
"""
returns a 2-tuple of debugger arguments:
(debugger_arguments, interactive)
"""
debug_args = self.options.debugger_args
interactive = self.options.interactive
if self.options.debugger:
debug_args, interactive = debugger_arguments(self.options.debugger)
return debug_args, interactive
def start(self, runner):
"""Starts the runner and waits for Firefox to exit or Keyboard Interrupt.
Shoule be overwritten to provide custom running of the runner instance."""
runner.start()
# attach a debugger if specified
debug_args, interactive = self.debugger_arguments()
runner.start(debug_args=debug_args, interactive=interactive)
print 'Starting:', ' '.join(runner.command)
try:
runner.wait()

View File

@ -1,7 +1,6 @@
#!/usr/bin/env python
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
"""
utility functions for mozrunner

View File

@ -1,13 +1,13 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
import os
import sys
from setuptools import setup, find_packages
PACKAGE_NAME = "mozrunner"
PACKAGE_VERSION = "5.1"
PACKAGE_VERSION = '5.7'
desc = """Reliable start/stop/configuration of Mozilla Applications (Firefox, Thunderbird, etc.)"""
# take description from README
@ -17,9 +17,9 @@ try:
except (OSError, IOError):
description = ''
deps = ['mozinfo',
'mozprocess',
'mozprofile >= 0.1',
deps = ['mozinfo == 0.3.3',
'mozprocess == 0.3',
'mozprofile == 0.4',
]
# we only support python 2 right now
@ -29,23 +29,25 @@ setup(name=PACKAGE_NAME,
version=PACKAGE_VERSION,
description=desc,
long_description=description,
author='Mikeal Rogers, Mozilla',
author_email='mikeal.rogers@gmail.com',
url='http://github.com/mozautomation/mozmill',
license='MPL 1.1/GPL 2.0/LGPL 2.1',
classifiers=['Environment :: Console',
'Intended Audience :: Developers',
'License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)',
'Natural Language :: English',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Topic :: Software Development :: Libraries :: Python Modules',
],
keywords='mozilla',
author='Mozilla Automation and Tools team',
author_email='tools@lists.mozilla.com',
url='https://github.com/mozilla/mozbase/tree/master/mozrunner',
license='MPL 2.0',
packages=find_packages(exclude=['legacy']),
zip_safe=False,
entry_points="""
[console_scripts]
mozrunner = mozrunner:cli
""",
platforms =['Any'],
install_requires = deps,
classifiers=['Development Status :: 4 - Beta',
'Environment :: Console',
'Intended Audience :: Developers',
'License :: OSI Approved :: Mozilla Public License 1.1 (MPL 1.1)',
'Operating System :: OS Independent',
'Topic :: Software Development :: Libraries :: Python Modules',
]
)
entry_points="""
# -*- Entry points: -*-
[console_scripts]
mozrunner = mozrunner:cli
""",
)

View File

@ -1,7 +1,8 @@
#!/usr/bin/env python
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
"""
Setup mozbase packages for development.
@ -12,8 +13,6 @@ If no arguments are given, install all packages.
See https://wiki.mozilla.org/Auto-tools/Projects/MozBase
"""
# XXX note that currently directory names must equal package names
import pkg_resources
import os
import sys
@ -149,13 +148,20 @@ def main(args=sys.argv[1:]):
parser.exit()
# gather dependencies
# TODO: version conflict checking
deps = {}
alldeps = {}
mapping = {} # mapping from subdir name to package name
# core dependencies
for package in packages:
key, value = dependencies(os.path.join(here, package))
deps[key] = [sanitize_dependency(dep) for dep in value]
mapping[package] = key
# keep track of all dependencies for non-mozbase packages
for dep in value:
alldeps[sanitize_dependency(dep)] = ''.join(dep.split())
# indirect dependencies
flag = True
while flag:
@ -165,6 +171,9 @@ def main(args=sys.argv[1:]):
if dep in all_packages and dep not in deps:
key, value = dependencies(os.path.join(here, dep))
deps[key] = [sanitize_dependency(dep) for dep in value]
for dep in value:
alldeps[sanitize_dependency(dep)] = ''.join(dep.split())
mapping[package] = key
flag = True
break
@ -193,9 +202,20 @@ def main(args=sys.argv[1:]):
print package
parser.exit()
# install non-mozbase dependencies
# (currently none on modern python)
# these need to be installed separately and the --no-deps flag
# subsequently used due to a bug in setuptools; see
# https://bugzilla.mozilla.org/show_bug.cgi?id=759836
pypi_deps = dict([(i, j) for i,j in alldeps.items()
if i not in unrolled])
for package, version in pypi_deps.items():
# easy_install should be available since we rely on setuptools
call(['easy_install', version])
# set up the packages for development
for package in unrolled:
call([sys.executable, 'setup.py', 'develop'],
call([sys.executable, 'setup.py', 'develop', '--no-deps'],
cwd=os.path.join(here, reverse_mapping[package]))
if __name__ == '__main__':

View File

@ -1,7 +1,3 @@
; This Source Code Form is subject to the terms of the Mozilla Public
; License, v. 2.0. If a copy of the MPL was not distributed with this
; file, You can obtain one at http://mozilla.org/MPL/2.0/.
# mozbase test manifest, in the format of
# https://github.com/mozilla/mozbase/blob/master/manifestdestiny/README.txt
@ -11,3 +7,4 @@
[include:mozprocess/tests/manifest.ini]
[include:mozprofile/tests/manifest.ini]
[include:mozhttpd/tests/manifest.ini]
[include:mozdevice/tests/manifest.ini]

View File

@ -1,8 +1,4 @@
#!/usr/bin/env python
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
"""
run mozbase tests from a manifest,