Bug 706844 - Create a make target for peptest. r=jmaher

This commit is contained in:
Andrew Halberstadt 2011-12-06 09:26:24 -05:00
Родитель 4e68ed8180
Коммит c17f88643d
30 изменённых файлов: 2907 добавлений и 163 удалений

Просмотреть файл

@ -66,6 +66,12 @@ MOZBASE_EXTRAS = \
README \
$(NULL)
_DEST_DIR = $(DEPTH)/_tests/mozbase
libs:: $(MOZBASE_PACKAGES)
$(PYTHON) $(topsrcdir)/config/nsinstall.py $^ $(_DEST_DIR)
libs:: $(MOZBASE_EXTRAS)
$(PYTHON) $(topsrcdir)/config/nsinstall.py $^ $(_DEST_DIR)
stage-package: PKG_STAGE = $(DIST)/test-package-stage
stage-package:
$(NSINSTALL) -D $(PKG_STAGE)/mozbase

Просмотреть файл

@ -1,33 +1,27 @@
ManifestDestiny
===============
Universal manifests for Mozilla test harnesses
# What is ManifestDestiny?
What is ManifestDestiny?
------------------------
What ManifestDestiny gives you::
What ManifestDestiny gives you:
* manifests are (ordered) lists of tests
* tests may have an arbitrary number of key, value pairs
* the parser returns an ordered list of test data structures, which
are just dicts with some keys. For example, a test with no
user-specified metadata looks like this::
user-specified metadata looks like this:
[{'path':
'/home/jhammel/mozmill/src/ManifestDestiny/manifestdestiny/tests/testToolbar/testBackForwardButtons.js',
'name': 'testToolbar/testBackForwardButtons.js', 'here':
'/home/jhammel/mozmill/src/ManifestDestiny/manifestdestiny/tests',
'manifest': '/home/jhammel/mozmill/src/ManifestDestiny/manifestdestiny/tests',}]
[{'path':
'/home/jhammel/mozmill/src/ManifestDestiny/manifestdestiny/tests/testToolbar/testBackForwardButtons.js',
'name': 'testToolbar/testBackForwardButtons.js', 'here':
'/home/jhammel/mozmill/src/ManifestDestiny/manifestdestiny/tests',
'manifest': '/home/jhammel/mozmill/src/ManifestDestiny/manifestdestiny/tests',}]
The keys displayed here (path, name, here, and manifest) are reserved
keys for ManifestDestiny and any consuming APIs. You can add
additional key, value metadata to each test.
Why have test manifests?
------------------------
# Why have test manifests?
Most Mozilla test harnesses work by crawling a directory structure.
While this is straight-forward, manifests offer several practical
@ -41,7 +35,7 @@ advantages::
one around that knows the test?), then backing out a test is at best
problematic. With a manifest, a test may be disabled without
removing it from the tree and a bug filed with the appropriate
reason::
reason:
[test_broken.js]
disabled = https://bugzilla.mozilla.org/show_bug.cgi?id=123456
@ -66,11 +60,10 @@ advantages::
(sub)manifests as appropriate to your needs.
Manifest Format
---------------
# Manifest Format
Manifests are .ini file with the section names denoting the path
relative to the manifest::
relative to the manifest:
[foo.js]
[bar.js]
@ -78,8 +71,8 @@ relative to the manifest::
The sections are read in order. In addition, tests may include
arbitrary key, value metadata to be used by the harness. You may also
have a ``[DEFAULT]`` section that will give key, value pairs that will
be inherited by each test unless overridden::
have a `[DEFAULT]` section that will give key, value pairs that will
be inherited by each test unless overridden:
[DEFAULT]
type = restart
@ -95,19 +88,18 @@ be inherited by each test unless overridden::
[roses.js]
color = red
You can also include other manifests::
You can also include other manifests:
[include:subdir/anothermanifest.ini]
Manifests are included relative to the directory of the manifest with
the ``[include:]`` directive unless they are absolute paths.
the `[include:]` directive unless they are absolute paths.
Data
----
# Data
Manifest Destiny gives tests as a list of dictionaries (in python
terms).
terms).
* path: full path to the test
* name: short name of the test; this is the (usually) relative path
@ -115,7 +107,7 @@ terms).
* here: the parent directory of the manifest
* manifest: the path to the manifest containing the test
This data corresponds to a one-line manifest::
This data corresponds to a one-line manifest:
[testToolbar/testBackForwardButtons.js]
@ -125,7 +117,7 @@ as well.
Outside of the reserved keys, the remaining key, values
are up to convention to use. There is a (currently very minimal)
generic integration layer in ManifestDestiny for use of all harnesses,
``manifestparser.TestManifest``.
`manifestparser.TestManifest`.
For instance, if the 'disabled' key is present, you can get the set of
tests without disabled (various other queries are doable as well).
@ -134,7 +126,7 @@ they want with the data. They may ignore it completely, they may use
the provided integration layer, or they may provide their own
integration layer. This should allow whatever sort of logic is
desired. For instance, if in yourtestharness you wanted to run only on
mondays for a certain class of tests::
mondays for a certain class of tests:
tests = []
for test in manifests.tests:
@ -150,144 +142,133 @@ To recap:
* you can use it however you want or process it further as you need
Tests are denoted by sections in an .ini file (see
http://hg.mozilla.org/automation/ManifestDestiny/file/tip/manifestdestiny/tests/mozmill-example.ini).
http://hg.mozilla.org/automation/ManifestDestiny/file/tip/manifestdestiny/tests/mozmill-example.ini).
Additional manifest files may be included with an ``[include:]`` directive::
Additional manifest files may be included with an `[include:]` directive:
[include:path-to-additional-file.manifest]
The path to included files is relative to the current manifest.
The ``[DEFAULT]`` section contains variables that all tests inherit from.
The `[DEFAULT]` section contains variables that all tests inherit from.
Included files will inherit the top-level variables but may override
in their own ``[DEFAULT]`` section.
in their own `[DEFAULT]` section.
ManifestDestiny Architecture
----------------------------
# ManifestDestiny Architecture
There is a two- or three-layered approach to the ManifestDestiny
architecture, depending on your needs::
architecture, depending on your needs:
1. ManifestParser: this is a generic parser for .ini manifests that
facilitates the `[include:]` logic and the inheritence of
metadata. Despite the internal variable being called ``self.tests``
metadata. Despite the internal variable being called `self.tests`
(an oversight), this layer has nothing in particular to do with tests.
2. TestManifest: this is a harness-agnostic integration layer that is
test-specific. TestManifest faciliates ``skip-if`` and ``run-if``
logic.
test-specific. TestManifest faciliates `skip-if` and `run-if` logic.
3. Optionally, a harness will have an integration layer than inherits
from TestManifest if more harness-specific customization is desired at
the manifest level.
See the source code at http://hg.mozilla.org/automation/ManifestDestiny
See the source code at https://github.com/mozilla/mozbase/tree/master/manifestdestiny
and
http://hg.mozilla.org/automation/ManifestDestiny/file/tip/manifestparser.py
https://github.com/mozilla/mozbase/blob/master/manifestdestiny/manifestparser.py
in particular.
Using Manifests
---------------
# Using Manifests
A test harness will normally call ``TestManifest.active_tests`` (
http://hg.mozilla.org/automation/ManifestDestiny/file/c0399fbfa830/manifestparser.py#l506 )::
A test harness will normally call `TestManifest.active_tests`:
506 def active_tests(self, exists=True, disabled=True, **tags):
def active_tests(self, exists=True, disabled=True, **tags):
The manifests are passed to the ``__init__`` or ``read`` methods with
appropriate arguments. ``active_tests`` then allows you to select the
tests you want::
The manifests are passed to the `__init__` or `read` methods with
appropriate arguments. `active_tests` then allows you to select the
tests you want:
- exists : return only existing tests
- disabled : whether to return disabled tests; if not these will be
filtered out; if True (the default), the ``disabled`` key of a
filtered out; if True (the default), the `disabled` key of a
test's metadata will be present and will be set to the reason that a
test is disabled
- tags : keys and values to filter on (e.g. ``os='linux'``)
- tags : keys and values to filter on (e.g. `os='linux'`)
``active_tests`` looks for tests with ``skip-if.${TAG}`` or
``run-if``. If the condition is or is not fulfilled,
`active_tests` looks for tests with `skip-if`
`run-if`. If the condition is or is not fulfilled,
respectively, the test is marked as disabled. For instance, if you
pass ``**dict(os='linux')`` as ``**tags``, if a test contains a line
``skip-if = os == 'linux'`` this test will be disabled, or
``run-if = os = 'win'`` in which case the test will also be disabled. It
is up to the harness to pass in tags appropriate to its usage.
pass `**dict(os='linux')` as `**tags`, if a test contains a line
`skip-if = os == 'linux'` this test will be disabled, or
`run-if = os = 'win'` in which case the test will also be disabled. It
is up to the harness to pass in tags appropriate to its usage.
Creating Manifests
------------------
# Creating Manifests
ManifestDestiny comes with a console script, ``manifestparser create``, that
ManifestDestiny comes with a console script, `manifestparser create`, that
may be used to create a seed manifest structure from a directory of
files. Run ``manifestparser help create`` for usage information.
files. Run `manifestparser help create` for usage information.
Copying Manifests
-----------------
# Copying Manifests
To copy tests and manifests from a source::
To copy tests and manifests from a source:
manifestparser [options] copy from_manifest to_directory -tag1 -tag2 --key1=value1 key2=value2 ...
manifestparser [options] copy from_manifest to_directory -tag1 -tag2 --key1=value1 key2=value2 ...
Upating Tests
-------------
# Upating Tests
To update the tests associated with with a manifest from a source
directory::
directory:
manifestparser [options] update manifest from_directory -tag1 -tag2 --key1=value1 --key2=value2 ...
manifestparser [options] update manifest from_directory -tag1 -tag2 --key1=value1 --key2=value2 ...
Tests
-----
# Tests
ManifestDestiny includes a suite of tests:
http://hg.mozilla.org/automation/ManifestDestiny/file/tip/manifestdestiny/tests
https://github.com/mozilla/mozbase/tree/master/manifestdestiny/tests
``test_manifest.txt`` is a doctest that may be helpful in figuring out
how to use the API. Tests are run via ``python test.py``.
`test_manifest.txt` is a doctest that may be helpful in figuring out
how to use the API. Tests are run via `python test.py`.
Bugs
----
# Bugs
Please file any bugs or feature requests at
Please file any bugs or feature requests at
https://bugzilla.mozilla.org/enter_bug.cgi?product=Testing&component=ManifestParser
Or contact jhammel @mozilla.org or in #ateam on irc.mozilla.org
CLI
---
# CLI
Run ``manifestparser help`` for usage information.
Run `manifestparser help` for usage information.
To create a manifest from a set of directories::
To create a manifest from a set of directories:
manifestparser [options] create directory <directory> <...> [create-options]
manifestparser [options] create directory <directory> <...> [create-options]
To output a manifest of tests::
To output a manifest of tests:
manifestparser [options] write manifest <manifest> <...> -tag1 -tag2 --key1=value1 --key2=value2 ...
manifestparser [options] write manifest <manifest> <...> -tag1 -tag2 --key1=value1 --key2=value2 ...
To copy tests and manifests from a source::
To copy tests and manifests from a source:
manifestparser [options] copy from_manifest to_manifest -tag1 -tag2 --key1=value1 key2=value2 ...
manifestparser [options] copy from_manifest to_manifest -tag1 -tag2 --key1=value1 key2=value2 ...
To update the tests associated with with a manifest from a source
directory::
directory:
manifestparser [options] update manifest from_directory -tag1 -tag2 --key1=value1 --key2=value2 ...
manifestparser [options] update manifest from_directory -tag1 -tag2 --key1=value1 --key2=value2 ...
Design Considerations
---------------------
# Design Considerations
Contrary to some opinion, manifestparser.py and the associated .ini
format were not magically plucked from the sky but were descended upon
@ -296,7 +277,7 @@ through several design considerations.
* test manifests should be ordered. While python 2.6 and greater has
a ConfigParser that can use an ordered dictionary, it is a
requirement that we support python 2.4 for the build + testing
environment. To that end, a ``read_ini`` function was implemented
environment. To that end, a `read_ini` function was implemented
in manifestparser.py that should be the equivalent of the .ini
dialect used by ConfigParser.
@ -304,7 +285,7 @@ through several design considerations.
there was initially some thought of using JSON, there was pushback
that JSON was not easily editable. An ideal manifest format would
degenerate to a line-separated list of files. While .ini format
requires an additional ``[]`` per line, and while there have been
requires an additional `[]` per line, and while there have been
complaints about this, hopefully this is good enough.
* python does not have an in-built YAML parser. Since it was
@ -322,14 +303,13 @@ through several design considerations.
transported. Traditionally, test harnesses have lived in
mozilla-central. This is less true these days and it is increasingly
likely that more tests will not live in mozilla-central going
forward. So ``manifestparser.py`` should be highly consumable. To
forward. So `manifestparser.py` should be highly consumable. To
this end, it is a single file, as appropriate to mozilla-central,
which is also a working python package deployed to PyPI for easy
installation.
installation.
Historical Reference
--------------------
# Historical Reference
Date-ordered list of links about how manifests came to be where they are today::

Просмотреть файл

@ -0,0 +1,38 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is manifestdestiny.
#
# The Initial Developer of the Original Code is
# The Mozilla Foundation.
# Portions created by the Initial Developer are Copyright (C) 2010
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Jeff Hammel <jhammel@mozilla.com> (Original author)
#
# Alternatively, the contents of this file may be used under the terms of
# either of the GNU General Public License Version 2 or later (the "GPL"),
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
from manifestparser import *

Просмотреть файл

@ -368,7 +368,7 @@ def read_ini(fp, variables=None, default='DEFAULT',
if strict:
# make sure this section doesn't already exist
assert section not in section_names
assert section not in section_names, "Section '%s' already found in '%s'" % (section, section_names)
section_names.add(section)
current_section = {}
@ -1005,49 +1005,6 @@ class HelpCLI(CLICommand):
for command in sorted(commands):
print ' %s : %s' % (command, commands[command].__doc__.strip())
class SetupCLI(CLICommand):
"""
setup using setuptools
"""
# use setup.py from the repo when you want to distribute to python!
# otherwise setuptools will complain that it can't find setup.py
# and result in a useless package
usage = '%prog [options] setup [setuptools options]'
def __call__(self, options, args):
sys.argv = [sys.argv[0]] + args
assert setup is not None, "You must have setuptools installed to use SetupCLI"
here = os.path.dirname(os.path.abspath(__file__))
try:
filename = os.path.join(here, 'README.txt')
description = file(filename).read()
except:
description = ''
os.chdir(here)
setup(name='ManifestDestiny',
version=version,
description="Universal manifests for Mozilla test harnesses",
long_description=description,
classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
keywords='mozilla manifests',
author='Jeff Hammel',
author_email='jhammel@mozilla.com',
url='https://wiki.mozilla.org/Auto-tools/Projects/ManifestDestiny',
license='MPL',
zip_safe=False,
py_modules=['manifestparser'],
install_requires=[
# -*- Extra requirements: -*-
],
entry_points="""
[console_scripts]
manifestparser = manifestparser:main
""",
)
class UpdateCLI(CLICommand):
"""
update the tests as listed in a manifest from a directory
@ -1081,8 +1038,6 @@ commands = { 'create': CreateCLI,
'help': HelpCLI,
'update': UpdateCLI,
'write': WriteCLI }
if setup is not None:
commands['setup'] = SetupCLI
def main(args=sys.argv[1:]):
"""console_script entry point"""

Просмотреть файл

@ -41,6 +41,37 @@
# otherwise setuptools will complain that it can't find setup.py
# and result in a useless package
from setuptools import setup, find_packages
import sys
from manifestparser import SetupCLI
SetupCLI(None)(None, sys.argv[1:])
import os
here = os.path.dirname(os.path.abspath(__file__))
try:
filename = os.path.join(here, 'README.txt')
description = file(filename).read()
except:
description = ''
PACKAGE_NAME = "ManifestDestiny"
PACKAGE_VERSION = "0.5.4"
setup(name=PACKAGE_NAME,
version=PACKAGE_VERSION,
description="Universal manifests for Mozilla test harnesses",
long_description=description,
classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
keywords='mozilla manifests',
author='Jeff Hammel',
author_email='jhammel@mozilla.com',
url='https://github.com/mozilla/mozbase/tree/master/manifestdestiny',
license='MPL',
zip_safe=False,
packages=find_packages(exclude=['legacy']),
install_requires=[
# -*- Extra requirements: -*-
],
entry_points="""
[console_scripts]
manifestparser = manifestparser:main
""",
)

0
testing/mozbase/manifestdestiny/tests/test.py Normal file → Executable file
Просмотреть файл

Просмотреть файл

@ -0,0 +1,5 @@
[mozdevice](https://github.com/mozilla/mozbase/tree/master/mozdevice) provides
an interface to interact with a remote device such as an Android phone connected
to a workstation. Currently there are two implementations of the interface: one
uses a TCP-based protocol to communicate with a server running on the device,
another uses Android's adb utility.

Просмотреть файл

@ -0,0 +1,39 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is Mozbase.
#
# The Initial Developer of the Original Code is
# The Mozilla Foundation.
# Portions created by the Initial Developer are Copyright (C) 2011
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Will Lachance <wlachance@mozilla.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
from devicemanagerADB import DeviceManagerADB
from devicemanagerSUT import DeviceManagerSUT

Просмотреть файл

@ -0,0 +1,538 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is Test Automation Framework.
#
# The Initial Developer of the Original Code is Joel Maher.
#
# Portions created by the Initial Developer are Copyright (C) 2009
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Joel Maher <joel.maher@gmail.com> (Original Developer)
# Clint Talbert <cmtalbert@gmail.com>
# Mark Cote <mcote@mozilla.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
import time
import hashlib
import socket
import os
import re
class FileError(Exception):
" Signifies an error which occurs while doing a file operation."
def __init__(self, msg = ''):
self.msg = msg
def __str__(self):
return self.msg
class DMError(Exception):
"generic devicemanager exception."
def __init__(self, msg= ''):
self.msg = msg
def __str__(self):
return self.msg
class DeviceManager:
# external function
# returns:
# success: True
# failure: False
def pushFile(self, localname, destname):
assert 0 == 1
return False
# external function
# returns:
# success: directory name
# failure: None
def mkDir(self, name):
assert 0 == 1
return None
# make directory structure on the device
# external function
# returns:
# success: directory structure that we created
# failure: None
def mkDirs(self, filename):
assert 0 == 1
return None
# push localDir from host to remoteDir on the device
# external function
# returns:
# success: remoteDir
# failure: None
def pushDir(self, localDir, remoteDir):
assert 0 == 1
return None
# external function
# returns:
# success: True
# failure: False
def dirExists(self, dirname):
assert 0 == 1
return False
# Because we always have / style paths we make this a lot easier with some
# assumptions
# external function
# returns:
# success: True
# failure: False
def fileExists(self, filepath):
assert 0 == 1
return False
# list files on the device, requires cd to directory first
# external function
# returns:
# success: array of filenames, ['file1', 'file2', ...]
# failure: []
def listFiles(self, rootdir):
assert 0 == 1
return []
# external function
# returns:
# success: output of telnet, i.e. "removing file: /mnt/sdcard/tests/test.txt"
# failure: None
def removeFile(self, filename):
assert 0 == 1
return False
# does a recursive delete of directory on the device: rm -Rf remoteDir
# external function
# returns:
# success: output of telnet, i.e. "removing file: /mnt/sdcard/tests/test.txt"
# failure: None
def removeDir(self, remoteDir):
assert 0 == 1
return None
# external function
# returns:
# success: array of process tuples
# failure: []
def getProcessList(self):
assert 0 == 1
return []
# external function
# returns:
# success: pid
# failure: None
def fireProcess(self, appname, failIfRunning=False):
assert 0 == 1
return None
# external function
# returns:
# success: output filename
# failure: None
def launchProcess(self, cmd, outputFile = "process.txt", cwd = '', env = '', failIfRunning=False):
assert 0 == 1
return None
# loops until 'process' has exited or 'timeout' seconds is reached
# loop sleeps for 'interval' seconds between iterations
# external function
# returns:
# success: [file contents, None]
# failure: [None, None]
def communicate(self, process, timeout = 600, interval = 5):
timed_out = True
if (timeout > 0):
total_time = 0
while total_time < timeout:
time.sleep(interval)
if self.processExist(process) == None:
timed_out = False
break
total_time += interval
if (timed_out == True):
return [None, None]
return [self.getFile(process, "temp.txt"), None]
# iterates process list and returns pid if exists, otherwise None
# external function
# returns:
# success: pid
# failure: None
def processExist(self, appname):
pid = None
#filter out extra spaces
parts = filter(lambda x: x != '', appname.split(' '))
appname = ' '.join(parts)
#filter out the quoted env string if it exists
#ex: '"name=value;name2=value2;etc=..." process args' -> 'process args'
parts = appname.split('"')
if (len(parts) > 2):
appname = ' '.join(parts[2:]).strip()
pieces = appname.split(' ')
parts = pieces[0].split('/')
app = parts[-1]
procre = re.compile('.*' + app + '.*')
procList = self.getProcessList()
if (procList == []):
return None
for proc in procList:
if (procre.match(proc[1])):
pid = proc[0]
break
return pid
# external function
# returns:
# success: output from testagent
# failure: None
def killProcess(self, appname):
assert 0 == 1
return None
# external function
# returns:
# success: filecontents
# failure: None
def catFile(self, remoteFile):
assert 0 == 1
return None
# external function
# returns:
# success: output of pullfile, string
# failure: None
def pullFile(self, remoteFile):
assert 0 == 1
return None
# copy file from device (remoteFile) to host (localFile)
# external function
# returns:
# success: output of pullfile, string
# failure: None
def getFile(self, remoteFile, localFile = ''):
assert 0 == 1
return None
# copy directory structure from device (remoteDir) to host (localDir)
# external function
# checkDir exists so that we don't create local directories if the
# remote directory doesn't exist but also so that we don't call isDir
# twice when recursing.
# returns:
# success: list of files, string
# failure: None
def getDirectory(self, remoteDir, localDir, checkDir=True):
assert 0 == 1
return None
# external function
# returns:
# success: True
# failure: False
# Throws a FileError exception when null (invalid dir/filename)
def isDir(self, remotePath):
assert 0 == 1
return False
# true/false check if the two files have the same md5 sum
# external function
# returns:
# success: True
# failure: False
def validateFile(self, remoteFile, localFile):
assert 0 == 1
return False
# return the md5 sum of a remote file
# internal function
# returns:
# success: MD5 hash for given filename
# failure: None
def getRemoteHash(self, filename):
assert 0 == 1
return None
# return the md5 sum of a file on the host
# internal function
# returns:
# success: MD5 hash for given filename
# failure: None
def getLocalHash(self, filename):
file = open(filename, 'rb')
if (file == None):
return None
try:
mdsum = hashlib.md5()
except:
return None
while 1:
data = file.read(1024)
if not data:
break
mdsum.update(data)
file.close()
hexval = mdsum.hexdigest()
if (self.debug >= 3): print "local hash returned: '" + hexval + "'"
return hexval
# Gets the device root for the testing area on the device
# For all devices we will use / type slashes and depend on the device-agent
# to sort those out. The agent will return us the device location where we
# should store things, we will then create our /tests structure relative to
# that returned path.
# Structure on the device is as follows:
# /tests
# /<fennec>|<firefox> --> approot
# /profile
# /xpcshell
# /reftest
# /mochitest
#
# external function
# returns:
# success: path for device root
# failure: None
def getDeviceRoot(self):
assert 0 == 1
return None
# Either we will have /tests/fennec or /tests/firefox but we will never have
# both. Return the one that exists
# TODO: ensure we can support org.mozilla.firefox
# external function
# returns:
# success: path for app root
# failure: None
def getAppRoot(self):
devroot = self.getDeviceRoot()
if (devroot == None):
return None
if (self.dirExists(devroot + '/fennec')):
return devroot + '/fennec'
elif (self.dirExists(devroot + '/firefox')):
return devroot + '/firefox'
elif (self.dirExsts('/data/data/org.mozilla.fennec')):
return 'org.mozilla.fennec'
elif (self.dirExists('/data/data/org.mozilla.firefox')):
return 'org.mozilla.firefox'
elif (self.dirExists('/data/data/org.mozilla.fennec_aurora')):
return 'org.mozilla.fennec_aurora'
elif (self.dirExists('/data/data/org.mozilla.firefox_beta')):
return 'org.mozilla.firefox_beta'
# Failure (either not installed or not a recognized platform)
return None
# Gets the directory location on the device for a specific test type
# Type is one of: xpcshell|reftest|mochitest
# external function
# returns:
# success: path for test root
# failure: None
def getTestRoot(self, type):
devroot = self.getDeviceRoot()
if (devroot == None):
return None
if (re.search('xpcshell', type, re.I)):
self.testRoot = devroot + '/xpcshell'
elif (re.search('?(i)reftest', type)):
self.testRoot = devroot + '/reftest'
elif (re.search('?(i)mochitest', type)):
self.testRoot = devroot + '/mochitest'
return self.testRoot
# Sends a specific process ID a signal code and action.
# For Example: SIGINT and SIGDFL to process x
def signal(self, processID, signalType, signalAction):
# currently not implemented in device agent - todo
pass
# Get a return code from process ending -- needs support on device-agent
def getReturnCode(self, processID):
# TODO: make this real
return 0
# external function
# returns:
# success: output of unzip command
# failure: None
def unpackFile(self, filename):
return None
# external function
# returns:
# success: status from test agent
# failure: None
def reboot(self, ipAddr=None, port=30000):
assert 0 == 1
return None
# validate localDir from host to remoteDir on the device
# external function
# returns:
# success: True
# failure: False
def validateDir(self, localDir, remoteDir):
if (self.debug >= 2): print "validating directory: " + localDir + " to " + remoteDir
for root, dirs, files in os.walk(localDir):
parts = root.split(localDir)
for file in files:
remoteRoot = remoteDir + '/' + parts[1]
remoteRoot = remoteRoot.replace('/', '/')
if (parts[1] == ""): remoteRoot = remoteDir
remoteName = remoteRoot + '/' + file
if (self.validateFile(remoteName, os.path.join(root, file)) <> True):
return False
return True
# Returns information about the device:
# Directive indicates the information you want to get, your choices are:
# os - name of the os
# id - unique id of the device
# uptime - uptime of the device
# systime - system time of the device
# screen - screen resolution
# memory - memory stats
# process - list of running processes (same as ps)
# disk - total, free, available bytes on disk
# power - power status (charge, battery temp)
# all - all of them - or call it with no parameters to get all the information
# returns:
# success: dict of info strings by directive name
# failure: {}
def getInfo(self, directive=None):
assert 0 == 1
return {}
# external function
# returns:
# success: output from agent for inst command
# failure: None
def installApp(self, appBundlePath, destPath=None):
assert 0 == 1
return None
# external function
# returns:
# success: True
# failure: None
def uninstallAppAndReboot(self, appName, installPath=None):
assert 0 == 1
return None
# external function
# returns:
# success: text status from command or callback server
# failure: None
def updateApp(self, appBundlePath, processName=None, destPath=None, ipAddr=None, port=30000):
assert 0 == 1
return None
# external function
# returns:
# success: time in ms
# failure: None
def getCurrentTime(self):
assert 0 == 1
return None
class NetworkTools:
def __init__(self):
pass
# Utilities to get the local ip address
def getInterfaceIp(self, ifname):
if os.name != "nt":
import fcntl
import struct
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
return socket.inet_ntoa(fcntl.ioctl(
s.fileno(),
0x8915, # SIOCGIFADDR
struct.pack('256s', ifname[:15])
)[20:24])
else:
return None
def getLanIp(self):
ip = socket.gethostbyname(socket.gethostname())
if ip.startswith("127.") and os.name != "nt":
interfaces = ["eth0","eth1","eth2","wlan0","wlan1","wifi0","ath0","ath1","ppp0"]
for ifname in interfaces:
try:
ip = self.getInterfaceIp(ifname)
break;
except IOError:
pass
return ip
# Gets an open port starting with the seed by incrementing by 1 each time
def findOpenPort(self, ip, seed):
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
connected = False
if isinstance(seed, basestring):
seed = int(seed)
maxportnum = seed + 5000 # We will try at most 5000 ports to find an open one
while not connected:
try:
s.bind((ip, seed))
connected = True
s.close()
break
except:
if seed > maxportnum:
print "Could not find open port after checking 5000 ports"
raise
seed += 1
except:
print "Socket error trying to find open port"
return seed

Просмотреть файл

@ -0,0 +1,580 @@
import subprocess
from devicemanager import DeviceManager, DMError
import re
import os
import sys
class DeviceManagerADB(DeviceManager):
def __init__(self, host = None, port = 20701, retrylimit = 5, packageName = None):
self.host = host
self.port = port
self.retrylimit = retrylimit
self.retries = 0
self._sock = None
self.useRunAs = False
self.packageName = None
if packageName == None:
if os.getenv('USER'):
packageName = 'org.mozilla.fennec_' + os.getenv('USER')
else:
packageName = 'org.mozilla.fennec_'
self.Init(packageName)
def Init(self, packageName):
# Initialization code that may fail: Catch exceptions here to allow
# successful initialization even if, for example, adb is not installed.
try:
self.verifyADB()
self.verifyRunAs(packageName)
except:
self.useRunAs = False
self.packageName = None
try:
# a test to see if we have root privs
files = self.listFiles("/data/data")
if (len(files) == 1):
if (files[0].find("Permission denied") != -1):
print "NOT running as root"
raise Exception("not running as root")
except:
try:
self.checkCmd(["root"])
except:
print "restarting as root failed"
# external function
# returns:
# success: True
# failure: False
def pushFile(self, localname, destname):
try:
if (os.name == "nt"):
destname = destname.replace('\\', '/')
if (self.useRunAs):
remoteTmpFile = self.tmpDir + "/" + os.path.basename(localname)
self.checkCmd(["push", os.path.realpath(localname), remoteTmpFile])
self.checkCmdAs(["shell", "cp", remoteTmpFile, destname])
self.checkCmd(["shell", "rm", remoteTmpFile])
else:
self.checkCmd(["push", os.path.realpath(localname), destname])
if (self.isDir(destname)):
destname = destname + "/" + os.path.basename(localname)
self.chmodDir(destname)
return True
except:
return False
# external function
# returns:
# success: directory name
# failure: None
def mkDir(self, name):
try:
self.checkCmdAs(["shell", "mkdir", name])
self.chmodDir(name)
return name
except:
return None
# make directory structure on the device
# external function
# returns:
# success: directory structure that we created
# failure: None
def mkDirs(self, filename):
parts = filename.split('/')
name = ""
for part in parts:
if (part == parts[-1]): break
if (part != ""):
name += '/' + part
if (not self.dirExists(name)):
if (self.mkDir(name) == None):
print "failed making directory: " + str(name)
return None
return name
# push localDir from host to remoteDir on the device
# external function
# returns:
# success: remoteDir
# failure: None
def pushDir(self, localDir, remoteDir):
# adb "push" accepts a directory as an argument, but if the directory
# contains symbolic links, the links are pushed, rather than the linked
# files; we push file-by-file to get around this limitation
try:
if (not self.dirExists(remoteDir)):
self.mkDirs(remoteDir+"/x")
for root, dirs, files in os.walk(localDir, followlinks='true'):
relRoot = os.path.relpath(root, localDir)
for file in files:
localFile = os.path.join(root, file)
remoteFile = remoteDir + "/"
if (relRoot!="."):
remoteFile = remoteFile + relRoot + "/"
remoteFile = remoteFile + file
self.pushFile(localFile, remoteFile)
for dir in dirs:
targetDir = remoteDir + "/"
if (relRoot!="."):
targetDir = targetDir + relRoot + "/"
targetDir = targetDir + dir
if (not self.dirExists(targetDir)):
self.mkDir(targetDir)
self.checkCmdAs(["shell", "chmod", "777", remoteDir])
return True
except:
print "pushing " + localDir + " to " + remoteDir + " failed"
return False
# external function
# returns:
# success: True
# failure: False
def dirExists(self, dirname):
return self.isDir(dirname)
# Because we always have / style paths we make this a lot easier with some
# assumptions
# external function
# returns:
# success: True
# failure: False
def fileExists(self, filepath):
p = self.runCmd(["shell", "ls", "-a", filepath])
data = p.stdout.readlines()
if (len(data) == 1):
if (data[0].rstrip() == filepath):
return True
return False
def removeFile(self, filename):
return self.runCmd(["shell", "rm", filename]).stdout.read()
# does a recursive delete of directory on the device: rm -Rf remoteDir
# external function
# returns:
# success: output of telnet, i.e. "removing file: /mnt/sdcard/tests/test.txt"
# failure: None
def removeSingleDir(self, remoteDir):
return self.runCmd(["shell", "rmdir", remoteDir]).stdout.read()
# does a recursive delete of directory on the device: rm -Rf remoteDir
# external function
# returns:
# success: output of telnet, i.e. "removing file: /mnt/sdcard/tests/test.txt"
# failure: None
def removeDir(self, remoteDir):
out = ""
if (self.isDir(remoteDir)):
files = self.listFiles(remoteDir.strip())
for f in files:
if (self.isDir(remoteDir.strip() + "/" + f.strip())):
out += self.removeDir(remoteDir.strip() + "/" + f.strip())
else:
out += self.removeFile(remoteDir.strip() + "/" + f.strip())
out += self.removeSingleDir(remoteDir.strip())
else:
out += self.removeFile(remoteDir.strip())
return out
def isDir(self, remotePath):
p = self.runCmd(["shell", "ls", "-a", remotePath])
data = p.stdout.readlines()
if (len(data) == 0):
return True
if (len(data) == 1):
if (data[0].rstrip() == remotePath):
return False
if (data[0].find("No such file or directory") != -1):
return False
if (data[0].find("Not a directory") != -1):
return False
return True
def listFiles(self, rootdir):
p = self.runCmd(["shell", "ls", "-a", rootdir])
data = p.stdout.readlines()
if (len(data) == 1):
if (data[0] == rootdir):
return []
if (data[0].find("No such file or directory") != -1):
return []
if (data[0].find("Not a directory") != -1):
return []
return data
# external function
# returns:
# success: array of process tuples
# failure: []
def getProcessList(self):
p = self.runCmd(["shell", "ps"])
# first line is the headers
p.stdout.readline()
proc = p.stdout.readline()
ret = []
while (proc):
els = proc.split()
ret.append(list([els[1], els[len(els) - 1], els[0]]))
proc = p.stdout.readline()
return ret
# external function
# returns:
# success: pid
# failure: None
def fireProcess(self, appname, failIfRunning=False):
#strip out env vars
parts = appname.split('"');
if (len(parts) > 2):
parts = parts[2:]
return self.launchProcess(parts, failIfRunning)
# external function
# returns:
# success: output filename
# failure: None
def launchProcess(self, cmd, outputFile = "process.txt", cwd = '', env = '', failIfRunning=False):
acmd = ["shell", "am","start"]
cmd = ' '.join(cmd).strip()
i = cmd.find(" ")
acmd.append("-n")
acmd.append(cmd[0:i] + "/.App")
acmd.append("--es")
acmd.append("args")
acmd.append(cmd[i:])
print acmd
self.checkCmd(acmd)
return outputFile;
# external function
# returns:
# success: output from testagent
# failure: None
def killProcess(self, appname):
procs = self.getProcessList()
for (pid, name, user) in procs:
if name == appname:
p = self.runCmdAs(["shell", "kill", pid])
return p.stdout.read()
return None
# external function
# returns:
# success: filecontents
# failure: None
def catFile(self, remoteFile):
#p = self.runCmd(["shell", "cat", remoteFile])
#return p.stdout.read()
return self.getFile(remoteFile)
# external function
# returns:
# success: output of pullfile, string
# failure: None
def pullFile(self, remoteFile):
#return self.catFile(remoteFile)
return self.getFile(remoteFile)
# copy file from device (remoteFile) to host (localFile)
# external function
# returns:
# success: output of pullfile, string
# failure: None
def getFile(self, remoteFile, localFile = 'tmpfile_dm_adb'):
# TODO: add debug flags and allow for printing stdout
# self.runCmd(["pull", remoteFile, localFile])
try:
self.runCmd(["pull", remoteFile, localFile]).stdout.read()
f = open(localFile)
ret = f.read()
f.close()
return ret;
except:
return None
# copy directory structure from device (remoteDir) to host (localDir)
# external function
# checkDir exists so that we don't create local directories if the
# remote directory doesn't exist but also so that we don't call isDir
# twice when recursing.
# returns:
# success: list of files, string
# failure: None
def getDirectory(self, remoteDir, localDir, checkDir=True):
ret = []
p = self.runCmd(["pull", remoteDir, localDir])
p.stderr.readline()
line = p.stderr.readline()
while (line):
els = line.split()
f = els[len(els) - 1]
i = f.find(localDir)
if (i != -1):
if (localDir[len(localDir) - 1] != '/'):
i = i + 1
f = f[i + len(localDir):]
i = f.find("/")
if (i > 0):
f = f[0:i]
ret.append(f)
line = p.stderr.readline()
#the last line is a summary
if (len(ret) > 0):
ret.pop()
return ret
# true/false check if the two files have the same md5 sum
# external function
# returns:
# success: True
# failure: False
def validateFile(self, remoteFile, localFile):
return self.getRemoteHash(remoteFile) == self.getLocalHash(localFile)
# return the md5 sum of a remote file
# internal function
# returns:
# success: MD5 hash for given filename
# failure: None
def getRemoteHash(self, filename):
data = p = self.runCmd(["shell", "ls", "-l", filename]).stdout.read()
return data.split()[3]
def getLocalHash(self, filename):
data = p = subprocess.Popen(["ls", "-l", filename], stdout=subprocess.PIPE).stdout.read()
return data.split()[4]
# Gets the device root for the testing area on the device
# For all devices we will use / type slashes and depend on the device-agent
# to sort those out. The agent will return us the device location where we
# should store things, we will then create our /tests structure relative to
# that returned path.
# Structure on the device is as follows:
# /tests
# /<fennec>|<firefox> --> approot
# /profile
# /xpcshell
# /reftest
# /mochitest
#
# external function
# returns:
# success: path for device root
# failure: None
def getDeviceRoot(self):
# /mnt/sdcard/tests is preferred to /data/local/tests, but this can be
# over-ridden by creating /data/local/tests
testRoot = "/data/local/tests"
if (self.dirExists(testRoot)):
return testRoot
root = "/mnt/sdcard"
if (not self.dirExists(root)):
root = "/data/local"
testRoot = root + "/tests"
if (not self.dirExists(testRoot)):
self.mkDir(testRoot)
return testRoot
# Either we will have /tests/fennec or /tests/firefox but we will never have
# both. Return the one that exists
# TODO: ensure we can support org.mozilla.firefox
# external function
# returns:
# success: path for app root
# failure: None
def getAppRoot(self):
devroot = self.getDeviceRoot()
if (devroot == None):
return None
if (self.dirExists(devroot + '/fennec')):
return devroot + '/fennec'
elif (self.dirExists(devroot + '/firefox')):
return devroot + '/firefox'
elif (self.packageName and self.dirExists('/data/data/' + self.packageName)):
return '/data/data/' + self.packageName
# Failure (either not installed or not a recognized platform)
print "devicemanagerADB: getAppRoot failed"
return None
# Gets the directory location on the device for a specific test type
# Type is one of: xpcshell|reftest|mochitest
# external function
# returns:
# success: path for test root
# failure: None
def getTestRoot(self, type):
devroot = self.getDeviceRoot()
if (devroot == None):
return None
if (re.search('xpcshell', type, re.I)):
self.testRoot = devroot + '/xpcshell'
elif (re.search('?(i)reftest', type)):
self.testRoot = devroot + '/reftest'
elif (re.search('?(i)mochitest', type)):
self.testRoot = devroot + '/mochitest'
return self.testRoot
# external function
# returns:
# success: status from test agent
# failure: None
def reboot(self, wait = False):
ret = self.runCmd(["reboot"]).stdout.read()
if (not wait):
return "Success"
countdown = 40
while (countdown > 0):
countdown
try:
self.checkCmd(["wait-for-device", "shell", "ls", "/sbin"])
return ret
except:
try:
self.checkCmd(["root"])
except:
time.sleep(1)
print "couldn't get root"
return "Success"
# external function
# returns:
# success: text status from command or callback server
# failure: None
def updateApp(self, appBundlePath, processName=None, destPath=None, ipAddr=None, port=30000):
return self.runCmd(["install", "-r", appBundlePath]).stdout.read()
# external function
# returns:
# success: time in ms
# failure: None
def getCurrentTime(self):
timestr = self.runCmd(["shell", "date", "+%s"]).stdout.read().strip()
if (not timestr or not timestr.isdigit()):
return None
return str(int(timestr)*1000)
# Returns information about the device:
# Directive indicates the information you want to get, your choices are:
# os - name of the os
# id - unique id of the device
# uptime - uptime of the device
# systime - system time of the device
# screen - screen resolution
# memory - memory stats
# process - list of running processes (same as ps)
# disk - total, free, available bytes on disk
# power - power status (charge, battery temp)
# all - all of them - or call it with no parameters to get all the information
# returns:
# success: dict of info strings by directive name
# failure: {}
def getInfo(self, directive="all"):
ret = {}
if (directive == "id" or directive == "all"):
ret["id"] = self.runCmd(["get-serialno"]).stdout.read()
if (directive == "os" or directive == "all"):
ret["os"] = self.runCmd(["shell", "getprop", "ro.build.display.id"]).stdout.read()
if (directive == "uptime" or directive == "all"):
utime = self.runCmd(["shell", "uptime"]).stdout.read()
if (not utime):
raise DMError("error getting uptime")
utime = utime[9:]
hours = utime[0:utime.find(":")]
utime = utime[utime[1:].find(":") + 2:]
minutes = utime[0:utime.find(":")]
utime = utime[utime[1:].find(":") + 2:]
seconds = utime[0:utime.find(",")]
ret["uptime"] = ["0 days " + hours + " hours " + minutes + " minutes " + seconds + " seconds"]
if (directive == "process" or directive == "all"):
ret["process"] = self.runCmd(["shell", "ps"]).stdout.read()
if (directive == "systime" or directive == "all"):
ret["systime"] = self.runCmd(["shell", "date"]).stdout.read()
print ret
return ret
def runCmd(self, args):
args.insert(0, "adb")
return subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
def runCmdAs(self, args):
if self.useRunAs:
args.insert(1, "run-as")
args.insert(2, self.packageName)
return self.runCmd(args)
def checkCmd(self, args):
args.insert(0, "adb")
return subprocess.check_call(args)
def checkCmdAs(self, args):
if (self.useRunAs):
args.insert(1, "run-as")
args.insert(2, self.packageName)
return self.checkCmd(args)
def chmodDir(self, remoteDir):
if (self.isDir(remoteDir)):
files = self.listFiles(remoteDir.strip())
for f in files:
if (self.isDir(remoteDir.strip() + "/" + f.strip())):
self.chmodDir(remoteDir.strip() + "/" + f.strip())
else:
self.checkCmdAs(["shell", "chmod", "777", remoteDir.strip()])
print "chmod " + remoteDir.strip()
self.checkCmdAs(["shell", "chmod", "777", remoteDir])
print "chmod " + remoteDir
else:
self.checkCmdAs(["shell", "chmod", "777", remoteDir.strip()])
print "chmod " + remoteDir.strip()
def verifyADB(self):
# Check to see if adb itself can be executed.
try:
self.runCmd(["version"])
except Exception as (ex):
print "unable to execute ADB: ensure Android SDK is installed and adb is in your $PATH"
def isCpAvailable(self):
# Some Android systems may not have a cp command installed,
# or it may not be executable by the user.
data = self.runCmd(["shell", "cp"]).stdout.read()
if (re.search('Usage', data)):
return True
else:
print "unable to execute 'cp' on device; consider installing busybox from Android Market"
return False
def verifyRunAs(self, packageName):
# If a valid package name is available, and certain other
# conditions are met, devicemanagerADB can execute file operations
# via the "run-as" command, so that pushed files and directories
# are created by the uid associated with the package, more closely
# echoing conditions encountered by Fennec at run time.
# Check to see if run-as can be used here, by verifying a
# file copy via run-as.
self.useRunAs = False
devroot = self.getDeviceRoot()
if (packageName and self.isCpAvailable() and devroot):
self.tmpDir = devroot + "/tmp"
if (not self.dirExists(self.tmpDir)):
self.mkDir(self.tmpDir)
self.checkCmd(["shell", "run-as", packageName, "mkdir", devroot + "/sanity"])
self.checkCmd(["push", os.path.abspath(sys.argv[0]), self.tmpDir + "/tmpfile"])
self.checkCmd(["shell", "run-as", packageName, "cp", self.tmpDir + "/tmpfile", devroot + "/sanity"])
if (self.fileExists(devroot + "/sanity/tmpfile")):
print "will execute commands via run-as " + packageName
self.packageName = packageName
self.useRunAs = True
self.checkCmd(["shell", "rm", devroot + "/tmp/tmpfile"])
self.checkCmd(["shell", "run-as", packageName, "rm", "-r", devroot + "/sanity"])

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,67 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is mozdevice.
#
# The Initial Developer of the Original Code is
# The Mozilla Foundation.
# Portions created by the Initial Developer are Copyright (C) 2011
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Will Lachance <wlachance@mozilla.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
import os
from setuptools import setup, find_packages
version = '0.1'
# take description from README
here = os.path.dirname(os.path.abspath(__file__))
try:
description = file(os.path.join(here, 'README.md')).read()
except (OSError, IOError):
description = ''
setup(name='mozdevice',
version=version,
description="Mozilla-authored device management",
long_description=description,
classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
keywords='',
author='Mozilla Automation and Testing Team',
author_email='tools@lists.mozilla.com',
url='http://github.com/mozilla/mozbase',
license='MPL',
packages=find_packages(exclude=['ez_setup', 'examples', 'tests']),
include_package_data=True,
zip_safe=False,
install_requires=[],
entry_points="""
# -*- Entry points: -*-
""",
)

Просмотреть файл

@ -0,0 +1,39 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is mozilla.org code.
#
# The Initial Developer of the Original Code is
# the Mozilla Foundation.
# Portions created by the Initial Developer are Copyright (C) 2011
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# William Lachance <wlachance@mozilla.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
from mozhttpd import MozHttpd, MozRequestHandler
import iface

Просмотреть файл

@ -0,0 +1,62 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is mozilla.org code.
#
# The Initial Developer of the Original Code is
# the Mozilla Foundation.
# Portions created by the Initial Developer are Copyright (C) 2011
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Joel Maher <joel.maher@gmail.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
import os
import socket
if os.name != 'nt':
import fcntl
import struct
def _get_interface_ip(ifname):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
return socket.inet_ntoa(fcntl.ioctl(
s.fileno(),
0x8915, # SIOCGIFADDR
struct.pack('256s', ifname[:15])
)[20:24])
def get_lan_ip():
ip = socket.gethostbyname(socket.gethostname())
if ip.startswith("127.") and os.name != "nt":
interfaces = ["eth0", "eth1", "eth2", "wlan0", "wlan1", "wifi0", "ath0", "ath1", "ppp0"]
for ifname in interfaces:
try:
ip = _get_interface_ip(ifname)
break;
except IOError:
pass
return ip

Просмотреть файл

@ -78,23 +78,24 @@ class MozRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
class MozHttpd(object):
def __init__(self, host="127.0.0.1", port=8888, docroot=os.getcwd()):
def __init__(self, host="127.0.0.1", port=8888, docroot=os.getcwd(), handler_class=MozRequestHandler):
self.host = host
self.port = int(port)
self.docroot = docroot
self.httpd = None
class MozRequestHandlerInstance(handler_class):
docroot = self.docroot
self.handler_class = MozRequestHandlerInstance
def start(self, block=False):
"""
start the server. If block is True, the call will not return.
If block is False, the server will be started on a separate thread that
can be terminated by a call to .stop()
"""
class MozRequestHandlerInstance(MozRequestHandler):
docroot = self.docroot
self.httpd = EasyServer((self.host, self.port), MozRequestHandlerInstance)
self.httpd = EasyServer((self.host, self.port), self.handler_class)
if block:
self.httpd.serve_forever()
else:

Просмотреть файл

@ -36,7 +36,7 @@
# ***** END LICENSE BLOCK *****
import os
from setuptools import setup
from setuptools import setup, find_packages
try:
here = os.path.dirname(os.path.abspath(__file__))
@ -59,7 +59,7 @@ setup(name='mozhttpd',
url='https://github.com/mozilla/mozbase/tree/master/mozhttpd',
license='MPL',
py_modules=['mozhttpd'],
packages=[],
packages=find_packages(exclude=['ez_setup', 'examples', 'tests']),
include_package_data=True,
zip_safe=False,
install_requires=deps,

Просмотреть файл

@ -0,0 +1,39 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is mozinfo.
#
# The Initial Developer of the Original Code is
# The Mozilla Foundation.
# Portions created by the Initial Developer are Copyright (C) 2010
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Jeff Hammel <jhammel@mozilla.com>
# Clint Talbert <ctalbert@mozilla.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
from mozinfo import *

Просмотреть файл

Просмотреть файл

@ -37,7 +37,7 @@
import os
from setuptools import setup
from setuptools import setup, find_packages
version = '0.3.3'
@ -65,8 +65,7 @@ setup(name='mozinfo',
author_email='jhammel@mozilla.com',
url='https://wiki.mozilla.org/Auto-tools',
license='MPL',
py_modules=['mozinfo'],
packages=[],
packages=find_packages(exclude=['legacy']),
include_package_data=True,
zip_safe=False,
install_requires=deps,

Просмотреть файл

@ -0,0 +1,38 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is mozinstall.
#
# The Initial Developer of the Original Code is
# The Mozilla Foundation.
# Portions created by the Initial Developer are Copyright (C) 2011
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Clint Talbert <ctalbert@mozilla.com>
# Andrew Halberstadt <halbersa@gmail.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
from mozinstall import *

Просмотреть файл

@ -37,7 +37,7 @@
# ***** END LICENSE BLOCK *****
import os
from setuptools import setup
from setuptools import setup, find_packages
try:
here = os.path.dirname(os.path.abspath(__file__))
@ -66,8 +66,7 @@ setup(name='mozInstall',
author_email='mdas@mozilla.com',
url='https://github.com/mozilla/mozbase',
license='MPL',
py_modules=['mozinstall'],
packages=[],
packages=find_packages(exclude=['legacy']),
include_package_data=True,
zip_safe=False,
install_requires=deps,

0
testing/mozbase/mozprocess/mozprocess/pid.py Normal file → Executable file
Просмотреть файл

Просмотреть файл

@ -42,12 +42,12 @@ import os
import sys
from setuptools import setup, find_packages
version = '0.1b2'
version = '0.1'
# we only support python 2 right now
assert sys.version_info[0] == 2
deps = ["ManifestDestiny == 0.5.4"]
deps = ["ManifestDestiny >= 0.5.4"]
# version-dependent dependencies
try:
import json

Просмотреть файл

@ -56,7 +56,10 @@ try:
import pkg_resources
def get_metadata_from_egg(module):
ret = {}
dist = pkg_resources.get_distribution(module)
try:
dist = pkg_resources.get_distribution(module)
except pkg_resources.DistributionNotFound:
return {}
if dist.has_metadata("PKG-INFO"):
key = None
for line in dist.get_metadata("PKG-INFO").splitlines():

Просмотреть файл

@ -43,7 +43,7 @@ import sys
from setuptools import setup, find_packages
PACKAGE_NAME = "mozrunner"
PACKAGE_VERSION = "4.0"
PACKAGE_VERSION = "4.1"
desc = """Reliable start/stop/configuration of Mozilla Applications (Firefox, Thunderbird, etc.)"""
# take description from README
@ -53,7 +53,10 @@ try:
except (OSError, IOError):
description = ''
deps = ['mozprocess', 'mozprofile', 'mozinfo']
deps = ['mozinfo',
'mozprocess',
'mozprofile >= 0.1',
]
# we only support python 2 right now
assert sys.version_info[0] == 2

0
testing/mozbase/setup_development.py Normal file → Executable file
Просмотреть файл

Просмотреть файл

@ -52,6 +52,7 @@ PEPTEST_HARNESS = \
PEPTEST_EXTRAS = \
setup.py \
runtests.py \
MANIFEST.in \
README.md \
$(NULL)
@ -60,6 +61,14 @@ PEPTEST_TESTS = \
tests \
$(NULL)
_DEST_DIR = $(DEPTH)/_tests/peptest
libs:: $(PEPTEST_HARNESS)
$(PYTHON) $(topsrcdir)/config/nsinstall.py $^ $(_DEST_DIR)
libs:: $(PEPTEST_EXTRAS)
$(PYTHON) $(topsrcdir)/config/nsinstall.py $^ $(_DEST_DIR)
libs:: $(PEPTEST_TESTS)
$(PYTHON) $(topsrcdir)/config/nsinstall.py $^ $(_DEST_DIR)
stage-package: PKG_STAGE = $(DIST)/test-package-stage
stage-package:
$(NSINSTALL) -D $(PKG_STAGE)/peptest

Просмотреть файл

@ -0,0 +1,61 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is peptest.
#
# The Initial Developer of the Original Code is
# The Mozilla Foundation.
# Portions created by the Initial Developer are Copyright (C) 2011
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Andrew Halberstadt <halbersa@gmail.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
"""
Adds peptest's dependencies to sys.path then runs the tests
"""
import os
import sys
deps = ['manifestdestiny',
'mozinfo',
'mozhttpd',
'mozlog',
'mozprofile',
'mozprocess',
'mozrunner',
]
here = os.path.dirname(__file__)
mozbase = os.path.realpath(os.path.join(here, '..', 'mozbase'))
for dep in deps:
module = os.path.join(mozbase, dep)
if module not in sys.path:
sys.path.insert(0, module)
from peptest import runpeptests
runpeptests.main(sys.argv[1:])

Просмотреть файл

@ -41,8 +41,10 @@
# replaces 'EXTRA_TEST_ARGS=--test-path=...'.
ifdef TEST_PATH
TEST_PATH_ARG := --test-path=$(TEST_PATH)
PEPTEST_PATH_ARG := --test-path=$(TEST_PATH)
else
TEST_PATH_ARG :=
PEPTEST_PATH_ARG := --test-path=_tests/peptest/tests/firefox/firefox_all.ini
endif
# include automation-build.mk to get the path to the binary
@ -228,6 +230,16 @@ xpcshell-tests-remote:
echo "please prepare your host with environment variables for TEST_DEVICE"; \
fi
# 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)
peptest:
$(RUN_PEPTEST)
$(CHECK_TEST_ERROR)
# Package up the tests and test harnesses
include $(topsrcdir)/toolkit/mozapps/installer/package-name.mk
@ -291,4 +303,5 @@ stage-mozbase: make-stage-dir
reftest crashtest \
xpcshell-tests \
jstestbrowser \
peptest \
package-tests make-stage-dir stage-mochitest stage-reftest stage-xpcshell stage-jstests stage-android stage-jetpack stage-firebug stage-peptest stage-mozbase