Merge "Remove unused folder development/testrunner" into main am: 3d6df7c624
Original change: https://android-review.googlesource.com/c/platform/development/+/2825850 Change-Id: I4c2ba5a680302984a1af713fa2199cb10a781864 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
2
testrunner/.gitignore
vendored
2
testrunner/.gitignore
vendored
@@ -1,2 +0,0 @@
|
||||
*.pyc
|
||||
.*
|
||||
@@ -1,29 +0,0 @@
|
||||
#
|
||||
# Install a list of test definitions on device
|
||||
#
|
||||
|
||||
# where to install the sample files on the device
|
||||
#
|
||||
local_target_dir := $(TARGET_OUT_DATA)/testinfo
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
########################
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := test_defs.xml
|
||||
LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
|
||||
LOCAL_LICENSE_CONDITIONS := notice
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
LOCAL_MODULE_CLASS := ETC
|
||||
LOCAL_MODULE_PATH := $(local_target_dir)
|
||||
LOCAL_SRC_FILES := $(LOCAL_MODULE)
|
||||
include $(BUILD_PREBUILT)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := coverage_targets.xml
|
||||
LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
|
||||
LOCAL_LICENSE_CONDITIONS := notice
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
LOCAL_MODULE_CLASS := ETC
|
||||
LOCAL_MODULE_PATH := $(local_target_dir)
|
||||
LOCAL_SRC_FILES := $(LOCAL_MODULE)
|
||||
include $(BUILD_PREBUILT)
|
||||
@@ -1 +0,0 @@
|
||||
__all__ = ['adb_interface', 'android_build', 'errors', 'logger', 'run_command']
|
||||
@@ -1,557 +0,0 @@
|
||||
#!/usr/bin/python2.4
|
||||
#
|
||||
#
|
||||
# Copyright 2008, The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Provides an interface to communicate with the device via the adb command.
|
||||
|
||||
Assumes adb binary is currently on system path.
|
||||
"""
|
||||
# Python imports
|
||||
import os
|
||||
import string
|
||||
import time
|
||||
|
||||
# local imports
|
||||
import am_instrument_parser
|
||||
import errors
|
||||
import logger
|
||||
import run_command
|
||||
|
||||
|
||||
class AdbInterface:
|
||||
"""Helper class for communicating with Android device via adb."""
|
||||
|
||||
# argument to pass to adb, to direct command to specific device
|
||||
_target_arg = ""
|
||||
|
||||
DEVICE_TRACE_DIR = "/data/test_results/"
|
||||
|
||||
def SetEmulatorTarget(self):
|
||||
"""Direct all future commands to the only running emulator."""
|
||||
self._target_arg = "-e"
|
||||
|
||||
def SetDeviceTarget(self):
|
||||
"""Direct all future commands to the only connected USB device."""
|
||||
self._target_arg = "-d"
|
||||
|
||||
def SetTargetSerial(self, serial):
|
||||
"""Direct all future commands to Android target with the given serial."""
|
||||
self._target_arg = "-s %s" % serial
|
||||
|
||||
def SendCommand(self, command_string, timeout_time=60, retry_count=3):
|
||||
"""Send a command via adb.
|
||||
|
||||
Args:
|
||||
command_string: adb command to run
|
||||
timeout_time: number of seconds to wait for command to respond before
|
||||
retrying
|
||||
retry_count: number of times to retry command before raising
|
||||
WaitForResponseTimedOutError
|
||||
Returns:
|
||||
string output of command
|
||||
|
||||
Raises:
|
||||
WaitForResponseTimedOutError if device does not respond to command within time
|
||||
"""
|
||||
adb_cmd = "adb %s %s" % (self._target_arg, command_string)
|
||||
logger.SilentLog("about to run %s" % adb_cmd)
|
||||
return run_command.RunCommand(adb_cmd, timeout_time=timeout_time,
|
||||
retry_count=retry_count)
|
||||
|
||||
def SendShellCommand(self, cmd, timeout_time=20, retry_count=3):
|
||||
"""Send a adb shell command.
|
||||
|
||||
Args:
|
||||
cmd: adb shell command to run
|
||||
timeout_time: number of seconds to wait for command to respond before
|
||||
retrying
|
||||
retry_count: number of times to retry command before raising
|
||||
WaitForResponseTimedOutError
|
||||
|
||||
Returns:
|
||||
string output of command
|
||||
|
||||
Raises:
|
||||
WaitForResponseTimedOutError: if device does not respond to command
|
||||
"""
|
||||
return self.SendCommand("shell %s" % cmd, timeout_time=timeout_time,
|
||||
retry_count=retry_count)
|
||||
|
||||
def BugReport(self, path):
|
||||
"""Dumps adb bugreport to the file specified by the path.
|
||||
|
||||
Args:
|
||||
path: Path of the file where adb bugreport is dumped to.
|
||||
"""
|
||||
bug_output = self.SendShellCommand("bugreport", timeout_time=60)
|
||||
bugreport_file = open(path, "w")
|
||||
bugreport_file.write(bug_output)
|
||||
bugreport_file.close()
|
||||
|
||||
def Push(self, src, dest):
|
||||
"""Pushes the file src onto the device at dest.
|
||||
|
||||
Args:
|
||||
src: file path of host file to push
|
||||
dest: destination absolute file path on device
|
||||
"""
|
||||
self.SendCommand("push %s %s" % (src, dest), timeout_time=60)
|
||||
|
||||
def Pull(self, src, dest):
|
||||
"""Pulls the file src on the device onto dest on the host.
|
||||
|
||||
Args:
|
||||
src: absolute file path of file on device to pull
|
||||
dest: destination file path on host
|
||||
|
||||
Returns:
|
||||
True if success and False otherwise.
|
||||
"""
|
||||
# Create the base dir if it doesn't exist already
|
||||
if not os.path.exists(os.path.dirname(dest)):
|
||||
os.makedirs(os.path.dirname(dest))
|
||||
|
||||
if self.DoesFileExist(src):
|
||||
self.SendCommand("pull %s %s" % (src, dest), timeout_time=60)
|
||||
return True
|
||||
else:
|
||||
logger.Log("ADB Pull Failed: Source file %s does not exist." % src)
|
||||
return False
|
||||
|
||||
def Install(self, apk_path, extra_flags):
|
||||
"""Installs apk on device.
|
||||
|
||||
Args:
|
||||
apk_path: file path to apk file on host
|
||||
extra_flags: Additional flags to use with adb install
|
||||
|
||||
Returns:
|
||||
output of install command
|
||||
"""
|
||||
return self.SendCommand("install -r %s %s" % (extra_flags, apk_path))
|
||||
|
||||
def DoesFileExist(self, src):
|
||||
"""Checks if the given path exists on device target.
|
||||
|
||||
Args:
|
||||
src: file path to be checked.
|
||||
|
||||
Returns:
|
||||
True if file exists
|
||||
"""
|
||||
|
||||
output = self.SendShellCommand("ls %s" % src)
|
||||
error = "No such file or directory"
|
||||
|
||||
if error in output:
|
||||
return False
|
||||
return True
|
||||
|
||||
def EnableAdbRoot(self):
|
||||
"""Enable adb root on device."""
|
||||
output = self.SendCommand("root")
|
||||
if "adbd is already running as root" in output:
|
||||
return True
|
||||
elif "restarting adbd as root" in output:
|
||||
# device will disappear from adb, wait for it to come back
|
||||
time.sleep(2)
|
||||
self.SendCommand("wait-for-device")
|
||||
return True
|
||||
else:
|
||||
logger.Log("Unrecognized output from adb root: %s" % output)
|
||||
return False
|
||||
|
||||
def StartInstrumentationForPackage(
|
||||
self, package_name, runner_name, timeout_time=60*10,
|
||||
no_window_animation=False, instrumentation_args={}, user=None,
|
||||
no_hidden_api_checks=False):
|
||||
"""Run instrumentation test for given package and runner.
|
||||
|
||||
Equivalent to StartInstrumentation, except instrumentation path is
|
||||
separated into its package and runner components.
|
||||
"""
|
||||
instrumentation_path = "%s/%s" % (package_name, runner_name)
|
||||
return self.StartInstrumentation(instrumentation_path, timeout_time=timeout_time,
|
||||
no_window_animation=no_window_animation,
|
||||
instrumentation_args=instrumentation_args,
|
||||
user=user,
|
||||
no_hidden_api_checks=no_hidden_api_checks)
|
||||
|
||||
def StartInstrumentation(
|
||||
self, instrumentation_path, timeout_time=60*10, no_window_animation=False,
|
||||
profile=False, instrumentation_args={}, user=None,
|
||||
no_hidden_api_checks=False):
|
||||
|
||||
"""Runs an instrumentation class on the target.
|
||||
|
||||
Returns a dictionary containing the key value pairs from the
|
||||
instrumentations result bundle and a list of TestResults. Also handles the
|
||||
interpreting of error output from the device and raises the necessary
|
||||
exceptions.
|
||||
|
||||
Args:
|
||||
instrumentation_path: string. It should be the fully classified package
|
||||
name, and instrumentation test runner, separated by "/"
|
||||
e.g. com.android.globaltimelaunch/.GlobalTimeLaunch
|
||||
timeout_time: Timeout value for the am command.
|
||||
no_window_animation: boolean, Whether you want window animations enabled
|
||||
or disabled
|
||||
profile: If True, profiling will be turned on for the instrumentation.
|
||||
instrumentation_args: Dictionary of key value bundle arguments to pass to
|
||||
instrumentation.
|
||||
user: The user id to start the instrumentation with.
|
||||
|
||||
Returns:
|
||||
(test_results, inst_finished_bundle)
|
||||
|
||||
test_results: a list of TestResults
|
||||
inst_finished_bundle (dict): Key/value pairs contained in the bundle that
|
||||
is passed into ActivityManager.finishInstrumentation(). Included in this
|
||||
bundle is the return code of the Instrumentation process, any error
|
||||
codes reported by the activity manager, and any results explicitly added
|
||||
by the instrumentation code.
|
||||
|
||||
Raises:
|
||||
WaitForResponseTimedOutError: if timeout occurred while waiting for
|
||||
response to adb instrument command
|
||||
DeviceUnresponsiveError: if device system process is not responding
|
||||
InstrumentationError: if instrumentation failed to run
|
||||
"""
|
||||
|
||||
command_string = self._BuildInstrumentationCommandPath(
|
||||
instrumentation_path, no_window_animation=no_window_animation,
|
||||
profile=profile, raw_mode=True,
|
||||
instrumentation_args=instrumentation_args,
|
||||
user=user, no_hidden_api_checks=no_hidden_api_checks)
|
||||
logger.Log(command_string)
|
||||
(test_results, inst_finished_bundle) = (
|
||||
am_instrument_parser.ParseAmInstrumentOutput(
|
||||
self.SendShellCommand(command_string, timeout_time=timeout_time,
|
||||
retry_count=2)))
|
||||
|
||||
if "code" not in inst_finished_bundle:
|
||||
raise errors.InstrumentationError("no test results... device setup "
|
||||
"correctly?")
|
||||
|
||||
if inst_finished_bundle["code"] == "0":
|
||||
short_msg_result = "no error message"
|
||||
if "shortMsg" in inst_finished_bundle:
|
||||
short_msg_result = inst_finished_bundle["shortMsg"]
|
||||
logger.Log("Error! Test run failed: %s" % short_msg_result)
|
||||
raise errors.InstrumentationError(short_msg_result)
|
||||
|
||||
if "INSTRUMENTATION_ABORTED" in inst_finished_bundle:
|
||||
logger.Log("INSTRUMENTATION ABORTED!")
|
||||
raise errors.DeviceUnresponsiveError
|
||||
|
||||
return (test_results, inst_finished_bundle)
|
||||
|
||||
def StartInstrumentationNoResults(
|
||||
self, package_name, runner_name, no_window_animation=False,
|
||||
raw_mode=False, instrumentation_args={}, user=None,
|
||||
no_hidden_api_checks=False):
|
||||
"""Runs instrumentation and dumps output to stdout.
|
||||
|
||||
Equivalent to StartInstrumentation, but will dump instrumentation
|
||||
'normal' output to stdout, instead of parsing return results. Command will
|
||||
never timeout.
|
||||
"""
|
||||
adb_command_string = self.PreviewInstrumentationCommand(
|
||||
package_name, runner_name, no_window_animation=no_window_animation,
|
||||
raw_mode=raw_mode, instrumentation_args=instrumentation_args,
|
||||
user=user, no_hidden_api_checks=no_hidden_api_checks)
|
||||
logger.Log(adb_command_string)
|
||||
run_command.RunCommand(adb_command_string, return_output=False)
|
||||
|
||||
def PreviewInstrumentationCommand(
|
||||
self, package_name, runner_name, no_window_animation=False,
|
||||
raw_mode=False, instrumentation_args={}, user=None,
|
||||
no_hidden_api_checks=False):
|
||||
"""Returns a string of adb command that will be executed."""
|
||||
inst_command_string = self._BuildInstrumentationCommand(
|
||||
package_name, runner_name, no_window_animation=no_window_animation,
|
||||
raw_mode=raw_mode, instrumentation_args=instrumentation_args,
|
||||
user=user, no_hidden_api_checks=no_hidden_api_checks)
|
||||
return self.PreviewShellCommand(inst_command_string)
|
||||
|
||||
def PreviewShellCommand(self, cmd):
|
||||
return "adb %s shell %s" % (self._target_arg, cmd)
|
||||
|
||||
def _BuildInstrumentationCommand(
|
||||
self, package, runner_name, no_window_animation=False, profile=False,
|
||||
raw_mode=True, instrumentation_args={}, user=None,
|
||||
no_hidden_api_checks=False):
|
||||
instrumentation_path = "%s/%s" % (package, runner_name)
|
||||
|
||||
return self._BuildInstrumentationCommandPath(
|
||||
instrumentation_path, no_window_animation=no_window_animation,
|
||||
profile=profile, raw_mode=raw_mode,
|
||||
instrumentation_args=instrumentation_args, user=user,
|
||||
no_hidden_api_checks=no_hidden_api_checks)
|
||||
|
||||
def _BuildInstrumentationCommandPath(
|
||||
self, instrumentation_path, no_window_animation=False, profile=False,
|
||||
raw_mode=True, instrumentation_args={}, user=None,
|
||||
no_hidden_api_checks=False):
|
||||
command_string = "am instrument"
|
||||
if no_hidden_api_checks:
|
||||
command_string += " --no-hidden-api-checks"
|
||||
if user:
|
||||
command_string += " --user %s" % user
|
||||
if no_window_animation:
|
||||
command_string += " --no_window_animation"
|
||||
if profile:
|
||||
self._CreateTraceDir()
|
||||
command_string += (
|
||||
" -p %s/%s.dmtrace" %
|
||||
(self.DEVICE_TRACE_DIR, instrumentation_path.split(".")[-1]))
|
||||
|
||||
for key, value in instrumentation_args.items():
|
||||
command_string += " -e %s '%s'" % (key, value)
|
||||
if raw_mode:
|
||||
command_string += " -r"
|
||||
command_string += " -w '%s'" % instrumentation_path
|
||||
return command_string
|
||||
|
||||
def _CreateTraceDir(self):
|
||||
ls_response = self.SendShellCommand("ls /data/trace")
|
||||
if ls_response.strip("#").strip(string.whitespace) != "":
|
||||
self.SendShellCommand("create /data/trace", "mkdir /data/trace")
|
||||
self.SendShellCommand("make /data/trace world writeable",
|
||||
"chmod 777 /data/trace")
|
||||
|
||||
def WaitForDevicePm(self, wait_time=120):
|
||||
"""Waits for targeted device's package manager to be up.
|
||||
|
||||
Args:
|
||||
wait_time: time in seconds to wait
|
||||
|
||||
Raises:
|
||||
WaitForResponseTimedOutError if wait_time elapses and pm still does not
|
||||
respond.
|
||||
"""
|
||||
logger.Log("Waiting for device package manager...")
|
||||
self.SendCommand("wait-for-device")
|
||||
# Now the device is there, but may not be running.
|
||||
# Query the package manager with a basic command
|
||||
try:
|
||||
self._WaitForShellCommandContents("pm path android", "package:",
|
||||
wait_time)
|
||||
except errors.WaitForResponseTimedOutError:
|
||||
raise errors.WaitForResponseTimedOutError(
|
||||
"Package manager did not respond after %s seconds" % wait_time)
|
||||
|
||||
def IsInstrumentationInstalled(self, package_name, runner_name):
|
||||
"""Checks if instrumentation is present on device."""
|
||||
instrumentation_path = "%s/%s" % (package_name, runner_name)
|
||||
command = "pm list instrumentation | grep %s" % instrumentation_path
|
||||
try:
|
||||
output = self.SendShellCommand(command)
|
||||
return output.startswith("instrumentation:")
|
||||
except errors.AbortError:
|
||||
# command can return error code on failure
|
||||
return False
|
||||
|
||||
def WaitForProcess(self, name, wait_time=120):
|
||||
"""Wait until a process is running on the device.
|
||||
|
||||
Args:
|
||||
name: the process name as it appears in `ps`
|
||||
wait_time: time in seconds to wait
|
||||
|
||||
Raises:
|
||||
WaitForResponseTimedOutError if wait_time elapses and the process is
|
||||
still not running
|
||||
"""
|
||||
logger.Log("Waiting for process %s" % name)
|
||||
self.SendCommand("wait-for-device")
|
||||
self._WaitForShellCommandContents("ps", name, wait_time)
|
||||
|
||||
def WaitForProcessEnd(self, name, wait_time=120):
|
||||
"""Wait until a process is no longer running on the device.
|
||||
|
||||
Args:
|
||||
name: the process name as it appears in `ps`
|
||||
wait_time: time in seconds to wait
|
||||
|
||||
Raises:
|
||||
WaitForResponseTimedOutError if wait_time elapses and the process is
|
||||
still running
|
||||
"""
|
||||
logger.Log("Waiting for process %s to end" % name)
|
||||
self._WaitForShellCommandContents("ps", name, wait_time, invert=True)
|
||||
|
||||
def _WaitForShellCommandContents(self, command, expected, wait_time,
|
||||
raise_abort=True, invert=False):
|
||||
"""Wait until the response to a command contains a given output.
|
||||
|
||||
Assumes that a only successful execution of "adb shell <command>" contains
|
||||
the substring expected. Assumes that a device is present.
|
||||
|
||||
Args:
|
||||
command: adb shell command to execute
|
||||
expected: the string that should appear to consider the
|
||||
command successful.
|
||||
wait_time: time in seconds to wait
|
||||
raise_abort: if False, retry when executing the command raises an
|
||||
AbortError, rather than failing.
|
||||
invert: if True, wait until the command output no longer contains the
|
||||
expected contents.
|
||||
|
||||
Raises:
|
||||
WaitForResponseTimedOutError: If wait_time elapses and the command has not
|
||||
returned an output containing expected yet.
|
||||
"""
|
||||
# Query the device with the command
|
||||
success = False
|
||||
attempts = 0
|
||||
wait_period = 5
|
||||
while not success and (attempts*wait_period) < wait_time:
|
||||
# assume the command will always contain expected in the success case
|
||||
try:
|
||||
output = self.SendShellCommand(command, retry_count=1)
|
||||
if ((not invert and expected in output)
|
||||
or (invert and expected not in output)):
|
||||
success = True
|
||||
except errors.AbortError, e:
|
||||
if raise_abort:
|
||||
raise
|
||||
# ignore otherwise
|
||||
|
||||
if not success:
|
||||
time.sleep(wait_period)
|
||||
attempts += 1
|
||||
|
||||
if not success:
|
||||
raise errors.WaitForResponseTimedOutError()
|
||||
|
||||
def WaitForBootComplete(self, wait_time=120):
|
||||
"""Waits for targeted device's bootcomplete flag to be set.
|
||||
|
||||
Args:
|
||||
wait_time: time in seconds to wait
|
||||
|
||||
Raises:
|
||||
WaitForResponseTimedOutError if wait_time elapses and pm still does not
|
||||
respond.
|
||||
"""
|
||||
logger.Log("Waiting for boot complete...")
|
||||
self.SendCommand("wait-for-device")
|
||||
# Now the device is there, but may not be running.
|
||||
# Query the package manager with a basic command
|
||||
boot_complete = False
|
||||
attempts = 0
|
||||
wait_period = 5
|
||||
while not boot_complete and (attempts*wait_period) < wait_time:
|
||||
output = self.SendShellCommand("getprop dev.bootcomplete", retry_count=1)
|
||||
output = output.strip()
|
||||
if output == "1":
|
||||
boot_complete = True
|
||||
else:
|
||||
time.sleep(wait_period)
|
||||
attempts += 1
|
||||
if not boot_complete:
|
||||
raise errors.WaitForResponseTimedOutError(
|
||||
"dev.bootcomplete flag was not set after %s seconds" % wait_time)
|
||||
|
||||
def Sync(self, retry_count=3, runtime_restart=False):
|
||||
"""Perform a adb sync.
|
||||
|
||||
Blocks until device package manager is responding.
|
||||
|
||||
Args:
|
||||
retry_count: number of times to retry sync before failing
|
||||
runtime_restart: stop runtime during sync and restart afterwards, useful
|
||||
for syncing system libraries (core, framework etc)
|
||||
|
||||
Raises:
|
||||
WaitForResponseTimedOutError if package manager does not respond
|
||||
AbortError if unrecoverable error occurred
|
||||
"""
|
||||
output = ""
|
||||
error = None
|
||||
if runtime_restart:
|
||||
self.SendShellCommand("setprop ro.test_harness 1", retry_count=retry_count)
|
||||
# manual rest bootcomplete flag
|
||||
self.SendShellCommand("setprop dev.bootcomplete 0",
|
||||
retry_count=retry_count)
|
||||
self.SendShellCommand("stop", retry_count=retry_count)
|
||||
|
||||
try:
|
||||
output = self.SendCommand("sync", retry_count=retry_count)
|
||||
except errors.AbortError, e:
|
||||
error = e
|
||||
output = e.msg
|
||||
if "Read-only file system" in output:
|
||||
logger.SilentLog(output)
|
||||
logger.Log("Remounting read-only filesystem")
|
||||
self.SendCommand("remount")
|
||||
output = self.SendCommand("sync", retry_count=retry_count)
|
||||
elif "No space left on device" in output:
|
||||
logger.SilentLog(output)
|
||||
logger.Log("Restarting device runtime")
|
||||
self.SendShellCommand("stop", retry_count=retry_count)
|
||||
output = self.SendCommand("sync", retry_count=retry_count)
|
||||
self.SendShellCommand("start", retry_count=retry_count)
|
||||
elif error is not None:
|
||||
# exception occurred that cannot be recovered from
|
||||
raise error
|
||||
logger.SilentLog(output)
|
||||
if runtime_restart:
|
||||
# start runtime and wait till boot complete flag is set
|
||||
self.SendShellCommand("start", retry_count=retry_count)
|
||||
self.WaitForBootComplete()
|
||||
# press the MENU key, this will disable key guard if runtime is started
|
||||
# with ro.monkey set to 1
|
||||
self.SendShellCommand("input keyevent 82", retry_count=retry_count)
|
||||
else:
|
||||
self.WaitForDevicePm()
|
||||
return output
|
||||
|
||||
def GetSerialNumber(self):
|
||||
"""Returns the serial number of the targeted device."""
|
||||
return self.SendCommand("get-serialno").strip()
|
||||
|
||||
def RuntimeReset(self, disable_keyguard=False, retry_count=3, preview_only=False):
|
||||
"""
|
||||
Resets the Android runtime (does *not* reboot the kernel).
|
||||
|
||||
Blocks until the reset is complete and the package manager
|
||||
is available.
|
||||
|
||||
Args:
|
||||
disable_keyguard: if True, presses the MENU key to disable
|
||||
key guard, after reset is finished
|
||||
retry_count: number of times to retry reset before failing
|
||||
|
||||
Raises:
|
||||
WaitForResponseTimedOutError if package manager does not respond
|
||||
AbortError if unrecoverable error occurred
|
||||
"""
|
||||
|
||||
logger.Log("adb shell stop")
|
||||
logger.Log("adb shell start")
|
||||
|
||||
if not preview_only:
|
||||
self.SendShellCommand("stop", retry_count=retry_count)
|
||||
self.SendShellCommand("start", retry_count=retry_count)
|
||||
|
||||
self.WaitForDevicePm()
|
||||
|
||||
if disable_keyguard:
|
||||
logger.Log("input keyevent 82 ## disable keyguard")
|
||||
if not preview_only:
|
||||
self.SendShellCommand("input keyevent 82", retry_count=retry_count)
|
||||
@@ -1,169 +0,0 @@
|
||||
#!/usr/bin/python2.4
|
||||
#
|
||||
#
|
||||
# Copyright 2008, The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Module that assists in parsing the output of "am instrument" commands run on
|
||||
the device."""
|
||||
|
||||
import re
|
||||
import string
|
||||
|
||||
|
||||
def ParseAmInstrumentOutput(result):
|
||||
"""Given the raw output of an "am instrument" command that targets and
|
||||
InstrumentationTestRunner, return structured data.
|
||||
|
||||
Args:
|
||||
result (string): Raw output of "am instrument"
|
||||
|
||||
Return
|
||||
(test_results, inst_finished_bundle)
|
||||
|
||||
test_results (list of am_output_parser.TestResult)
|
||||
inst_finished_bundle (dict): Key/value pairs contained in the bundle that is
|
||||
passed into ActivityManager.finishInstrumentation(). Included in this bundle is the return
|
||||
code of the Instrumentation process, any error codes reported by the
|
||||
activity manager, and any results explicity added by the instrumentation
|
||||
code.
|
||||
"""
|
||||
|
||||
re_status_code = re.compile(r'INSTRUMENTATION_STATUS_CODE: (?P<status_code>-?\d)$')
|
||||
test_results = []
|
||||
inst_finished_bundle = {}
|
||||
|
||||
result_block_string = ""
|
||||
for line in result.splitlines():
|
||||
result_block_string += line + '\n'
|
||||
|
||||
if "INSTRUMENTATION_STATUS_CODE:" in line:
|
||||
test_result = TestResult(result_block_string)
|
||||
if test_result.GetStatusCode() == 1: # The test started
|
||||
pass
|
||||
elif test_result.GetStatusCode() in [0, -1, -2]:
|
||||
test_results.append(test_result)
|
||||
else:
|
||||
pass
|
||||
result_block_string = ""
|
||||
if "INSTRUMENTATION_CODE:" in line:
|
||||
inst_finished_bundle = _ParseInstrumentationFinishedBundle(result_block_string)
|
||||
result_block_string = ""
|
||||
|
||||
return (test_results, inst_finished_bundle)
|
||||
|
||||
|
||||
def _ParseInstrumentationFinishedBundle(result):
|
||||
"""Given the raw output of "am instrument" returns a dictionary of the
|
||||
key/value pairs from the bundle passed into
|
||||
ActivityManager.finishInstrumentation().
|
||||
|
||||
Args:
|
||||
result (string): Raw output of "am instrument"
|
||||
|
||||
Return:
|
||||
inst_finished_bundle (dict): Key/value pairs contained in the bundle that is
|
||||
passed into ActivityManager.finishInstrumentation(). Included in this bundle is the return
|
||||
code of the Instrumentation process, any error codes reported by the
|
||||
activity manager, and any results explicity added by the instrumentation
|
||||
code.
|
||||
"""
|
||||
|
||||
re_result = re.compile(r'INSTRUMENTATION_RESULT: ([^=]+)=(.*)$')
|
||||
re_code = re.compile(r'INSTRUMENTATION_CODE: (\-?\d)$')
|
||||
result_dict = {}
|
||||
key = ''
|
||||
val = ''
|
||||
last_tag = ''
|
||||
|
||||
for line in result.split('\n'):
|
||||
line = line.strip(string.whitespace)
|
||||
if re_result.match(line):
|
||||
last_tag = 'INSTRUMENTATION_RESULT'
|
||||
key = re_result.search(line).group(1).strip(string.whitespace)
|
||||
if key.startswith('performance.'):
|
||||
key = key[len('performance.'):]
|
||||
val = re_result.search(line).group(2).strip(string.whitespace)
|
||||
try:
|
||||
result_dict[key] = float(val)
|
||||
except ValueError:
|
||||
result_dict[key] = val
|
||||
except TypeError:
|
||||
result_dict[key] = val
|
||||
elif re_code.match(line):
|
||||
last_tag = 'INSTRUMENTATION_CODE'
|
||||
key = 'code'
|
||||
val = re_code.search(line).group(1).strip(string.whitespace)
|
||||
result_dict[key] = val
|
||||
elif 'INSTRUMENTATION_ABORTED:' in line:
|
||||
last_tag = 'INSTRUMENTATION_ABORTED'
|
||||
key = 'INSTRUMENTATION_ABORTED'
|
||||
val = ''
|
||||
result_dict[key] = val
|
||||
elif last_tag == 'INSTRUMENTATION_RESULT':
|
||||
result_dict[key] += '\n' + line
|
||||
|
||||
if not result_dict.has_key('code'):
|
||||
result_dict['code'] = '0'
|
||||
result_dict['shortMsg'] = "No result returned from instrumentation"
|
||||
|
||||
return result_dict
|
||||
|
||||
|
||||
class TestResult(object):
|
||||
"""A class that contains information about a single test result."""
|
||||
|
||||
def __init__(self, result_block_string):
|
||||
"""
|
||||
Args:
|
||||
result_block_string (string): Is a single "block" of output. A single
|
||||
"block" would be either a "test started" status report, or a "test
|
||||
finished" status report.
|
||||
"""
|
||||
|
||||
self._test_name = None
|
||||
self._status_code = None
|
||||
self._failure_reason = None
|
||||
self._fields_map = {}
|
||||
|
||||
re_status_code = re.search(r'INSTRUMENTATION_STATUS_CODE: '
|
||||
'(?P<status_code>1|0|-1|-2)', result_block_string)
|
||||
re_fields = re.compile(r'INSTRUMENTATION_STATUS: '
|
||||
'(?P<key>[\w.]+)=(?P<value>.*?)(?=\nINSTRUMENTATION_STATUS)', re.DOTALL)
|
||||
|
||||
for field in re_fields.finditer(result_block_string):
|
||||
key, value = (field.group('key').strip(), field.group('value').strip())
|
||||
if key.startswith('performance.'):
|
||||
key = key[len('performance.'):]
|
||||
self._fields_map[key] = value
|
||||
self._fields_map.setdefault('class')
|
||||
self._fields_map.setdefault('test')
|
||||
|
||||
self._test_name = '%s:%s' % (self._fields_map['class'],
|
||||
self._fields_map['test'])
|
||||
self._status_code = int(re_status_code.group('status_code'))
|
||||
if 'stack' in self._fields_map:
|
||||
self._failure_reason = self._fields_map['stack']
|
||||
|
||||
def GetTestName(self):
|
||||
return self._test_name
|
||||
|
||||
def GetStatusCode(self):
|
||||
return self._status_code
|
||||
|
||||
def GetFailureReason(self):
|
||||
return self._failure_reason
|
||||
|
||||
def GetResultFields(self):
|
||||
return self._fields_map
|
||||
@@ -1,198 +0,0 @@
|
||||
#!/usr/bin/python2.4
|
||||
#
|
||||
#
|
||||
# Copyright 2008, The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Contains utility functions for interacting with the Android build system."""
|
||||
|
||||
# Python imports
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
# local imports
|
||||
import errors
|
||||
import logger
|
||||
|
||||
|
||||
def GetTop():
|
||||
"""Returns the full pathname of the "top" of the Android development tree.
|
||||
|
||||
Assumes build environment has been properly configured by envsetup &
|
||||
lunch/choosecombo.
|
||||
|
||||
Returns:
|
||||
the absolute file path of the Android build root.
|
||||
|
||||
Raises:
|
||||
AbortError: if Android build root could not be found.
|
||||
"""
|
||||
# TODO: does this need to be reimplemented to be like gettop() in envsetup.sh
|
||||
root_path = os.getenv("ANDROID_BUILD_TOP")
|
||||
if root_path is None:
|
||||
logger.Log("Error: ANDROID_BUILD_TOP not defined. Please run "
|
||||
"envsetup.sh and lunch/choosecombo")
|
||||
raise errors.AbortError
|
||||
return root_path
|
||||
|
||||
|
||||
def GetHostOutDir():
|
||||
"""Returns the full pathname of out/host/arch of the Android development tree.
|
||||
|
||||
Assumes build environment has been properly configured by envsetup &
|
||||
lunch/choosecombo.
|
||||
|
||||
Returns:
|
||||
the absolute file path of the Android host output directory.
|
||||
Raises:
|
||||
AbortError: if Android host output directory could not be found.
|
||||
"""
|
||||
host_out_path = os.getenv("ANDROID_HOST_OUT")
|
||||
if host_out_path is None:
|
||||
logger.Log("Error: ANDROID_HOST_OUT not defined. Please run "
|
||||
"envsetup.sh and lunch/choosecombo")
|
||||
raise errors.AbortError
|
||||
return host_out_path
|
||||
|
||||
|
||||
def GetOutDir():
|
||||
"""Returns the full pathname of the "out" of the Android development tree.
|
||||
|
||||
Assumes build environment has been properly configured by envsetup &
|
||||
lunch/choosecombo.
|
||||
|
||||
Returns:
|
||||
the absolute file path of the Android build output directory.
|
||||
"""
|
||||
root_path = os.getenv("OUT_DIR")
|
||||
if root_path is None:
|
||||
root_path = os.path.join(GetTop(), "out")
|
||||
return root_path
|
||||
|
||||
|
||||
def GetHostBin():
|
||||
"""Compute the full pathname to the host binary directory.
|
||||
|
||||
Typically $ANDROID_HOST_OUT/bin.
|
||||
|
||||
Assumes build environment has been properly configured by envsetup &
|
||||
lunch/choosecombo.
|
||||
|
||||
Returns:
|
||||
The absolute file path of the Android host binary directory.
|
||||
|
||||
Raises:
|
||||
AbortError: if Android host binary directory could not be found.
|
||||
"""
|
||||
path = os.path.join(GetHostOutDir(), "bin")
|
||||
if not os.path.exists(path):
|
||||
logger.Log("Error: Host bin path could not be found %s" % path)
|
||||
raise errors.AbortError
|
||||
return path
|
||||
|
||||
|
||||
def GetProductOut():
|
||||
"""Returns the full pathname to the target/product directory.
|
||||
|
||||
Typically the value of the env variable $ANDROID_PRODUCT_OUT.
|
||||
|
||||
Assumes build environment has been properly configured by envsetup &
|
||||
lunch/choosecombo.
|
||||
|
||||
Returns:
|
||||
The absolute file path of the Android product directory.
|
||||
|
||||
Raises:
|
||||
AbortError: if Android product directory could not be found.
|
||||
"""
|
||||
path = os.getenv("ANDROID_PRODUCT_OUT")
|
||||
if path is None:
|
||||
logger.Log("Error: ANDROID_PRODUCT_OUT not defined. Please run "
|
||||
"envsetup.sh and lunch/choosecombo")
|
||||
raise errors.AbortError
|
||||
return path
|
||||
|
||||
|
||||
def GetTargetNativeTestPath():
|
||||
"""Returns the full pathname to target/product data/nativetest/ directory.
|
||||
|
||||
Assumes build environment has been properly configured by envsetup &
|
||||
lunch/choosecombo.
|
||||
|
||||
Returns:
|
||||
The absolute file path of the Android target native test directory.
|
||||
|
||||
Raises:
|
||||
AbortError: if Android target native test directory could not be found.
|
||||
"""
|
||||
path = os.path.join(GetProductOut(), "data", "nativetest")
|
||||
if not os.path.exists(path):
|
||||
logger.Log("Error: Target native test path could not be found")
|
||||
raise errors.AbortError
|
||||
return path
|
||||
|
||||
|
||||
def GetTargetSystemBin():
|
||||
"""Returns the full pathname to the target/product system/bin directory.
|
||||
|
||||
Typically the value of the env variable $ANDROID_PRODUCT_OUT/system/bin
|
||||
|
||||
Assumes build environment has been properly configured by envsetup &
|
||||
lunch/choosecombo.
|
||||
|
||||
Returns:
|
||||
The absolute file path of the Android target system bin directory.
|
||||
|
||||
Raises:
|
||||
AbortError: if Android target system bin directory could not be found.
|
||||
"""
|
||||
path = os.path.join(GetProductOut(), "system", "bin")
|
||||
if not os.path.exists(path):
|
||||
logger.Log("Error: Target system bin path could not be found")
|
||||
raise errors.AbortError
|
||||
return path
|
||||
|
||||
def GetHostLibraryPath():
|
||||
"""Returns the full pathname to the host java library output directory.
|
||||
|
||||
Typically $ANDROID_HOST_OUT/framework.
|
||||
|
||||
Assumes build environment has been properly configured by envsetup &
|
||||
lunch/choosecombo.
|
||||
|
||||
Returns:
|
||||
The absolute file path of the Android host java library directory.
|
||||
|
||||
Raises:
|
||||
AbortError: if Android host java library directory could not be found.
|
||||
"""
|
||||
path = os.path.join(GetHostOutDir(), "framework")
|
||||
if not os.path.exists(path):
|
||||
logger.Log("Error: Host library path could not be found %s" % path)
|
||||
raise errors.AbortError
|
||||
return path
|
||||
|
||||
def GetTestAppPath():
|
||||
"""Returns the full pathname to the test app build output directory.
|
||||
|
||||
Typically $ANDROID_PRODUCT_OUT/data/app
|
||||
|
||||
Assumes build environment has been properly configured by envsetup &
|
||||
lunch/choosecombo.
|
||||
|
||||
Returns:
|
||||
The absolute file path of the Android test app build directory.
|
||||
"""
|
||||
return os.path.join(GetProductOut(), "data", "app")
|
||||
@@ -1,127 +0,0 @@
|
||||
#!/usr/bin/python2.4
|
||||
#
|
||||
#
|
||||
# Copyright 2009, The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""In memory representation of AndroidManifest.xml file.
|
||||
|
||||
Specification of AndroidManifest.xml can be found at
|
||||
http://developer.android.com/guide/topics/manifest/manifest-intro.html
|
||||
"""
|
||||
|
||||
# python imports
|
||||
import os
|
||||
import xml.dom.minidom
|
||||
import xml.parsers
|
||||
|
||||
|
||||
class AndroidManifest(object):
|
||||
"""In memory representation of AndroidManifest.xml file."""
|
||||
|
||||
FILENAME = 'AndroidManifest.xml'
|
||||
|
||||
def __init__(self, app_path=None):
|
||||
if app_path:
|
||||
self._ParseManifest(app_path)
|
||||
|
||||
def GetAppPath(self):
|
||||
"""Retrieve file system path to this manifest file's directory."""
|
||||
return self._app_path
|
||||
|
||||
def GetPackageName(self):
|
||||
"""Retrieve package name defined at <manifest package="...">.
|
||||
|
||||
Returns:
|
||||
Package name if defined, otherwise None
|
||||
"""
|
||||
manifest = self._GetManifestElement()
|
||||
if not manifest or not manifest.hasAttribute('package'):
|
||||
return None
|
||||
return manifest.getAttribute('package')
|
||||
|
||||
def _ParseManifest(self, app_path):
|
||||
"""Parse AndroidManifest.xml at the specified path.
|
||||
|
||||
Args:
|
||||
app_path: path to folder containing AndroidManifest.xml
|
||||
Raises:
|
||||
IOError: AndroidManifest.xml cannot be found at given path, or cannot be
|
||||
opened for reading
|
||||
"""
|
||||
self._app_path = app_path
|
||||
self._manifest_path = os.path.join(app_path, self.FILENAME)
|
||||
self._dom = xml.dom.minidom.parse(self._manifest_path)
|
||||
|
||||
def AddUsesSdk(self, min_sdk_version):
|
||||
"""Adds a uses-sdk element to manifest.
|
||||
|
||||
Args:
|
||||
min_sdk_version: value to provide for minSdkVersion attribute.
|
||||
"""
|
||||
manifest = self._GetManifestElement()
|
||||
uses_sdk_elements = manifest.getElementsByTagName('uses-sdk')
|
||||
if uses_sdk_elements:
|
||||
uses_sdk_element = uses_sdk_elements[0]
|
||||
else:
|
||||
uses_sdk_element = self._dom.createElement('uses-sdk')
|
||||
manifest.appendChild(uses_sdk_element)
|
||||
|
||||
uses_sdk_element.setAttribute('android:minSdkVersion', min_sdk_version)
|
||||
self._SaveXml()
|
||||
|
||||
def GetInstrumentationNames(self):
|
||||
"""Get the instrumentation names from manifest.
|
||||
|
||||
Returns:
|
||||
list of names, might be empty
|
||||
"""
|
||||
instr_elements = self._dom.getElementsByTagName('instrumentation')
|
||||
instrs = []
|
||||
for element in instr_elements:
|
||||
instrs.append(element.getAttribute('android:name'))
|
||||
return instrs
|
||||
|
||||
def _GetManifestElement(self):
|
||||
"""Retrieve the root manifest element.
|
||||
|
||||
Returns:
|
||||
the DOM element for manifest or None.
|
||||
"""
|
||||
manifests = self._dom.getElementsByTagName('manifest')
|
||||
if not manifests:
|
||||
return None
|
||||
return manifests[0]
|
||||
|
||||
def _SaveXml(self):
|
||||
"""Saves the manifest to disk."""
|
||||
self._dom.writexml(open(self._manifest_path, mode='w'), encoding='utf-8')
|
||||
|
||||
|
||||
def CreateAndroidManifest(path):
|
||||
"""Factory method for creating a AndroidManifest.
|
||||
|
||||
Args:
|
||||
path: the directory for the manifest file
|
||||
|
||||
Return:
|
||||
the AndroidManifest or None if there was no file present
|
||||
"""
|
||||
manifest_path = os.path.join(path, AndroidManifest.FILENAME)
|
||||
if os.path.isfile(manifest_path):
|
||||
manifest = AndroidManifest()
|
||||
manifest._ParseManifest(path)
|
||||
return manifest
|
||||
else:
|
||||
return None
|
||||
@@ -1,175 +0,0 @@
|
||||
#!/usr/bin/python2.4
|
||||
#
|
||||
#
|
||||
# Copyright 2009, The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""In memory representation of Android.mk file.
|
||||
|
||||
Specifications for Android.mk can be found at
|
||||
development/ndk/docs/ANDROID-MK.txt
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
from sets import Set
|
||||
|
||||
import logger
|
||||
|
||||
class AndroidMK(object):
|
||||
"""In memory representation of Android.mk file."""
|
||||
|
||||
_RE_INCLUDE = re.compile(r'include\s+\$\((.+)\)')
|
||||
_RE_VARIABLE_REF = re.compile(r'\$\((.+)\)')
|
||||
_VAR_DELIMITER = ":="
|
||||
FILENAME = "Android.mk"
|
||||
CERTIFICATE = "LOCAL_CERTIFICATE"
|
||||
PACKAGE_NAME = "LOCAL_PACKAGE_NAME"
|
||||
|
||||
def __init__(self):
|
||||
self._includes = Set() # variables included in makefile
|
||||
self._variables = {} # variables defined in makefile
|
||||
self._has_gtestlib = False
|
||||
|
||||
def _ProcessMKLine(self, line):
|
||||
"""Add a variable definition or include.
|
||||
|
||||
Ignores unrecognized lines.
|
||||
|
||||
Args:
|
||||
line: line of text from makefile
|
||||
"""
|
||||
m = self._RE_INCLUDE.match(line)
|
||||
if m:
|
||||
self._includes.add(m.group(1))
|
||||
else:
|
||||
parts = line.split(self._VAR_DELIMITER)
|
||||
if len(parts) > 1:
|
||||
self._variables[parts[0].strip()] = parts[1].strip()
|
||||
# hack, look for explicit mention of libgtest_main
|
||||
if line.find('libgtest_main') != -1:
|
||||
self._has_gtestlib = True
|
||||
|
||||
def GetVariable(self, identifier):
|
||||
"""Retrieve makefile variable.
|
||||
|
||||
Args:
|
||||
identifier: name of variable to retrieve
|
||||
Returns:
|
||||
value of specified identifier, None if identifier not found in makefile
|
||||
"""
|
||||
# use dict.get(x) rather than dict[x] to avoid KeyError exception,
|
||||
# so None is returned if identifier not found
|
||||
return self._variables.get(identifier, None)
|
||||
|
||||
def GetExpandedVariable(self, identifier):
|
||||
"""Retrieve makefile variable.
|
||||
|
||||
If variable value refers to another variable, recursively expand it to
|
||||
find its literal value
|
||||
|
||||
Args:
|
||||
identifier: name of variable to retrieve
|
||||
Returns:
|
||||
value of specified identifier, None if identifier not found in makefile
|
||||
"""
|
||||
# use dict.get(x) rather than dict[x] to avoid KeyError exception,
|
||||
# so None is returned if identifier not found
|
||||
return self.__RecursiveGetVariable(identifier, Set())
|
||||
|
||||
def __RecursiveGetVariable(self, identifier, visited_variables):
|
||||
variable_value = self.GetVariable(identifier)
|
||||
if not variable_value:
|
||||
return None
|
||||
if variable_value in visited_variables:
|
||||
raise RuntimeError('recursive loop found for makefile variable %s'
|
||||
% variable_value)
|
||||
m = self._RE_VARIABLE_REF.match(variable_value)
|
||||
if m:
|
||||
logger.SilentLog('Found variable ref %s for identifier %s'
|
||||
% (variable_value, identifier))
|
||||
variable_ref = m.group(1)
|
||||
visited_variables.add(variable_ref)
|
||||
return self.__RecursiveGetVariable(variable_ref, visited_variables)
|
||||
else:
|
||||
return variable_value
|
||||
|
||||
def HasInclude(self, identifier):
|
||||
"""Check variable is included in makefile.
|
||||
|
||||
Args:
|
||||
identifer: name of variable to check
|
||||
Returns:
|
||||
True if identifer is included in makefile, otherwise False
|
||||
"""
|
||||
return identifier in self._includes
|
||||
|
||||
def IncludesMakefilesUnder(self):
|
||||
"""Check if makefile has a 'include makefiles under here' rule"""
|
||||
return self.HasInclude('call all-makefiles-under,$(LOCAL_PATH)')
|
||||
|
||||
def HasJavaLibrary(self, library_name):
|
||||
"""Check if library is specified as a local java library in makefile.
|
||||
|
||||
Args:
|
||||
library_name: name of library to check
|
||||
Returns:
|
||||
True if library_name is included in makefile, otherwise False
|
||||
"""
|
||||
java_lib_string = self.GetExpandedVariable('LOCAL_JAVA_LIBRARIES')
|
||||
if java_lib_string:
|
||||
java_libs = java_lib_string.split(' ')
|
||||
return library_name in java_libs
|
||||
return False
|
||||
|
||||
def HasGTest(self):
|
||||
"""Check if makefile includes rule to build a native gtest.
|
||||
|
||||
Returns:
|
||||
True if rule to build native test is in makefile, otherwise False
|
||||
"""
|
||||
return self._has_gtestlib or self.HasInclude('BUILD_NATIVE_TEST')
|
||||
|
||||
def _ParseMK(self, mk_path):
|
||||
"""Parse Android.mk at the specified path.
|
||||
|
||||
Args:
|
||||
mk_path: path to Android.mk
|
||||
Raises:
|
||||
IOError: Android.mk cannot be found at given path, or cannot be opened
|
||||
for reading
|
||||
"""
|
||||
mk = open(mk_path)
|
||||
for line in mk:
|
||||
self._ProcessMKLine(line)
|
||||
mk.close()
|
||||
|
||||
|
||||
def CreateAndroidMK(path, filename=AndroidMK.FILENAME):
|
||||
"""Factory method for creating a AndroidMK.
|
||||
|
||||
Args:
|
||||
path: the directory of the make file
|
||||
filename: the filename of the makefile
|
||||
|
||||
Return:
|
||||
the AndroidMK or None if there was no file present
|
||||
"""
|
||||
mk_path = os.path.join(path, filename)
|
||||
if os.path.isfile(mk_path):
|
||||
mk = AndroidMK()
|
||||
mk._ParseMK(mk_path)
|
||||
return mk
|
||||
else:
|
||||
return None
|
||||
@@ -1 +0,0 @@
|
||||
__all__ = ['coverage', 'coverage_targets', 'coverage_target']
|
||||
@@ -1,338 +0,0 @@
|
||||
#!/usr/bin/python2.4
|
||||
#
|
||||
#
|
||||
# Copyright 2008, The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Utilities for generating code coverage reports for Android tests."""
|
||||
|
||||
# Python imports
|
||||
import glob
|
||||
import optparse
|
||||
import os
|
||||
|
||||
# local imports
|
||||
import android_build
|
||||
import android_mk
|
||||
import coverage_target
|
||||
import coverage_targets
|
||||
import errors
|
||||
import logger
|
||||
import run_command
|
||||
|
||||
|
||||
class CoverageGenerator(object):
|
||||
"""Helper utility for obtaining code coverage results on Android.
|
||||
|
||||
Intended to simplify the process of building,running, and generating code
|
||||
coverage results for a pre-defined set of tests and targets
|
||||
"""
|
||||
|
||||
# path to EMMA host jar, relative to Android build root
|
||||
_EMMA_JAR = os.path.join("external", "emma", "lib", "emma.jar")
|
||||
_TEST_COVERAGE_EXT = "ec"
|
||||
# root path of generated coverage report files, relative to Android build root
|
||||
_COVERAGE_REPORT_PATH = "emma"
|
||||
_TARGET_DEF_FILE = "coverage_targets.xml"
|
||||
_CORE_TARGET_PATH = os.path.join("development", "testrunner",
|
||||
_TARGET_DEF_FILE)
|
||||
# vendor glob file path patterns to tests, relative to android
|
||||
# build root
|
||||
_VENDOR_TARGET_PATH = os.path.join("vendor", "*", "tests", "testinfo",
|
||||
_TARGET_DEF_FILE)
|
||||
|
||||
# path to root of target build intermediates
|
||||
_TARGET_INTERMEDIATES_BASE_PATH = os.path.join("target", "common",
|
||||
"obj")
|
||||
|
||||
def __init__(self, adb_interface):
|
||||
self._root_path = android_build.GetTop()
|
||||
self._out_path = android_build.GetOut()
|
||||
self._output_root_path = os.path.join(self._out_path,
|
||||
self._COVERAGE_REPORT_PATH)
|
||||
self._emma_jar_path = os.path.join(self._root_path, self._EMMA_JAR)
|
||||
self._adb = adb_interface
|
||||
self._targets_manifest = self._ReadTargets()
|
||||
|
||||
def ExtractReport(self,
|
||||
test_suite_name,
|
||||
target,
|
||||
device_coverage_path,
|
||||
output_path=None,
|
||||
test_qualifier=None):
|
||||
"""Extract runtime coverage data and generate code coverage report.
|
||||
|
||||
Assumes test has just been executed.
|
||||
Args:
|
||||
test_suite_name: name of TestSuite to generate coverage data for
|
||||
target: the CoverageTarget to use as basis for coverage calculation
|
||||
device_coverage_path: location of coverage file on device
|
||||
output_path: path to place output files in. If None will use
|
||||
<android_out_path>/<_COVERAGE_REPORT_PATH>/<target>/<test[-qualifier]>
|
||||
test_qualifier: designates mode test was run with. e.g size=small.
|
||||
If not None, this will be used to customize output_path as shown above.
|
||||
|
||||
Returns:
|
||||
absolute file path string of generated html report file.
|
||||
"""
|
||||
if output_path is None:
|
||||
report_name = test_suite_name
|
||||
if test_qualifier:
|
||||
report_name = report_name + "-" + test_qualifier
|
||||
output_path = os.path.join(self._out_path,
|
||||
self._COVERAGE_REPORT_PATH,
|
||||
target.GetName(),
|
||||
report_name)
|
||||
|
||||
coverage_local_name = "%s.%s" % (report_name,
|
||||
self._TEST_COVERAGE_EXT)
|
||||
coverage_local_path = os.path.join(output_path,
|
||||
coverage_local_name)
|
||||
if self._adb.Pull(device_coverage_path, coverage_local_path):
|
||||
|
||||
report_path = os.path.join(output_path,
|
||||
report_name)
|
||||
return self._GenerateReport(report_path, coverage_local_path, [target],
|
||||
do_src=True)
|
||||
return None
|
||||
|
||||
def _GenerateReport(self, report_path, coverage_file_path, targets,
|
||||
do_src=True):
|
||||
"""Generate the code coverage report.
|
||||
|
||||
Args:
|
||||
report_path: absolute file path of output file, without extension
|
||||
coverage_file_path: absolute file path of code coverage result file
|
||||
targets: list of CoverageTargets to use as base for code coverage
|
||||
measurement.
|
||||
do_src: True if generate coverage report with source linked in.
|
||||
Note this will increase size of generated report.
|
||||
|
||||
Returns:
|
||||
absolute file path to generated report file.
|
||||
"""
|
||||
input_metadatas = self._GatherMetadatas(targets)
|
||||
|
||||
if do_src:
|
||||
src_arg = self._GatherSrcs(targets)
|
||||
else:
|
||||
src_arg = ""
|
||||
|
||||
report_file = "%s.html" % report_path
|
||||
cmd1 = ("java -cp %s emma report -r html -in %s %s %s " %
|
||||
(self._emma_jar_path, coverage_file_path, input_metadatas, src_arg))
|
||||
cmd2 = "-Dreport.html.out.file=%s" % report_file
|
||||
self._RunCmd(cmd1 + cmd2)
|
||||
return report_file
|
||||
|
||||
def _GatherMetadatas(self, targets):
|
||||
"""Builds the emma input metadata argument from provided targets.
|
||||
|
||||
Args:
|
||||
targets: list of CoverageTargets
|
||||
|
||||
Returns:
|
||||
input metadata argument string
|
||||
"""
|
||||
input_metadatas = ""
|
||||
for target in targets:
|
||||
input_metadata = os.path.join(self._GetBuildIntermediatePath(target),
|
||||
"coverage.em")
|
||||
input_metadatas += " -in %s" % input_metadata
|
||||
return input_metadatas
|
||||
|
||||
def _GetBuildIntermediatePath(self, target):
|
||||
return os.path.join(
|
||||
self._out_path, self._TARGET_INTERMEDIATES_BASE_PATH, target.GetType(),
|
||||
"%s_intermediates" % target.GetName())
|
||||
|
||||
def _GatherSrcs(self, targets):
|
||||
"""Builds the emma input source path arguments from provided targets.
|
||||
|
||||
Args:
|
||||
targets: list of CoverageTargets
|
||||
Returns:
|
||||
source path arguments string
|
||||
"""
|
||||
src_list = []
|
||||
for target in targets:
|
||||
target_srcs = target.GetPaths()
|
||||
for path in target_srcs:
|
||||
src_list.append("-sp %s" % os.path.join(self._root_path, path))
|
||||
return " ".join(src_list)
|
||||
|
||||
def _MergeFiles(self, input_paths, dest_path):
|
||||
"""Merges a set of emma coverage files into a consolidated file.
|
||||
|
||||
Args:
|
||||
input_paths: list of string absolute coverage file paths to merge
|
||||
dest_path: absolute file path of destination file
|
||||
"""
|
||||
input_list = []
|
||||
for input_path in input_paths:
|
||||
input_list.append("-in %s" % input_path)
|
||||
input_args = " ".join(input_list)
|
||||
self._RunCmd("java -cp %s emma merge %s -out %s" % (self._emma_jar_path,
|
||||
input_args, dest_path))
|
||||
|
||||
def _RunCmd(self, cmd):
|
||||
"""Runs and logs the given os command."""
|
||||
run_command.RunCommand(cmd, return_output=False)
|
||||
|
||||
def _CombineTargetCoverage(self):
|
||||
"""Combines all target mode code coverage results.
|
||||
|
||||
Will find all code coverage data files in direct sub-directories of
|
||||
self._output_root_path, and combine them into a single coverage report.
|
||||
Generated report is placed at self._output_root_path/android.html
|
||||
"""
|
||||
coverage_files = self._FindCoverageFiles(self._output_root_path)
|
||||
combined_coverage = os.path.join(self._output_root_path,
|
||||
"android.%s" % self._TEST_COVERAGE_EXT)
|
||||
self._MergeFiles(coverage_files, combined_coverage)
|
||||
report_path = os.path.join(self._output_root_path, "android")
|
||||
# don't link to source, to limit file size
|
||||
self._GenerateReport(report_path, combined_coverage,
|
||||
self._targets_manifest.GetTargets(), do_src=False)
|
||||
|
||||
def _CombineTestCoverage(self):
|
||||
"""Consolidates code coverage results for all target result directories."""
|
||||
target_dirs = os.listdir(self._output_root_path)
|
||||
for target_name in target_dirs:
|
||||
output_path = os.path.join(self._output_root_path, target_name)
|
||||
target = self._targets_manifest.GetTarget(target_name)
|
||||
if os.path.isdir(output_path) and target is not None:
|
||||
coverage_files = self._FindCoverageFiles(output_path)
|
||||
combined_coverage = os.path.join(output_path, "%s.%s" %
|
||||
(target_name, self._TEST_COVERAGE_EXT))
|
||||
self._MergeFiles(coverage_files, combined_coverage)
|
||||
report_path = os.path.join(output_path, target_name)
|
||||
self._GenerateReport(report_path, combined_coverage, [target])
|
||||
else:
|
||||
logger.Log("%s is not a valid target directory, skipping" % output_path)
|
||||
|
||||
def _FindCoverageFiles(self, root_path):
|
||||
"""Finds all files in <root_path>/*/*.<_TEST_COVERAGE_EXT>.
|
||||
|
||||
Args:
|
||||
root_path: absolute file path string to search from
|
||||
Returns:
|
||||
list of absolute file path strings of coverage files
|
||||
"""
|
||||
file_pattern = os.path.join(root_path, "*", "*.%s" %
|
||||
self._TEST_COVERAGE_EXT)
|
||||
coverage_files = glob.glob(file_pattern)
|
||||
return coverage_files
|
||||
|
||||
def _ReadTargets(self):
|
||||
"""Parses the set of coverage target data.
|
||||
|
||||
Returns:
|
||||
a CoverageTargets object that contains set of parsed targets.
|
||||
Raises:
|
||||
AbortError if a fatal error occurred when parsing the target files.
|
||||
"""
|
||||
core_target_path = os.path.join(self._root_path, self._CORE_TARGET_PATH)
|
||||
try:
|
||||
targets = coverage_targets.CoverageTargets()
|
||||
targets.Parse(core_target_path)
|
||||
vendor_targets_pattern = os.path.join(self._root_path,
|
||||
self._VENDOR_TARGET_PATH)
|
||||
target_file_paths = glob.glob(vendor_targets_pattern)
|
||||
for target_file_path in target_file_paths:
|
||||
targets.Parse(target_file_path)
|
||||
return targets
|
||||
except errors.ParseError:
|
||||
raise errors.AbortError
|
||||
|
||||
def TidyOutput(self):
|
||||
"""Runs tidy on all generated html files.
|
||||
|
||||
This is needed to the html files can be displayed cleanly on a web server.
|
||||
Assumes tidy is on current PATH.
|
||||
"""
|
||||
logger.Log("Tidying output files")
|
||||
self._TidyDir(self._output_root_path)
|
||||
|
||||
def _TidyDir(self, dir_path):
|
||||
"""Recursively tidy all html files in given dir_path."""
|
||||
html_file_pattern = os.path.join(dir_path, "*.html")
|
||||
html_files_iter = glob.glob(html_file_pattern)
|
||||
for html_file_path in html_files_iter:
|
||||
os.system("tidy -m -errors -quiet %s" % html_file_path)
|
||||
sub_dirs = os.listdir(dir_path)
|
||||
for sub_dir_name in sub_dirs:
|
||||
sub_dir_path = os.path.join(dir_path, sub_dir_name)
|
||||
if os.path.isdir(sub_dir_path):
|
||||
self._TidyDir(sub_dir_path)
|
||||
|
||||
def CombineCoverage(self):
|
||||
"""Create combined coverage reports for all targets and tests."""
|
||||
self._CombineTestCoverage()
|
||||
self._CombineTargetCoverage()
|
||||
|
||||
def GetCoverageTarget(self, name):
|
||||
"""Find the CoverageTarget for given name"""
|
||||
target = self._targets_manifest.GetTarget(name)
|
||||
if target is None:
|
||||
msg = ["Error: test references undefined target %s." % name]
|
||||
msg.append(" Ensure target is defined in %s" % self._TARGET_DEF_FILE)
|
||||
raise errors.AbortError(msg)
|
||||
return target
|
||||
|
||||
def GetCoverageTargetForPath(self, path):
|
||||
"""Find the CoverageTarget for given file system path"""
|
||||
android_mk_path = os.path.join(path, "Android.mk")
|
||||
if os.path.exists(android_mk_path):
|
||||
android_mk_parser = android_mk.CreateAndroidMK(path)
|
||||
target = coverage_target.CoverageTarget()
|
||||
target.SetBuildPath(os.path.join(path, "src"))
|
||||
target.SetName(android_mk_parser.GetVariable(android_mk_parser.PACKAGE_NAME))
|
||||
target.SetType("APPS")
|
||||
return target
|
||||
else:
|
||||
msg = "No Android.mk found at %s" % path
|
||||
raise errors.AbortError(msg)
|
||||
|
||||
|
||||
def EnableCoverageBuild():
|
||||
"""Enable building an Android target with code coverage instrumentation."""
|
||||
os.environ["EMMA_INSTRUMENT"] = "true"
|
||||
|
||||
def Run():
|
||||
"""Does coverage operations based on command line args."""
|
||||
# TODO: do we want to support combining coverage for a single target
|
||||
|
||||
try:
|
||||
parser = optparse.OptionParser(usage="usage: %prog --combine-coverage")
|
||||
parser.add_option(
|
||||
"-c", "--combine-coverage", dest="combine_coverage", default=False,
|
||||
action="store_true", help="Combine coverage results stored given "
|
||||
"android root path")
|
||||
parser.add_option(
|
||||
"-t", "--tidy", dest="tidy", default=False, action="store_true",
|
||||
help="Run tidy on all generated html files")
|
||||
|
||||
options, args = parser.parse_args()
|
||||
|
||||
coverage = CoverageGenerator(None)
|
||||
if options.combine_coverage:
|
||||
coverage.CombineCoverage()
|
||||
if options.tidy:
|
||||
coverage.TidyOutput()
|
||||
except errors.AbortError:
|
||||
logger.SilentLog("Exiting due to AbortError")
|
||||
|
||||
if __name__ == "__main__":
|
||||
Run()
|
||||
@@ -1,48 +0,0 @@
|
||||
#
|
||||
# Copyright 2012, The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
class CoverageTarget:
|
||||
""" Represents a code coverage target definition"""
|
||||
|
||||
def __init__(self):
|
||||
self._name = None
|
||||
self._type = None
|
||||
self._build_path = None
|
||||
self._paths = []
|
||||
|
||||
def GetName(self):
|
||||
return self._name
|
||||
|
||||
def SetName(self, name):
|
||||
self._name = name
|
||||
|
||||
def GetPaths(self):
|
||||
return self._paths
|
||||
|
||||
def AddPath(self, path):
|
||||
self._paths.append(path)
|
||||
|
||||
def GetType(self):
|
||||
return self._type
|
||||
|
||||
def SetType(self, buildtype):
|
||||
self._type = buildtype
|
||||
|
||||
def GetBuildPath(self):
|
||||
return self._build_path
|
||||
|
||||
def SetBuildPath(self, build_path):
|
||||
self._build_path = build_path
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
#!/usr/bin/python2.4
|
||||
#
|
||||
#
|
||||
# Copyright 2008, The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import xml.dom.minidom
|
||||
import xml.parsers
|
||||
import os
|
||||
|
||||
|
||||
import coverage_target
|
||||
import logger
|
||||
import errors
|
||||
|
||||
class CoverageTargets:
|
||||
"""Accessor for the code coverage target xml file
|
||||
Expects the following format:
|
||||
<targets>
|
||||
<target
|
||||
name=""
|
||||
type="JAVA_LIBRARIES|APPS"
|
||||
build_path=""
|
||||
|
||||
[<src path=""/>] (0..*) - These are relative to build_path. If missing,
|
||||
assumes 'src'
|
||||
>/target>
|
||||
|
||||
TODO: add more format checking
|
||||
"""
|
||||
|
||||
_TARGET_TAG_NAME = 'coverage_target'
|
||||
_NAME_ATTR = 'name'
|
||||
_TYPE_ATTR = 'type'
|
||||
_BUILD_ATTR = 'build_path'
|
||||
_SRC_TAG = 'src'
|
||||
_PATH_ATTR = 'path'
|
||||
|
||||
def __init__(self, ):
|
||||
self._target_map= {}
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self._target_map.values())
|
||||
|
||||
def Parse(self, file_path):
|
||||
"""Parse the coverage target data from from given file path, and add it to
|
||||
the current object
|
||||
Args:
|
||||
file_path: absolute file path to parse
|
||||
Raises:
|
||||
errors.ParseError if file_path cannot be parsed
|
||||
"""
|
||||
try:
|
||||
doc = xml.dom.minidom.parse(file_path)
|
||||
except IOError:
|
||||
# Error: The results file does not exist
|
||||
logger.Log('Results file %s does not exist' % file_path)
|
||||
raise errors.ParseError
|
||||
except xml.parsers.expat.ExpatError:
|
||||
logger.Log('Error Parsing xml file: %s ' % file_path)
|
||||
raise errors.ParseError
|
||||
|
||||
target_elements = doc.getElementsByTagName(self._TARGET_TAG_NAME)
|
||||
|
||||
for target_element in target_elements:
|
||||
target = coverage_target.CoverageTarget()
|
||||
self._ParseCoverageTarget(target, target_element)
|
||||
self._AddTarget(target)
|
||||
|
||||
def _AddTarget(self, target):
|
||||
self._target_map[target.GetName()] = target
|
||||
|
||||
def GetBuildTargets(self):
|
||||
""" returns list of target names """
|
||||
build_targets = []
|
||||
for target in self:
|
||||
build_targets.append(target.GetName())
|
||||
return build_targets
|
||||
|
||||
def GetTargets(self):
|
||||
""" returns list of CoverageTarget"""
|
||||
return self._target_map.values()
|
||||
|
||||
def GetTarget(self, name):
|
||||
""" returns CoverageTarget for given name. None if not found """
|
||||
try:
|
||||
return self._target_map[name]
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
def _ParseCoverageTarget(self, target, target_element):
|
||||
"""Parse coverage data from XML.
|
||||
|
||||
Args:
|
||||
target: the Coverage object to populate
|
||||
target_element: the XML element to get data from
|
||||
"""
|
||||
target.SetName(target_element.getAttribute(self._NAME_ATTR))
|
||||
target.SetType(target_element.getAttribute(self._TYPE_ATTR))
|
||||
target.SetBuildPath(target_element.getAttribute(self._BUILD_ATTR))
|
||||
self._paths = []
|
||||
self._ParsePaths(target, target_element)
|
||||
|
||||
def _ParsePaths(self, target, target_element):
|
||||
src_elements = target_element.getElementsByTagName(self._SRC_TAG)
|
||||
if len(src_elements) <= 0:
|
||||
# no src tags specified. Assume build_path + src
|
||||
target.AddPath(os.path.join(target.GetBuildPath(), "src"))
|
||||
for src_element in src_elements:
|
||||
rel_path = src_element.getAttribute(self._PATH_ATTR)
|
||||
target.AddPath(os.path.join(target.GetBuildPath(), rel_path))
|
||||
|
||||
|
||||
def Parse(xml_file_path):
|
||||
"""parses out a file_path class from given path to xml"""
|
||||
targets = CoverageTargets()
|
||||
targets.Parse(xml_file_path)
|
||||
return targets
|
||||
@@ -1,112 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!-- Copyright (C) 2009 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<!--Defines the list of test code coverage targets for core android platform
|
||||
Intent is to list all modules that are present in a typical 'user' build
|
||||
|
||||
TODO: auto-generate this file from build
|
||||
|
||||
Expected syntax in this file is
|
||||
<coverage_targets>
|
||||
<coverage_target name type build_path
|
||||
[<src path=""/>] (0..*)
|
||||
>/coverage_target>
|
||||
|
||||
Where
|
||||
name - unique name of Android target
|
||||
type - one of JAVA_LIBRARIES,APPS
|
||||
build_path - path to build directory for this module, relative to android
|
||||
source tree root
|
||||
src - optional sub-elements. Contains complete set of source code locations
|
||||
for target, relative to build_path. If not present, assumes valeu of "src"
|
||||
|
||||
|
||||
-->
|
||||
|
||||
<coverage_targets>
|
||||
<!-- Java libs -->
|
||||
<coverage_target name="framework" type="JAVA_LIBRARIES"
|
||||
build_path="frameworks/base">
|
||||
<src path="core/java" />
|
||||
<src path="graphics/java" />
|
||||
<src path="im/java" />
|
||||
<src path="location/java" />
|
||||
<src path="media/java" />
|
||||
<src path="opengl/java" />
|
||||
<src path="sax/java" />
|
||||
<src path="telephony/java" />
|
||||
<src path="wifi/java" />
|
||||
</coverage_target>
|
||||
<coverage_target name="android.test.runner"
|
||||
build_path="frameworks/base/test-runner" type="JAVA_LIBRARIES" />
|
||||
|
||||
<!-- apps -->
|
||||
<coverage_target name="ApiDemos" build_path="development/samples/ApiDemos"
|
||||
type="APPS" />
|
||||
<coverage_target name="Calculator" build_path="packages/apps/Calculator"
|
||||
type="APPS" />
|
||||
<coverage_target name="Calendar" build_path="packages/apps/Calendar"
|
||||
type="APPS" />
|
||||
<coverage_target name="Camera" build_path="packages/apps/Camera"
|
||||
type="APPS" />
|
||||
<coverage_target name="Contacts" build_path="packages/apps/Contacts"
|
||||
type="APPS" />
|
||||
<coverage_target name="DeskClock" build_path="packages/apps/DeskClock"
|
||||
type="APPS" />
|
||||
<coverage_target name="Dialer" build_path="packages/apps/Dialer"
|
||||
type="APPS" />
|
||||
<coverage_target name="Email" build_path="packages/apps/Email"
|
||||
type="APPS" />
|
||||
<coverage_target name="Exchange" build_path="packages/apps/Exchange"
|
||||
type="APPS" />
|
||||
<coverage_target name="Settings" build_path="packages/apps/Settings"
|
||||
type="APPS" />
|
||||
<coverage_target name="Phone" build_path="packages/apps/Phone"
|
||||
type="APPS" />
|
||||
<coverage_target name="QuickSearchBox" build_path="packages/apps/QuickSearchBox"
|
||||
type="APPS" />
|
||||
<coverage_target name="Launcher2" build_path="packages/apps/Launcher2"
|
||||
type="APPS" />
|
||||
<coverage_target name="Mms" build_path="packages/apps/Mms" type="APPS" />
|
||||
<coverage_target name="Music" build_path="packages/apps/Music"
|
||||
type="APPS" />
|
||||
<coverage_target name="SystemUI" build_path="frameworks/base/packages/SystemUI"
|
||||
type="APPS" />
|
||||
<coverage_target name="VoiceDialer" build_path="packages/apps/VoiceDialer"
|
||||
type="APPS" />
|
||||
|
||||
<!-- content providers -->
|
||||
<coverage_target name="CalendarProvider"
|
||||
build_path="packages/providers/CalendarProvider" type="APPS" />
|
||||
<coverage_target name="ContactsProvider"
|
||||
build_path="packages/providers/ContactsProvider" type="APPS" />
|
||||
<coverage_target name="DownloadProvider"
|
||||
build_path="packages/providers/DownloadProvider" type="APPS" />
|
||||
<coverage_target name="DrmProvider" build_path="packages/providers/drm"
|
||||
type="APPS" />
|
||||
<coverage_target name="MediaProvider"
|
||||
build_path="packages/providers/MediaProvider" type="APPS" />
|
||||
<coverage_target name="SettingsProvider"
|
||||
build_path="frameworks/base/packages/SettingsProvider" type="APPS" />
|
||||
<coverage_target name="TelephonyProvider"
|
||||
build_path="packages/providers/telephony" type="APPS" />
|
||||
|
||||
<!-- input methods -->
|
||||
<coverage_target name="LatinIME" build_path="packages/inputmethods/LatinIME"
|
||||
type="APPS" />
|
||||
|
||||
</coverage_targets>
|
||||
@@ -1,246 +0,0 @@
|
||||
#!/usr/bin/python2.4
|
||||
#
|
||||
#
|
||||
# Copyright 2009, The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Utility to create Android project files for tests."""
|
||||
|
||||
# python imports
|
||||
import datetime
|
||||
import optparse
|
||||
import os
|
||||
import string
|
||||
import sys
|
||||
|
||||
# local imports
|
||||
import android_mk
|
||||
import android_manifest
|
||||
|
||||
|
||||
class TestsConsts(object):
|
||||
"""Constants for test Android.mk and AndroidManifest.xml creation."""
|
||||
|
||||
MK_BUILD_INCLUDE = "call all-makefiles-under,$(LOCAL_PATH)"
|
||||
MK_BUILD_STRING = "\ninclude $(%s)\n" % MK_BUILD_INCLUDE
|
||||
TEST_MANIFEST_TEMPLATE = """<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) $YEAR The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="$PACKAGE_NAME.tests">
|
||||
|
||||
<application>
|
||||
<uses-library android:name="android.test.runner" />
|
||||
</application>
|
||||
|
||||
<instrumentation android:name="android.test.InstrumentationTestRunner"
|
||||
android:targetPackage="$PACKAGE_NAME"
|
||||
android:label="Tests for $MODULE_NAME">
|
||||
</instrumentation>
|
||||
</manifest>
|
||||
"""
|
||||
TEST_MK_TEMPLATE = """LOCAL_PATH := $$(call my-dir)
|
||||
include $$(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
|
||||
LOCAL_JAVA_LIBRARIES := android.test.runner
|
||||
|
||||
LOCAL_SRC_FILES := $$(call all-java-files-under, src)
|
||||
|
||||
LOCAL_PACKAGE_NAME := ${MODULE_NAME}Tests${CERTIFICATE}
|
||||
|
||||
LOCAL_INSTRUMENTATION_FOR := ${MODULE_NAME}
|
||||
|
||||
LOCAL_SDK_VERSION := current
|
||||
|
||||
include $$(BUILD_PACKAGE)
|
||||
"""
|
||||
TESTS_FOLDER = "tests"
|
||||
|
||||
|
||||
def _GenerateTestManifest(manifest, module_name, mapping=None):
|
||||
"""Create and populate tests/AndroidManifest.xml with variable values from
|
||||
Android.mk and AndroidManifest.xml.
|
||||
|
||||
Does nothing if tests/AndroidManifest.xml already exists.
|
||||
|
||||
Args:
|
||||
manifest: AndroidManifest object for application manifest
|
||||
module_name: module name used for labelling
|
||||
mapping: optional user defined mapping of variable values, replaces values
|
||||
extracted from AndroidManifest.xml
|
||||
Raises:
|
||||
IOError: tests/AndroidManifest.xml cannot be opened for writing
|
||||
"""
|
||||
# skip if file already exists
|
||||
tests_path = "%s/%s" % (manifest.GetAppPath(), TestsConsts.TESTS_FOLDER)
|
||||
tests_manifest_path = "%s/%s" % (tests_path, manifest.FILENAME)
|
||||
if os.path.exists(tests_manifest_path):
|
||||
_PrintMessage("%s already exists, not overwritten" % tests_manifest_path)
|
||||
return
|
||||
|
||||
if not mapping:
|
||||
package_name = manifest.GetPackageName()
|
||||
mapping = {"PACKAGE_NAME":package_name, "MODULE_NAME":module_name,
|
||||
"YEAR":datetime.date.today().year}
|
||||
output = string.Template(TestsConsts.TEST_MANIFEST_TEMPLATE).substitute(mapping)
|
||||
|
||||
# create tests folder if not existent
|
||||
if not os.path.exists(tests_path):
|
||||
os.mkdir(tests_path)
|
||||
|
||||
# write tests/AndroidManifest.xml
|
||||
tests_manifest = open(tests_manifest_path, mode="w")
|
||||
tests_manifest.write(output)
|
||||
tests_manifest.close()
|
||||
_PrintMessage("Created %s" % tests_manifest_path)
|
||||
|
||||
|
||||
def _GenerateTestMK(mk, app_path, mapping=None):
|
||||
"""Create and populate tests/Android.mk with variable values from Android.mk.
|
||||
|
||||
Does nothing if tests/Android.mk already exists.
|
||||
|
||||
Args:
|
||||
mk: AndroidMK object for application makefile
|
||||
app_path: path to the application being tested
|
||||
mapping: optional user defined mapping of variable values, replaces
|
||||
values stored in mk
|
||||
Raises:
|
||||
IOError: tests/Android.mk cannot be opened for writing
|
||||
"""
|
||||
# skip if file already exists
|
||||
tests_path = "%s/%s" % (app_path, TestsConsts.TESTS_FOLDER)
|
||||
tests_mk_path = "%s/%s" % (tests_path, mk.FILENAME)
|
||||
if os.path.exists(tests_mk_path):
|
||||
_PrintMessage("%s already exists, not overwritten" % tests_mk_path)
|
||||
return
|
||||
|
||||
# append test build if not existent in makefile
|
||||
if not mk.HasInclude(TestsConsts.MK_BUILD_INCLUDE):
|
||||
mk_path = "%s/%s" % (app_path, mk.FILENAME)
|
||||
mk_file = open(mk_path, mode="a")
|
||||
mk_file.write(TestsConsts.MK_BUILD_STRING)
|
||||
mk_file.close()
|
||||
|
||||
# construct tests/Android.mk
|
||||
# include certificate definition if existent in makefile
|
||||
certificate = mk.GetVariable(mk.CERTIFICATE)
|
||||
if certificate:
|
||||
cert_definition = ("\n%s := %s" % (mk.CERTIFICATE, certificate))
|
||||
else:
|
||||
cert_definition = ""
|
||||
if not mapping:
|
||||
module_name = mk.GetVariable(mk.PACKAGE_NAME)
|
||||
mapping = {"MODULE_NAME":module_name, "CERTIFICATE":cert_definition}
|
||||
output = string.Template(TestsConsts.TEST_MK_TEMPLATE).substitute(mapping)
|
||||
|
||||
# create tests folder if not existent
|
||||
if not os.path.exists(tests_path):
|
||||
os.mkdir(tests_path)
|
||||
|
||||
# write tests/Android.mk to disk
|
||||
tests_mk = open(tests_mk_path, mode="w")
|
||||
tests_mk.write(output)
|
||||
tests_mk.close()
|
||||
_PrintMessage("Created %s" % tests_mk_path)
|
||||
|
||||
|
||||
def _ParseArgs(argv):
|
||||
"""Parse the command line arguments.
|
||||
|
||||
Args:
|
||||
argv: the list of command line arguments
|
||||
Returns:
|
||||
a tuple of options and individual command line arguments.
|
||||
"""
|
||||
parser = optparse.OptionParser(usage="%s <app_path>" % sys.argv[0])
|
||||
options, args = parser.parse_args(argv)
|
||||
if len(args) < 1:
|
||||
_PrintError("Error: Incorrect syntax")
|
||||
parser.print_usage()
|
||||
sys.exit()
|
||||
return (options, args)
|
||||
|
||||
|
||||
def _PrintMessage(msg):
|
||||
print >> sys.stdout, msg
|
||||
|
||||
|
||||
def _PrintError(msg):
|
||||
print >> sys.stderr, msg
|
||||
|
||||
|
||||
def _ValidateInputFiles(mk, manifest):
|
||||
"""Verify that required variables are defined in input files.
|
||||
|
||||
Args:
|
||||
mk: AndroidMK object for application makefile
|
||||
manifest: AndroidManifest object for application manifest
|
||||
Raises:
|
||||
RuntimeError: mk does not define LOCAL_PACKAGE_NAME or
|
||||
manifest does not define package variable
|
||||
"""
|
||||
module_name = mk.GetVariable(mk.PACKAGE_NAME)
|
||||
if not module_name:
|
||||
raise RuntimeError("Variable %s missing from %s" %
|
||||
(mk.PACKAGE_NAME, mk.FILENAME))
|
||||
|
||||
package_name = manifest.GetPackageName()
|
||||
if not package_name:
|
||||
raise RuntimeError("Variable package missing from %s" % manifest.FILENAME)
|
||||
|
||||
|
||||
def main(argv):
|
||||
options, args = _ParseArgs(argv)
|
||||
app_path = args[0];
|
||||
|
||||
if not os.path.exists(app_path):
|
||||
_PrintError("Error: Application path %s not found" % app_path)
|
||||
sys.exit()
|
||||
|
||||
try:
|
||||
mk = android_mk.CreateAndroidMK(path=app_path)
|
||||
manifest = android_manifest.AndroidManifest(app_path=app_path)
|
||||
_ValidateInputFiles(mk, manifest)
|
||||
|
||||
module_name = mk.GetVariable(mk.PACKAGE_NAME)
|
||||
_GenerateTestMK(mk, app_path)
|
||||
_GenerateTestManifest(manifest, module_name)
|
||||
except Exception, e:
|
||||
_PrintError("Error: %s" % e)
|
||||
_PrintError("Error encountered, script aborted")
|
||||
sys.exit()
|
||||
|
||||
src_path = app_path + "/tests/src"
|
||||
if not os.path.exists(src_path):
|
||||
os.mkdir(src_path)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv[1:])
|
||||
@@ -1,46 +0,0 @@
|
||||
#!/usr/bin/python2.4
|
||||
#
|
||||
#
|
||||
# Copyright 2008, The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Defines common exception classes for this package."""
|
||||
|
||||
|
||||
class MsgException(Exception):
|
||||
"""Generic exception with an optional string msg."""
|
||||
def __init__(self, msg=""):
|
||||
self.msg = msg
|
||||
|
||||
|
||||
class WaitForResponseTimedOutError(Exception):
|
||||
"""We sent a command and had to wait too long for response."""
|
||||
|
||||
|
||||
class DeviceUnresponsiveError(Exception):
|
||||
"""Device is unresponsive to command."""
|
||||
|
||||
|
||||
class InstrumentationError(Exception):
|
||||
"""Failed to run instrumentation."""
|
||||
|
||||
|
||||
class AbortError(MsgException):
|
||||
"""Generic exception that indicates a fatal error has occurred and program
|
||||
execution should be aborted."""
|
||||
|
||||
|
||||
class ParseError(MsgException):
|
||||
"""Raised when xml data to parse has unrecognized format."""
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
#!/usr/bin/python2.4
|
||||
#
|
||||
#
|
||||
# Copyright 2007, The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Simple logging utility. Dumps log messages to stdout, and optionally, to a
|
||||
log file.
|
||||
|
||||
Init(path) must be called to enable logging to a file
|
||||
"""
|
||||
|
||||
import datetime
|
||||
|
||||
_LOG_FILE = None
|
||||
_verbose = False
|
||||
_log_time = True
|
||||
|
||||
def Init(log_file_path):
|
||||
"""Set the path to the log file"""
|
||||
global _LOG_FILE
|
||||
_LOG_FILE = log_file_path
|
||||
print "Using log file: %s" % _LOG_FILE
|
||||
|
||||
def GetLogFilePath():
|
||||
"""Returns the path and name of the Log file"""
|
||||
global _LOG_FILE
|
||||
return _LOG_FILE
|
||||
|
||||
def Log(new_str):
|
||||
"""Appends new_str to the end of _LOG_FILE and prints it to stdout.
|
||||
|
||||
Args:
|
||||
# new_str is a string.
|
||||
new_str: 'some message to log'
|
||||
"""
|
||||
msg = _PrependTimeStamp(new_str)
|
||||
print msg
|
||||
_WriteLog(msg)
|
||||
|
||||
def _WriteLog(msg):
|
||||
global _LOG_FILE
|
||||
if _LOG_FILE is not None:
|
||||
file_handle = file(_LOG_FILE, 'a')
|
||||
file_handle.write('\n' + str(msg))
|
||||
file_handle.close()
|
||||
|
||||
def _PrependTimeStamp(log_string):
|
||||
"""Returns the log_string prepended with current timestamp """
|
||||
global _log_time
|
||||
if _log_time:
|
||||
return "# %s: %s" % (datetime.datetime.now().strftime("%m/%d/%y %H:%M:%S"),
|
||||
log_string)
|
||||
else:
|
||||
# timestamp logging disabled
|
||||
return log_string
|
||||
|
||||
def SilentLog(new_str):
|
||||
"""Silently log new_str. Unless verbose mode is enabled, will log new_str
|
||||
only to the log file
|
||||
Args:
|
||||
# new_str is a string.
|
||||
new_str: 'some message to log'
|
||||
"""
|
||||
global _verbose
|
||||
msg = _PrependTimeStamp(new_str)
|
||||
if _verbose:
|
||||
print msg
|
||||
_WriteLog(msg)
|
||||
|
||||
def SetVerbose(new_verbose=True):
|
||||
""" Enable or disable verbose logging"""
|
||||
global _verbose
|
||||
_verbose = new_verbose
|
||||
|
||||
def SetTimestampLogging(new_timestamp=True):
|
||||
""" Enable or disable outputting a timestamp with each log entry"""
|
||||
global _log_time
|
||||
_log_time = new_timestamp
|
||||
|
||||
def main():
|
||||
pass
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,116 +0,0 @@
|
||||
#
|
||||
# Copyright 2012, The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Data structure for processing makefiles."""
|
||||
|
||||
import os
|
||||
|
||||
import android_build
|
||||
import android_mk
|
||||
import errors
|
||||
|
||||
class MakeNode(object):
|
||||
"""Represents single node in make tree."""
|
||||
|
||||
def __init__(self, name, parent):
|
||||
self._name = name
|
||||
self._children_map = {}
|
||||
self._is_leaf = False
|
||||
self._parent = parent
|
||||
self._includes_submake = None
|
||||
if parent:
|
||||
self._path = os.path.join(parent._GetPath(), name)
|
||||
else:
|
||||
self._path = ""
|
||||
|
||||
def _AddPath(self, path_segs):
|
||||
"""Adds given path to this node.
|
||||
|
||||
Args:
|
||||
path_segs: list of path segments
|
||||
"""
|
||||
if not path_segs:
|
||||
# done processing path
|
||||
return self
|
||||
current_seg = path_segs.pop(0)
|
||||
child = self._children_map.get(current_seg)
|
||||
if not child:
|
||||
child = MakeNode(current_seg, self)
|
||||
self._children_map[current_seg] = child
|
||||
return child._AddPath(path_segs)
|
||||
|
||||
def _SetLeaf(self, is_leaf):
|
||||
self._is_leaf = is_leaf
|
||||
|
||||
def _GetPath(self):
|
||||
return self._path
|
||||
|
||||
def _DoesIncludesSubMake(self):
|
||||
if self._includes_submake is None:
|
||||
if self._is_leaf:
|
||||
path = os.path.join(android_build.GetTop(), self._path)
|
||||
mk_parser = android_mk.CreateAndroidMK(path)
|
||||
self._includes_submake = mk_parser.IncludesMakefilesUnder()
|
||||
else:
|
||||
self._includes_submake = False
|
||||
return self._includes_submake
|
||||
|
||||
def _DoesParentIncludeMe(self):
|
||||
return self._parent and self._parent._DoesIncludesSubMake()
|
||||
|
||||
def _BuildPrunedMakeList(self, make_list):
|
||||
if self._is_leaf and not self._DoesParentIncludeMe():
|
||||
make_list.append(os.path.join(self._path, "Android.mk"))
|
||||
for child in self._children_map.itervalues():
|
||||
child._BuildPrunedMakeList(make_list)
|
||||
|
||||
|
||||
class MakeTree(MakeNode):
|
||||
"""Data structure for building a non-redundant set of Android.mk paths.
|
||||
|
||||
Used to collapse set of Android.mk files to use to prevent issuing make
|
||||
command that include same module multiple times due to include rules.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super(MakeTree, self).__init__("", None)
|
||||
|
||||
def AddPath(self, path):
|
||||
"""Adds make directory path to tree.
|
||||
|
||||
Will have no effect if path is already included in make set.
|
||||
|
||||
Args:
|
||||
path: filesystem path to directory to build, relative to build root.
|
||||
"""
|
||||
path = os.path.normpath(path)
|
||||
mk_path = os.path.join(android_build.GetTop(), path, "Android.mk")
|
||||
if not os.path.isfile(mk_path):
|
||||
raise errors.AbortError("%s does not exist" % mk_path)
|
||||
path_segs = path.split(os.sep)
|
||||
child = self._AddPath(path_segs)
|
||||
child._SetLeaf(True)
|
||||
|
||||
def GetPrunedMakeList(self):
|
||||
"""Return as list of the minimum set of Android.mk files necessary to
|
||||
build all leaf nodes in tree.
|
||||
"""
|
||||
make_list = []
|
||||
self._BuildPrunedMakeList(make_list)
|
||||
return make_list
|
||||
|
||||
def IsEmpty(self):
|
||||
return not self._children_map
|
||||
|
||||
@@ -1,196 +0,0 @@
|
||||
#!/usr/bin/python2.4
|
||||
#
|
||||
#
|
||||
# Copyright 2007, The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# System imports
|
||||
import os
|
||||
import signal
|
||||
import subprocess
|
||||
import threading
|
||||
import time
|
||||
|
||||
# local imports
|
||||
import errors
|
||||
import logger
|
||||
|
||||
_abort_on_error = False
|
||||
|
||||
def SetAbortOnError(abort=True):
|
||||
"""Sets behavior of RunCommand to throw AbortError if command process returns
|
||||
a negative error code"""
|
||||
global _abort_on_error
|
||||
_abort_on_error = abort
|
||||
|
||||
def RunCommand(cmd, timeout_time=None, retry_count=3, return_output=True,
|
||||
stdin_input=None):
|
||||
"""Spawn and retry a subprocess to run the given shell command.
|
||||
|
||||
Args:
|
||||
cmd: shell command to run
|
||||
timeout_time: time in seconds to wait for command to run before aborting.
|
||||
retry_count: number of times to retry command
|
||||
return_output: if True return output of command as string. Otherwise,
|
||||
direct output of command to stdout.
|
||||
stdin_input: data to feed to stdin
|
||||
Returns:
|
||||
output of command
|
||||
"""
|
||||
result = None
|
||||
while True:
|
||||
try:
|
||||
result = RunOnce(cmd, timeout_time=timeout_time,
|
||||
return_output=return_output, stdin_input=stdin_input)
|
||||
except errors.WaitForResponseTimedOutError:
|
||||
if retry_count == 0:
|
||||
raise
|
||||
retry_count -= 1
|
||||
logger.Log("No response for %s, retrying" % cmd)
|
||||
else:
|
||||
# Success
|
||||
return result
|
||||
|
||||
def RunOnce(cmd, timeout_time=None, return_output=True, stdin_input=None):
|
||||
"""Spawns a subprocess to run the given shell command.
|
||||
|
||||
Args:
|
||||
cmd: shell command to run
|
||||
timeout_time: time in seconds to wait for command to run before aborting.
|
||||
return_output: if True return output of command as string. Otherwise,
|
||||
direct output of command to stdout.
|
||||
stdin_input: data to feed to stdin
|
||||
Returns:
|
||||
output of command
|
||||
Raises:
|
||||
errors.WaitForResponseTimedOutError if command did not complete within
|
||||
timeout_time seconds.
|
||||
errors.AbortError is command returned error code and SetAbortOnError is on.
|
||||
"""
|
||||
start_time = time.time()
|
||||
so = []
|
||||
pid = []
|
||||
global _abort_on_error, error_occurred
|
||||
error_occurred = False
|
||||
|
||||
def Run():
|
||||
global error_occurred
|
||||
if return_output:
|
||||
output_dest = subprocess.PIPE
|
||||
else:
|
||||
# None means direct to stdout
|
||||
output_dest = None
|
||||
if stdin_input:
|
||||
stdin_dest = subprocess.PIPE
|
||||
else:
|
||||
stdin_dest = None
|
||||
pipe = subprocess.Popen(
|
||||
cmd,
|
||||
executable='/bin/bash',
|
||||
stdin=stdin_dest,
|
||||
stdout=output_dest,
|
||||
stderr=subprocess.STDOUT,
|
||||
shell=True)
|
||||
pid.append(pipe.pid)
|
||||
try:
|
||||
output = pipe.communicate(input=stdin_input)[0]
|
||||
if output is not None and len(output) > 0:
|
||||
so.append(output)
|
||||
except OSError, e:
|
||||
logger.SilentLog("failed to retrieve stdout from: %s" % cmd)
|
||||
logger.Log(e)
|
||||
so.append("ERROR")
|
||||
error_occurred = True
|
||||
if pipe.returncode:
|
||||
logger.SilentLog("Error: %s returned %d error code" %(cmd,
|
||||
pipe.returncode))
|
||||
error_occurred = True
|
||||
|
||||
t = threading.Thread(target=Run)
|
||||
t.start()
|
||||
|
||||
break_loop = False
|
||||
while not break_loop:
|
||||
if not t.isAlive():
|
||||
break_loop = True
|
||||
|
||||
# Check the timeout
|
||||
if (not break_loop and timeout_time is not None
|
||||
and time.time() > start_time + timeout_time):
|
||||
try:
|
||||
os.kill(pid[0], signal.SIGKILL)
|
||||
except OSError:
|
||||
# process already dead. No action required.
|
||||
pass
|
||||
|
||||
logger.SilentLog("about to raise a timeout for: %s" % cmd)
|
||||
raise errors.WaitForResponseTimedOutError
|
||||
if not break_loop:
|
||||
time.sleep(0.1)
|
||||
|
||||
t.join()
|
||||
output = "".join(so)
|
||||
if _abort_on_error and error_occurred:
|
||||
raise errors.AbortError(msg=output)
|
||||
|
||||
return "".join(so)
|
||||
|
||||
|
||||
def RunHostCommand(binary, valgrind=False):
|
||||
"""Run a command on the host (opt using valgrind).
|
||||
|
||||
Runs the host binary and returns the exit code.
|
||||
If successfull, the output (stdout and stderr) are discarded,
|
||||
but printed in case of error.
|
||||
The command can be run under valgrind in which case all the
|
||||
output are always discarded.
|
||||
|
||||
Args:
|
||||
binary: full path of the file to be run.
|
||||
valgrind: If True the command will be run under valgrind.
|
||||
|
||||
Returns:
|
||||
The command exit code (int)
|
||||
"""
|
||||
if not valgrind:
|
||||
subproc = subprocess.Popen(binary, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT)
|
||||
subproc.wait()
|
||||
if subproc.returncode != 0: # In case of error print the output
|
||||
print subproc.communicate()[0]
|
||||
return subproc.returncode
|
||||
else:
|
||||
# Need the full path to valgrind to avoid other versions on the system.
|
||||
subproc = subprocess.Popen(["/usr/bin/valgrind", "--tool=memcheck",
|
||||
"--leak-check=yes", "-q", binary],
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
# Cannot rely on the retcode of valgrind. Instead look for an empty output.
|
||||
valgrind_out = subproc.communicate()[0].strip()
|
||||
if valgrind_out:
|
||||
print valgrind_out
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
|
||||
def HasValgrind():
|
||||
"""Check that /usr/bin/valgrind exists.
|
||||
|
||||
We look for the fullpath to avoid picking up 'alternative' valgrind
|
||||
on the system.
|
||||
|
||||
Returns:
|
||||
True if a system valgrind was found.
|
||||
"""
|
||||
return os.path.exists("/usr/bin/valgrind")
|
||||
@@ -1,558 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2008, The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Command line utility for running Android tests
|
||||
|
||||
runtest helps automate the instructions for building and running tests
|
||||
- It builds the corresponding test package for the code you want to test
|
||||
- It pushes the test package to your device or emulator
|
||||
- It launches InstrumentationTestRunner (or similar) to run the tests you
|
||||
specify.
|
||||
|
||||
runtest supports running tests whose attributes have been pre-defined in
|
||||
_TEST_FILE_NAME files, (runtest <testname>), or by specifying the file
|
||||
system path to the test to run (runtest --path <path>).
|
||||
|
||||
Do runtest --help to see full list of options.
|
||||
"""
|
||||
|
||||
# Python imports
|
||||
import glob
|
||||
import optparse
|
||||
import os
|
||||
import re
|
||||
from sets import Set
|
||||
import sys
|
||||
import time
|
||||
|
||||
# local imports
|
||||
import adb_interface
|
||||
import android_build
|
||||
from coverage import coverage
|
||||
import errors
|
||||
import logger
|
||||
import make_tree
|
||||
import run_command
|
||||
from test_defs import test_defs
|
||||
from test_defs import test_walker
|
||||
|
||||
|
||||
class TestRunner(object):
|
||||
"""Command line utility class for running pre-defined Android test(s)."""
|
||||
|
||||
_TEST_FILE_NAME = "test_defs.xml"
|
||||
|
||||
# file path to android core platform tests, relative to android build root
|
||||
# TODO move these test data files to another directory
|
||||
_CORE_TEST_PATH = os.path.join("development", "testrunner",
|
||||
_TEST_FILE_NAME)
|
||||
|
||||
# vendor glob file path patterns to tests, relative to android
|
||||
# build root
|
||||
_VENDOR_TEST_PATH = os.path.join("vendor", "*", "tests", "testinfo",
|
||||
_TEST_FILE_NAME)
|
||||
|
||||
_RUNTEST_USAGE = (
|
||||
"usage: runtest.py [options] short-test-name[s]\n\n"
|
||||
"The runtest script works in two ways. You can query it "
|
||||
"for a list of tests, or you can launch one or more tests.")
|
||||
|
||||
# default value for make -jX
|
||||
_DEFAULT_JOBS = 16
|
||||
|
||||
_DALVIK_VERIFIER_PROP = "dalvik.vm.dexopt-flags"
|
||||
_DALVIK_VERIFIER_OFF_VALUE = "v=n"
|
||||
_DALVIK_VERIFIER_OFF_PROP = "%s = %s" %(_DALVIK_VERIFIER_PROP, _DALVIK_VERIFIER_OFF_VALUE)
|
||||
|
||||
# regular expression to match path to artifacts to install in make output
|
||||
_RE_MAKE_INSTALL = re.compile(r'INSTALL-PATH:\s([^\s]+)\s(.*)$')
|
||||
|
||||
def __init__(self):
|
||||
# disable logging of timestamp
|
||||
self._root_path = android_build.GetTop()
|
||||
out_base_name = os.path.basename(android_build.GetOutDir())
|
||||
# regular expression to find remote device path from a file path relative
|
||||
# to build root
|
||||
pattern = r'' + out_base_name + r'\/target\/product\/\w+\/(.+)$'
|
||||
self._re_make_install_path = re.compile(pattern)
|
||||
logger.SetTimestampLogging(False)
|
||||
self._adb = None
|
||||
self._known_tests = None
|
||||
self._options = None
|
||||
self._test_args = None
|
||||
self._tests_to_run = None
|
||||
|
||||
def _ProcessOptions(self):
|
||||
"""Processes command-line options."""
|
||||
# TODO error messages on once-only or mutually-exclusive options.
|
||||
user_test_default = os.path.join(os.environ.get("HOME"), ".android",
|
||||
self._TEST_FILE_NAME)
|
||||
|
||||
parser = optparse.OptionParser(usage=self._RUNTEST_USAGE)
|
||||
|
||||
parser.add_option("-l", "--list-tests", dest="only_list_tests",
|
||||
default=False, action="store_true",
|
||||
help="To view the list of tests")
|
||||
parser.add_option("-b", "--skip-build", dest="skip_build", default=False,
|
||||
action="store_true", help="Skip build - just launch")
|
||||
parser.add_option("-j", "--jobs", dest="make_jobs",
|
||||
metavar="X", default=self._DEFAULT_JOBS,
|
||||
help="Number of make jobs to use when building")
|
||||
parser.add_option("-n", "--skip_execute", dest="preview", default=False,
|
||||
action="store_true",
|
||||
help="Do not execute, just preview commands")
|
||||
parser.add_option("-i", "--build-install-only", dest="build_install_only", default=False,
|
||||
action="store_true",
|
||||
help="Do not execute, build tests and install to device only")
|
||||
parser.add_option("-r", "--raw-mode", dest="raw_mode", default=False,
|
||||
action="store_true",
|
||||
help="Raw mode (for output to other tools)")
|
||||
parser.add_option("-a", "--suite-assign", dest="suite_assign_mode",
|
||||
default=False, action="store_true",
|
||||
help="Suite assignment (for details & usage see "
|
||||
"InstrumentationTestRunner)")
|
||||
parser.add_option("-v", "--verbose", dest="verbose", default=False,
|
||||
action="store_true",
|
||||
help="Increase verbosity of %s" % sys.argv[0])
|
||||
parser.add_option("-w", "--wait-for-debugger", dest="wait_for_debugger",
|
||||
default=False, action="store_true",
|
||||
help="Wait for debugger before launching tests")
|
||||
parser.add_option("-c", "--test-class", dest="test_class",
|
||||
help="Restrict test to a specific class")
|
||||
parser.add_option("-m", "--test-method", dest="test_method",
|
||||
help="Restrict test to a specific method")
|
||||
parser.add_option("-p", "--test-package", dest="test_package",
|
||||
help="Restrict test to a specific java package")
|
||||
parser.add_option("-z", "--size", dest="test_size",
|
||||
help="Restrict test to a specific test size")
|
||||
parser.add_option("--annotation", dest="test_annotation",
|
||||
help="Include only those tests tagged with a specific"
|
||||
" annotation")
|
||||
parser.add_option("--not-annotation", dest="test_not_annotation",
|
||||
help="Exclude any tests tagged with a specific"
|
||||
" annotation")
|
||||
parser.add_option("-u", "--user-tests-file", dest="user_tests_file",
|
||||
metavar="FILE", default=user_test_default,
|
||||
help="Alternate source of user test definitions")
|
||||
parser.add_option("-o", "--coverage", dest="coverage",
|
||||
default=False, action="store_true",
|
||||
help="Generate code coverage metrics for test(s)")
|
||||
parser.add_option("--coverage-target", dest="coverage_target_path",
|
||||
default=None,
|
||||
help="Path to app to collect code coverage target data for.")
|
||||
parser.add_option("-k", "--skip-permissions", dest="skip_permissions",
|
||||
default=False, action="store_true",
|
||||
help="Do not grant runtime permissions during test package"
|
||||
" installation.")
|
||||
parser.add_option("-x", "--path", dest="test_path",
|
||||
help="Run test(s) at given file system path")
|
||||
parser.add_option("-t", "--all-tests", dest="all_tests",
|
||||
default=False, action="store_true",
|
||||
help="Run all defined tests")
|
||||
parser.add_option("--continuous", dest="continuous_tests",
|
||||
default=False, action="store_true",
|
||||
help="Run all tests defined as part of the continuous "
|
||||
"test set")
|
||||
parser.add_option("--timeout", dest="timeout",
|
||||
default=300, help="Set a timeout limit (in sec) for "
|
||||
"running native tests on a device (default: 300 secs)")
|
||||
parser.add_option("--suite", dest="suite",
|
||||
help="Run all tests defined as part of the "
|
||||
"the given test suite")
|
||||
parser.add_option("--user", dest="user",
|
||||
help="The user that test apks are installing to."
|
||||
" This is the integer user id, e.g. 0 or 10."
|
||||
" If no user is specified, apk will be installed with"
|
||||
" adb's default behavior, which is currently all users.")
|
||||
parser.add_option("--install-filter", dest="filter_re",
|
||||
help="Regular expression which generated apks have to"
|
||||
" match to be installed to target device. Default is None"
|
||||
" and will install all packages built. This is"
|
||||
" useful when the test path has a lot of apks but you"
|
||||
" only care about one.")
|
||||
parser.add_option("--no-hidden-api-checks", dest="no_hidden_api_checks",
|
||||
default=False, action="store_true",
|
||||
help="Disable hidden API checks in instrumentation"
|
||||
" tests.")
|
||||
group = optparse.OptionGroup(
|
||||
parser, "Targets", "Use these options to direct tests to a specific "
|
||||
"Android target")
|
||||
group.add_option("-e", "--emulator", dest="emulator", default=False,
|
||||
action="store_true", help="use emulator")
|
||||
group.add_option("-d", "--device", dest="device", default=False,
|
||||
action="store_true", help="use device")
|
||||
group.add_option("-s", "--serial", dest="serial",
|
||||
help="use specific serial")
|
||||
parser.add_option_group(group)
|
||||
self._options, self._test_args = parser.parse_args()
|
||||
|
||||
if (not self._options.only_list_tests
|
||||
and not self._options.all_tests
|
||||
and not self._options.continuous_tests
|
||||
and not self._options.suite
|
||||
and not self._options.test_path
|
||||
and len(self._test_args) < 1):
|
||||
parser.print_help()
|
||||
logger.SilentLog("at least one test name must be specified")
|
||||
raise errors.AbortError
|
||||
|
||||
self._adb = adb_interface.AdbInterface()
|
||||
if self._options.emulator:
|
||||
self._adb.SetEmulatorTarget()
|
||||
elif self._options.device:
|
||||
self._adb.SetDeviceTarget()
|
||||
elif self._options.serial is not None:
|
||||
self._adb.SetTargetSerial(self._options.serial)
|
||||
if self._options.verbose:
|
||||
logger.SetVerbose(True)
|
||||
|
||||
if self._options.coverage_target_path:
|
||||
self._options.coverage = True
|
||||
|
||||
self._known_tests = self._ReadTests()
|
||||
|
||||
self._options.host_lib_path = android_build.GetHostLibraryPath()
|
||||
self._options.test_data_path = android_build.GetTestAppPath()
|
||||
|
||||
def _ReadTests(self):
|
||||
"""Parses the set of test definition data.
|
||||
|
||||
Returns:
|
||||
A TestDefinitions object that contains the set of parsed tests.
|
||||
Raises:
|
||||
AbortError: If a fatal error occurred when parsing the tests.
|
||||
"""
|
||||
try:
|
||||
known_tests = test_defs.TestDefinitions()
|
||||
# only read tests when not in path mode
|
||||
if not self._options.test_path:
|
||||
core_test_path = os.path.join(self._root_path, self._CORE_TEST_PATH)
|
||||
if os.path.isfile(core_test_path):
|
||||
known_tests.Parse(core_test_path)
|
||||
# read all <android root>/vendor/*/tests/testinfo/test_defs.xml paths
|
||||
vendor_tests_pattern = os.path.join(self._root_path,
|
||||
self._VENDOR_TEST_PATH)
|
||||
test_file_paths = glob.glob(vendor_tests_pattern)
|
||||
for test_file_path in test_file_paths:
|
||||
known_tests.Parse(test_file_path)
|
||||
if os.path.isfile(self._options.user_tests_file):
|
||||
known_tests.Parse(self._options.user_tests_file)
|
||||
return known_tests
|
||||
except errors.ParseError:
|
||||
raise errors.AbortError
|
||||
|
||||
def _DumpTests(self):
|
||||
"""Prints out set of defined tests."""
|
||||
print "The following tests are currently defined:\n"
|
||||
print "%-25s %-40s %s" % ("name", "build path", "description")
|
||||
print "-" * 80
|
||||
for test in self._known_tests:
|
||||
print "%-25s %-40s %s" % (test.GetName(), test.GetBuildPath(),
|
||||
test.GetDescription())
|
||||
print "\nSee %s for more information" % self._TEST_FILE_NAME
|
||||
|
||||
def _DoBuild(self):
|
||||
logger.SilentLog("Building tests...")
|
||||
tests = self._GetTestsToRun()
|
||||
|
||||
# Build and install tests that do not get granted permissions
|
||||
self._DoPermissionAwareBuild(tests, False)
|
||||
|
||||
# Build and install tests that require granted permissions
|
||||
self._DoPermissionAwareBuild(tests, True)
|
||||
|
||||
def _DoPermissionAwareBuild(self, tests, test_requires_permissions):
|
||||
# turn off dalvik verifier if necessary
|
||||
# TODO: skip turning off verifier for now, since it puts device in bad
|
||||
# state b/14088982
|
||||
#self._TurnOffVerifier(tests)
|
||||
self._DoFullBuild(tests, test_requires_permissions)
|
||||
|
||||
target_tree = make_tree.MakeTree()
|
||||
|
||||
extra_args_set = []
|
||||
for test_suite in tests:
|
||||
if test_suite.IsGrantedPermissions() == test_requires_permissions:
|
||||
self._AddBuildTarget(test_suite, target_tree, extra_args_set)
|
||||
|
||||
if not self._options.preview:
|
||||
self._adb.EnableAdbRoot()
|
||||
else:
|
||||
logger.Log("adb root")
|
||||
|
||||
if not target_tree.IsEmpty():
|
||||
if self._options.coverage:
|
||||
coverage.EnableCoverageBuild()
|
||||
target_tree.AddPath("external/emma")
|
||||
|
||||
target_list = target_tree.GetPrunedMakeList()
|
||||
target_dir_list = [re.sub(r'Android[.]mk$', r'', i) for i in target_list]
|
||||
target_build_string = " ".join(target_list)
|
||||
target_dir_build_string = " ".join(target_dir_list)
|
||||
extra_args_string = " ".join(extra_args_set)
|
||||
|
||||
install_path_goals = []
|
||||
mmma_goals = []
|
||||
for d in target_dir_list:
|
||||
if d.startswith("./"):
|
||||
d = d[2:]
|
||||
if d.endswith("/"):
|
||||
d = d[:-1]
|
||||
install_path_goals.append("GET-INSTALL-PATH-IN-" + d.replace("/","-"))
|
||||
mmma_goals.append("MODULES-IN-" + d.replace("/","-"))
|
||||
# mmm cannot be used from python, so perform a similar operation using
|
||||
# ONE_SHOT_MAKEFILE
|
||||
cmd = 'ONE_SHOT_MAKEFILE="%s" make -j%s -C "%s" %s %s %s' % (
|
||||
target_build_string, self._options.make_jobs, self._root_path,
|
||||
" ".join(install_path_goals), " ".join(mmma_goals), extra_args_string)
|
||||
# mmma cannot be used from python, so perform a similar operation
|
||||
alt_cmd = 'make -j%s -C "%s" -f build/core/main.mk %s %s' % (
|
||||
self._options.make_jobs, self._root_path, extra_args_string, " ".join(mmma_goals))
|
||||
|
||||
logger.Log(cmd)
|
||||
if not self._options.preview:
|
||||
run_command.SetAbortOnError()
|
||||
try:
|
||||
output = run_command.RunCommand(cmd, return_output=True, timeout_time=600)
|
||||
## Chances are this failed because it didn't build the dependencies
|
||||
except errors.AbortError:
|
||||
logger.Log("make failed. Trying to rebuild all dependencies.")
|
||||
logger.Log("mmma -j%s %s" %(self._options.make_jobs, target_dir_build_string))
|
||||
# Try again with mma equivalent, which will build the dependencies
|
||||
run_command.RunCommand(alt_cmd, return_output=False, timeout_time=600)
|
||||
# Run mmm again to get the install paths only
|
||||
output = run_command.RunCommand(cmd, return_output=True, timeout_time=600)
|
||||
run_command.SetAbortOnError(False)
|
||||
logger.SilentLog(output)
|
||||
filter_re = re.compile(self._options.filter_re) if self._options.filter_re else None
|
||||
|
||||
self._DoInstall(output, test_requires_permissions, filter_re=filter_re)
|
||||
|
||||
def _DoInstall(self, make_output, test_requires_permissions, filter_re=None):
|
||||
"""Install artifacts from build onto device.
|
||||
|
||||
Looks for 'install:' text from make output to find artifacts to install.
|
||||
|
||||
Files with the .apk extension get 'adb install'ed, all other files
|
||||
get 'adb push'ed onto the device.
|
||||
|
||||
Args:
|
||||
make_output: stdout from make command
|
||||
"""
|
||||
for line in make_output.split("\n"):
|
||||
m = self._RE_MAKE_INSTALL.match(line)
|
||||
if m:
|
||||
# strip the 'INSTALL: <name>' from the left hand side
|
||||
# the remaining string is a space-separated list of build-generated files
|
||||
install_paths = m.group(2)
|
||||
for install_path in re.split(r'\s+', install_paths):
|
||||
if filter_re and not filter_re.match(install_path):
|
||||
continue
|
||||
if install_path.endswith(".apk"):
|
||||
abs_install_path = os.path.join(self._root_path, install_path)
|
||||
extra_flags = ""
|
||||
if test_requires_permissions and not self._options.skip_permissions:
|
||||
extra_flags = "-g"
|
||||
if self._options.user:
|
||||
extra_flags += " --user " + self._options.user
|
||||
logger.Log("adb install -r %s %s" % (extra_flags, abs_install_path))
|
||||
logger.Log(self._adb.Install(abs_install_path, extra_flags))
|
||||
else:
|
||||
self._PushInstallFileToDevice(install_path)
|
||||
|
||||
def _PushInstallFileToDevice(self, install_path):
|
||||
m = self._re_make_install_path.match(install_path)
|
||||
if m:
|
||||
remote_path = m.group(1)
|
||||
remote_dir = os.path.dirname(remote_path)
|
||||
logger.Log("adb shell mkdir -p %s" % remote_dir)
|
||||
self._adb.SendShellCommand("mkdir -p %s" % remote_dir)
|
||||
abs_install_path = os.path.join(self._root_path, install_path)
|
||||
logger.Log("adb push %s %s" % (abs_install_path, remote_path))
|
||||
self._adb.Push(abs_install_path, remote_path)
|
||||
else:
|
||||
logger.Log("Error: Failed to recognize path of file to install %s" % install_path)
|
||||
|
||||
def _DoFullBuild(self, tests, test_requires_permissions):
|
||||
"""If necessary, run a full 'make' command for the tests that need it."""
|
||||
extra_args_set = Set()
|
||||
|
||||
for test in tests:
|
||||
if test.IsFullMake() and test.IsGrantedPermissions() == test_requires_permissions:
|
||||
if test.GetExtraBuildArgs():
|
||||
# extra args contains the args to pass to 'make'
|
||||
extra_args_set.add(test.GetExtraBuildArgs())
|
||||
else:
|
||||
logger.Log("Warning: test %s needs a full build but does not specify"
|
||||
" extra_build_args" % test.GetName())
|
||||
|
||||
# check if there is actually any tests that required a full build
|
||||
if extra_args_set:
|
||||
cmd = ('make -j%s %s' % (self._options.make_jobs,
|
||||
' '.join(list(extra_args_set))))
|
||||
logger.Log(cmd)
|
||||
if not self._options.preview:
|
||||
old_dir = os.getcwd()
|
||||
os.chdir(self._root_path)
|
||||
output = run_command.RunCommand(cmd, return_output=True)
|
||||
logger.SilentLog(output)
|
||||
os.chdir(old_dir)
|
||||
self._DoInstall(output, test_requires_permissions)
|
||||
|
||||
def _AddBuildTarget(self, test_suite, target_tree, extra_args_set):
|
||||
if not test_suite.IsFullMake():
|
||||
build_dir = test_suite.GetBuildPath()
|
||||
if self._AddBuildTargetPath(build_dir, target_tree):
|
||||
extra_args_set.append(test_suite.GetExtraBuildArgs())
|
||||
for path in test_suite.GetBuildDependencies(self._options):
|
||||
self._AddBuildTargetPath(path, target_tree)
|
||||
|
||||
def _AddBuildTargetPath(self, build_dir, target_tree):
|
||||
if build_dir is not None:
|
||||
target_tree.AddPath(build_dir)
|
||||
return True
|
||||
return False
|
||||
|
||||
def _GetTestsToRun(self):
|
||||
"""Get a list of TestSuite objects to run, based on command line args."""
|
||||
if self._tests_to_run:
|
||||
return self._tests_to_run
|
||||
|
||||
self._tests_to_run = []
|
||||
if self._options.all_tests:
|
||||
self._tests_to_run = self._known_tests.GetTests()
|
||||
elif self._options.continuous_tests:
|
||||
self._tests_to_run = self._known_tests.GetContinuousTests()
|
||||
elif self._options.suite:
|
||||
self._tests_to_run = \
|
||||
self._known_tests.GetTestsInSuite(self._options.suite)
|
||||
elif self._options.test_path:
|
||||
walker = test_walker.TestWalker()
|
||||
self._tests_to_run = walker.FindTests(self._options.test_path)
|
||||
|
||||
for name in self._test_args:
|
||||
test = self._known_tests.GetTest(name)
|
||||
if test is None:
|
||||
logger.Log("Error: Could not find test %s" % name)
|
||||
self._DumpTests()
|
||||
raise errors.AbortError
|
||||
self._tests_to_run.append(test)
|
||||
return self._tests_to_run
|
||||
|
||||
def _TurnOffVerifier(self, test_list):
|
||||
"""Turn off the dalvik verifier if needed by given tests.
|
||||
|
||||
If one or more tests needs dalvik verifier off, and it is not already off,
|
||||
turns off verifier and reboots device to allow change to take effect.
|
||||
"""
|
||||
# hack to check if these are frameworks/base tests. If so, turn off verifier
|
||||
# to allow framework tests to access private/protected/package-private framework api
|
||||
framework_test = False
|
||||
for test in test_list:
|
||||
if os.path.commonprefix([test.GetBuildPath(), "frameworks/base"]):
|
||||
framework_test = True
|
||||
if framework_test:
|
||||
# check if verifier is off already - to avoid the reboot if not
|
||||
# necessary
|
||||
output = self._adb.SendShellCommand("cat /data/local.prop")
|
||||
if not self._DALVIK_VERIFIER_OFF_PROP in output:
|
||||
|
||||
# Read the existing dalvik verifier flags.
|
||||
old_prop_value = self._adb.SendShellCommand("getprop %s" \
|
||||
%(self._DALVIK_VERIFIER_PROP))
|
||||
old_prop_value = old_prop_value.strip() if old_prop_value else ""
|
||||
|
||||
# Append our verifier flags to existing flags
|
||||
new_prop_value = "%s %s" %(self._DALVIK_VERIFIER_OFF_VALUE, old_prop_value)
|
||||
|
||||
# Update property now, as /data/local.prop is not read until reboot
|
||||
logger.Log("adb shell setprop %s '%s'" \
|
||||
%(self._DALVIK_VERIFIER_PROP, new_prop_value))
|
||||
if not self._options.preview:
|
||||
self._adb.SendShellCommand("setprop %s '%s'" \
|
||||
%(self._DALVIK_VERIFIER_PROP, new_prop_value))
|
||||
|
||||
# Write prop to /data/local.prop
|
||||
# Every time device is booted, it will pick up this value
|
||||
new_prop_assignment = "%s = %s" %(self._DALVIK_VERIFIER_PROP, new_prop_value)
|
||||
if self._options.preview:
|
||||
logger.Log("adb shell \"echo %s >> /data/local.prop\""
|
||||
% new_prop_assignment)
|
||||
logger.Log("adb shell chmod 644 /data/local.prop")
|
||||
else:
|
||||
logger.Log("Turning off dalvik verifier and rebooting")
|
||||
self._adb.SendShellCommand("\"echo %s >> /data/local.prop\""
|
||||
% new_prop_assignment)
|
||||
|
||||
# Reset runtime so that dalvik picks up new verifier flags from prop
|
||||
self._ChmodRuntimeReset()
|
||||
elif not self._options.preview:
|
||||
# check the permissions on the file
|
||||
permout = self._adb.SendShellCommand("ls -l /data/local.prop")
|
||||
if not "-rw-r--r--" in permout:
|
||||
logger.Log("Fixing permissions on /data/local.prop and rebooting")
|
||||
self._ChmodRuntimeReset()
|
||||
|
||||
def _ChmodRuntimeReset(self):
|
||||
"""Perform a chmod of /data/local.prop and reset the runtime.
|
||||
"""
|
||||
logger.Log("adb shell chmod 644 /data/local.prop ## u+w,a+r")
|
||||
if not self._options.preview:
|
||||
self._adb.SendShellCommand("chmod 644 /data/local.prop")
|
||||
|
||||
self._adb.RuntimeReset(preview_only=self._options.preview)
|
||||
|
||||
if not self._options.preview:
|
||||
self._adb.EnableAdbRoot()
|
||||
|
||||
|
||||
def RunTests(self):
|
||||
"""Main entry method - executes the tests according to command line args."""
|
||||
try:
|
||||
run_command.SetAbortOnError()
|
||||
self._ProcessOptions()
|
||||
if self._options.only_list_tests:
|
||||
self._DumpTests()
|
||||
return
|
||||
|
||||
if not self._options.skip_build:
|
||||
self._DoBuild()
|
||||
|
||||
if self._options.build_install_only:
|
||||
logger.Log("Skipping test execution (due to --build-install-only flag)")
|
||||
return
|
||||
|
||||
for test_suite in self._GetTestsToRun():
|
||||
try:
|
||||
test_suite.Run(self._options, self._adb)
|
||||
except errors.WaitForResponseTimedOutError:
|
||||
logger.Log("Timed out waiting for response")
|
||||
|
||||
except KeyboardInterrupt:
|
||||
logger.Log("Exiting...")
|
||||
except errors.AbortError, error:
|
||||
logger.Log(error.msg)
|
||||
logger.SilentLog("Exiting due to AbortError...")
|
||||
except errors.WaitForResponseTimedOutError:
|
||||
logger.Log("Timed out waiting for response")
|
||||
|
||||
|
||||
def RunTests():
|
||||
runner = TestRunner()
|
||||
runner.RunTests()
|
||||
|
||||
if __name__ == "__main__":
|
||||
RunTests()
|
||||
@@ -1,484 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2009 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<!--
|
||||
This file contains standard test definitions for the Android platform
|
||||
|
||||
The following test types are supported:
|
||||
- On device Java instrumentation tests are defined by <test> tags.
|
||||
- native ones (C/C++) are defined by <test-native> tags.
|
||||
- host java tests are defined by <test-host> tags.
|
||||
|
||||
See test_defs.xsd for more information.
|
||||
-->
|
||||
|
||||
<test-definitions xmlns="http://schemas.android.com/testrunner/test_defs/1.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://schemas.android.com/testrunner/test_defs/1.0 test_defs.xsd">
|
||||
|
||||
<!-- frameworks tests -->
|
||||
<test name="frameworks-core"
|
||||
build_path="frameworks/base/core/tests/coretests"
|
||||
package="com.android.frameworks.coretests"
|
||||
runner="android.support.test.runner.AndroidJUnitRunner"
|
||||
coverage_target="framework"
|
||||
continuous="true" />
|
||||
|
||||
<test name="frameworks-net"
|
||||
build_path="frameworks/base/tests/net"
|
||||
package="com.android.frameworks.tests.net"
|
||||
runner="android.support.test.runner.AndroidJUnitRunner"
|
||||
coverage_target="framework"
|
||||
continuous = "true" />
|
||||
|
||||
<!-- will not run in the continuous test as it needs both Wifi & 3G -->
|
||||
<test name="frameworks-connectivity"
|
||||
build_path="frameworks/base/core/tests/ConnectivityManagerTest/"
|
||||
package="com.android.connectivitymanagertest"
|
||||
runner=".ConnectivityManagerUnitTestRunner"
|
||||
coverage_target="framework" />
|
||||
|
||||
<test name="frameworks-graphics"
|
||||
build_path="frameworks/base/graphics/tests/graphicstests"
|
||||
package="com.android.frameworks.graphicstests"
|
||||
coverage_target="framework"
|
||||
continuous="true" />
|
||||
|
||||
<test name="frameworks-location"
|
||||
build_path="frameworks/base/location/tests/locationtests"
|
||||
package="com.android.frameworks.locationtests"
|
||||
runner="android.support.test.runner.AndroidJUnitRunner"
|
||||
coverage_target="framework"
|
||||
continuous="true" />
|
||||
|
||||
<test name="frameworks-sax"
|
||||
build_path="frameworks/base/sax/tests/saxtests"
|
||||
package="com.android.frameworks.saxtests"
|
||||
coverage_target="framework"
|
||||
continuous="true" />
|
||||
|
||||
<test name="frameworks-services"
|
||||
build_path="frameworks/base/services/tests/servicestests"
|
||||
package="com.android.frameworks.servicestests"
|
||||
runner="android.support.test.runner.AndroidJUnitRunner"
|
||||
coverage_target="framework"
|
||||
continuous="true" />
|
||||
|
||||
<test name="frameworks-telephony"
|
||||
build_path="frameworks/opt/telephony/tests/telephonytests"
|
||||
package="com.android.frameworks.telephonytests"
|
||||
runner="android.support.test.runner.AndroidJUnitRunner"
|
||||
coverage_target="framework"
|
||||
continuous="true" />
|
||||
|
||||
<test name="frameworks-util"
|
||||
build_path="frameworks/base/core/tests/utiltests"
|
||||
package="com.android.frameworks.utiltests"
|
||||
runner="android.support.test.runner.AndroidJUnitRunner"
|
||||
continuous="true" />
|
||||
|
||||
<test name="frameworks-wifi"
|
||||
build_path="frameworks/opt/net/wifi/tests/wifitests"
|
||||
package="com.android.server.wifi.test"
|
||||
runner="android.support.test.runner.AndroidJUnitRunner"
|
||||
coverage_target="framework"
|
||||
continuous="true" />
|
||||
|
||||
<test name="frameworks-testrunner"
|
||||
build_path="frameworks/base/test-runner"
|
||||
package="com.android.frameworks.testrunner.tests"
|
||||
coverage_target="android.test.runner"
|
||||
continuous="true" />
|
||||
|
||||
<test name="frameworks-vpn"
|
||||
build_path="frameworks/base/vpn/tests/vpntests"
|
||||
package="com.android.frameworks.vpntests"
|
||||
coverage_target="framework"
|
||||
continuous="true" />
|
||||
|
||||
<test name="frameworks-support"
|
||||
build_path="frameworks/support/tests"
|
||||
package="android.support.tests"
|
||||
continuous="true" />
|
||||
|
||||
<test name="core"
|
||||
build_path="frameworks/base/tests/CoreTests"
|
||||
package="android.core"
|
||||
coverage_target="framework"
|
||||
continuous="true" />
|
||||
|
||||
<test name="keystore-unit"
|
||||
build_path="frameworks/base/keystore/tests"
|
||||
package="android.security.tests"
|
||||
coverage_target="framework"
|
||||
continuous="true" />
|
||||
|
||||
<test name="imf"
|
||||
build_path="frameworks/base/tests/ImfTest"
|
||||
package="com.android.imftest.tests"
|
||||
coverage_target="framework"
|
||||
continuous="true" />
|
||||
|
||||
<test name="framework-permission"
|
||||
build_path="frameworks/base/tests/permission"
|
||||
package="com.android.framework.permission.tests"
|
||||
runner="android.test.InstrumentationTestRunner"
|
||||
coverage_target="framework"
|
||||
continuous="true" />
|
||||
|
||||
<test name="android-common"
|
||||
build_path="frameworks/base/common/tests"
|
||||
package="com.android.common.tests"
|
||||
coverage_target="framework"
|
||||
continuous="true" />
|
||||
|
||||
<test name="android-common-ex"
|
||||
build_path="frameworks/ex/common/tests"
|
||||
package="com.android.common.tests"
|
||||
coverage_target="framework"
|
||||
continuous="true" />
|
||||
|
||||
<test name="ex-variablespeed"
|
||||
build_path="frameworks/ex/variablespeed/tests"
|
||||
package="com.android.ex.variablespeed.tests"
|
||||
coverage_target="framework"
|
||||
continuous="true"
|
||||
description="Framework variable speed audio tests" />
|
||||
|
||||
<test-native name="libandroidfw"
|
||||
build_path="frameworks/base/libs/androidfw/tests"
|
||||
description="Framework libandroidfw unit tests." />
|
||||
|
||||
<test-native name="libinput"
|
||||
build_path="frameworks/native/libs/input/tests"
|
||||
description="Framework libinput unit tests." />
|
||||
|
||||
<test-native name="libinputservice"
|
||||
build_path="frameworks/base/services/input/tests"
|
||||
description="Framework libinputservice unit tests." />
|
||||
|
||||
<test name="volley"
|
||||
build_path="frameworks/support/volley/tests"
|
||||
package="com.android.volley.tests"
|
||||
continuous="true" />
|
||||
|
||||
<test name="networksecurityconfig"
|
||||
build_path="frameworks/base/tests/NetworkSecurityConfigTest"
|
||||
package="android.security.net.config"
|
||||
coverage_target="framework"
|
||||
description="Android network security config tests." />
|
||||
|
||||
<!-- end of framework tests -->
|
||||
|
||||
<!-- media framework tests -->
|
||||
<test name="media"
|
||||
build_path="frameworks/base/media/tests/MediaFrameworkTest"
|
||||
package="com.android.mediaframeworktest"
|
||||
runner=".MediaFrameworkTestRunner"
|
||||
coverage_target="framework"
|
||||
continuous="true" />
|
||||
|
||||
<test name="mediaapitest"
|
||||
build_path="frameworks/base/media/tests/MediaFrameworkTest"
|
||||
package="com.android.mediaframeworktest"
|
||||
class="com.android.mediaframeworktest.functional.MediaPlayerApiTest"
|
||||
runner=".MediaFrameworkTestRunner"
|
||||
coverage_target="framework" />
|
||||
|
||||
<test name="mediarecordertest"
|
||||
build_path="frameworks/base/media/tests/MediaFrameworkTest"
|
||||
package="com.android.mediaframeworktest"
|
||||
class="com.android.mediaframeworktest.functional.MediaRecorderTest"
|
||||
runner=".MediaFrameworkTestRunner"
|
||||
coverage_target="framework" />
|
||||
|
||||
<test name="mediastresstest"
|
||||
build_path="frameworks/base/media/tests/MediaFrameworkTest"
|
||||
package="com.android.mediaframeworktest"
|
||||
runner=".MediaRecorderStressTestRunner"
|
||||
coverage_target="framework" />
|
||||
|
||||
<test name="mediamemorystress"
|
||||
build_path="frameworks/base/media/tests/MediaFrameworkTest"
|
||||
package="com.android.mediaframeworktest"
|
||||
runner=".MediaFrameworkPerfTestRunner"
|
||||
coverage_target="framework" />
|
||||
|
||||
<test name="mediaunit"
|
||||
build_path="frameworks/base/media/tests/MediaFrameworkTest"
|
||||
package="com.android.mediaframeworktest"
|
||||
runner=".MediaFrameworkUnitTestRunner"
|
||||
coverage_target="framework" />
|
||||
|
||||
<test name="mediaintegrationtest"
|
||||
build_path="frameworks/base/media/tests/MediaFrameworkTest"
|
||||
package="com.android.mediaframeworktest"
|
||||
runner=".MediaFrameworkIntegrationTestRunner"
|
||||
coverage_target="framework" />
|
||||
|
||||
<test-native name="camera-client-native"
|
||||
build_path="frameworks/av/camera/tests/"
|
||||
description="Camera client native tests." />
|
||||
|
||||
<test-native name="camera-hal2-native"
|
||||
build_path="hardware/libhardware/tests/camera2"
|
||||
description="Camera hal2 native tests." />
|
||||
<!-- end of media framework tests -->
|
||||
|
||||
<!-- targeted framework tests -->
|
||||
<test name="account"
|
||||
build_path="frameworks/base/core/tests/coretests"
|
||||
package="com.android.frameworks.coretests"
|
||||
class="android.accounts.AccountManagerServiceTest"
|
||||
coverage_target="framework" />
|
||||
|
||||
<test name="smoke"
|
||||
build_path="frameworks/base/tests/SmokeTest"
|
||||
package="com.android.smoketest.tests"
|
||||
coverage_target="framework"
|
||||
continuous="true" />
|
||||
|
||||
<test name="launchperf"
|
||||
build_path="development/apps/launchperf"
|
||||
package="com.android.launchperf"
|
||||
runner=".SimpleActivityLaunchPerformance"
|
||||
coverage_target="framework" />
|
||||
|
||||
<test name="contentprovideroperation"
|
||||
build_path="frameworks/base/core/tests/coretests"
|
||||
package="com.android.frameworks.coretests"
|
||||
class="android.content.ContentProviderOperationTest"
|
||||
coverage_target="framework" />
|
||||
|
||||
<!-- selected app tests -->
|
||||
<test name="apidemos"
|
||||
build_path="development/samples/ApiDemos"
|
||||
package="com.example.android.apis.tests" />
|
||||
|
||||
<test name="bluetooth"
|
||||
build_path="packages/apps/Bluetooth/tests/unit"
|
||||
package="com.android.bluetooth.tests"
|
||||
runner="android.support.test.runner.AndroidJUnitRunner"
|
||||
continuous="true" />
|
||||
|
||||
<test name="calculator"
|
||||
build_path="packages/apps/Calculator"
|
||||
package="com.android.calculator2.tests"
|
||||
coverage_target="Calculator"
|
||||
continuous="true" />
|
||||
|
||||
<test name="calendar"
|
||||
build_path="packages/apps/Calendar"
|
||||
package="com.android.calendar.tests"
|
||||
coverage_target="Calendar"
|
||||
continuous="true" />
|
||||
|
||||
<test name="calprov"
|
||||
build_path="packages/providers/CalendarProvider"
|
||||
package="com.android.providers.calendar.tests"
|
||||
coverage_target="CalendarProvider"
|
||||
continuous="true" />
|
||||
|
||||
<test name="camera-functional"
|
||||
build_path="packages/apps/Camera"
|
||||
package="com.google.android.camera.tests"
|
||||
runner="com.android.camera.CameraTestRunner"
|
||||
coverage_target="Camera"
|
||||
description="Camera functional test"
|
||||
continuous="true" />
|
||||
|
||||
<test name="contactsprov"
|
||||
build_path="packages/providers/ContactsProvider"
|
||||
package="com.android.providers.contacts.tests"
|
||||
coverage_target="ContactsProvider"
|
||||
continuous="true" />
|
||||
|
||||
<test name="contacts"
|
||||
build_path="packages/apps/Contacts"
|
||||
package="com.android.contacts.tests"
|
||||
runner="android.test.InstrumentationTestRunner"
|
||||
coverage_target="Contacts"
|
||||
description="Tests for the Contacts app."
|
||||
continuous="true" />
|
||||
|
||||
<test name="contacts-launch"
|
||||
build_path="packages/apps/Contacts"
|
||||
package="com.android.contacts.tests"
|
||||
runner="com.android.contacts.ContactsLaunchPerformance"
|
||||
description="Launch performance for Contacts." />
|
||||
|
||||
<test name="dialer"
|
||||
build_path="packages/apps/Dialer"
|
||||
package="com.android.dialer.tests"
|
||||
runner="android.test.InstrumentationTestRunner"
|
||||
coverage_target="Dialer"
|
||||
description="Tests for the Dialer app."
|
||||
continuous="true" />
|
||||
|
||||
<test name="managed-provisioning"
|
||||
build_path="packages/apps/ManagedProvisioning/tests"
|
||||
package="com.android.managedprovisioning.tests"
|
||||
runner="com.android.managedprovisioning.TestInstrumentationRunner"
|
||||
coverage_target="ManagedProvisioning"
|
||||
description="Tests for the ManagedProvisioning app."
|
||||
continuous="true" />
|
||||
|
||||
<test name="downloadprovider"
|
||||
build_path="packages/providers/DownloadProvider/tests"
|
||||
package="com.android.providers.downloads.tests"
|
||||
coverage_target="DownloadProvider"
|
||||
continuous="true" />
|
||||
|
||||
<test name="downloadprovider-permission"
|
||||
build_path="packages/providers/DownloadProvider/tests/permission"
|
||||
package="com.android.providers.downloads.permission.tests"
|
||||
coverage_target="DownloadProvider"
|
||||
continuous="true" />
|
||||
|
||||
<test name="email"
|
||||
build_path="packages/apps/Email"
|
||||
package="com.android.email.tests"
|
||||
coverage_target="Email"
|
||||
continuous="true" />
|
||||
|
||||
<test name="emailsmall"
|
||||
build_path="packages/apps/Email"
|
||||
package="com.android.email.tests"
|
||||
class="com.android.email.SmallTests"
|
||||
coverage_target="Email" />
|
||||
|
||||
<test name="exchange"
|
||||
build_path="packages/apps/Exchange"
|
||||
package="com.android.exchange.tests"
|
||||
coverage_target="Exchange"
|
||||
continuous="true" />
|
||||
|
||||
<test name="musicplayer"
|
||||
build_path="packages/apps/Music"
|
||||
package="com.android.music.tests"
|
||||
runner=".MusicPlayerFunctionalTestRunner"
|
||||
coverage_target="Music" />
|
||||
|
||||
<test name="mms"
|
||||
build_path="packages/apps/Mms"
|
||||
package="com.android.mms.tests"
|
||||
coverage_target="Mms" />
|
||||
|
||||
<!-- Unit tests for the phone application. -->
|
||||
<test name="phone-unit"
|
||||
build_path="packages/services/Telephony"
|
||||
package="com.android.phone.tests"
|
||||
continuous="true"
|
||||
coverage_target="Phone" />
|
||||
|
||||
<test name="carrierconfig-unit"
|
||||
build_path="packages/apps/CarrierConfig"
|
||||
package="com.android.carrierconfig.tests"
|
||||
continuous="true"
|
||||
coverage_target="Phone"
|
||||
description="Tests for default carrier config app" />
|
||||
|
||||
<test name="telecom-unit"
|
||||
build_path="packages/services/Telecomm"
|
||||
package="com.android.server.telecom.tests"
|
||||
continuous="true"
|
||||
coverage_target="Phone" />
|
||||
|
||||
<test name="quicksearchbox"
|
||||
build_path="packages/apps/QuickSearchBox"
|
||||
package="com.android.quicksearchbox.tests"
|
||||
coverage_target="QuickSearchBox" />
|
||||
|
||||
<test name="systemui"
|
||||
build_path="frameworks/base/packages/SystemUI/tests"
|
||||
package="com.android.systemui.tests"
|
||||
coverage_target="SystemUI"
|
||||
runner="android.testing.TestableInstrumentation"
|
||||
continuous="true"
|
||||
description="SystemUI tests" />
|
||||
|
||||
<test name="systemui-jank"
|
||||
build_path="platform_testing/tests/jank/UbSystemUiJankTests"
|
||||
package="android.platform.systemui.tests.jank"
|
||||
runner="android.support.test.runner.AndroidJUnitRunner "
|
||||
continuous="true"
|
||||
description="SystemUI jank tests" />
|
||||
|
||||
<test name="systemui-notification"
|
||||
build_path="frameworks/base/services/tests/uiservicestests"
|
||||
package="com.android.frameworks.tests.uiservices"
|
||||
runner="android.testing.TestableInstrumentation"
|
||||
continuous="true"
|
||||
description="SystemUI Services tests" />
|
||||
|
||||
<test name="systemui-functional-notification"
|
||||
build_path="platform_testing/tests/functional/notificationtests"
|
||||
package="com.android.notification.functional"
|
||||
runner="android.support.test.runner.AndroidJUnitRunner"
|
||||
continuous="true"
|
||||
description="SystemUI functional notification tests" />
|
||||
|
||||
<test name="apptransition-perf"
|
||||
build_path="platform_testing/tests/perf/PerfTransitionTest"
|
||||
package="com.android.apptransition.tests"
|
||||
runner="android.support.test.runner.AndroidJUnitRunner"
|
||||
continuous="true"
|
||||
description="App transition latency and other latency tests" />
|
||||
|
||||
<test name="documentsui"
|
||||
build_path="frameworks/base/packages/DocumentsUI/tests"
|
||||
package="com.android.documentsui.tests"
|
||||
coverage_target="DocumentsUI"
|
||||
continuous="true"
|
||||
description="DocumentsUI tests" />
|
||||
|
||||
<!-- native tests -->
|
||||
|
||||
<!-- Bionic C++ -->
|
||||
<test-native name="libstdcpp"
|
||||
build_path="system/extras/tests/bionic/libstdc++"
|
||||
description="Bionic libstdc++."
|
||||
extra_build_args="BIONIC_TESTS=1" />
|
||||
|
||||
<test-native name="libskia"
|
||||
build_path="external/skia/tests"
|
||||
description="Skia tests." />
|
||||
|
||||
<!-- Google Test -->
|
||||
<test-native name="gtest"
|
||||
build_path="external/gtest"
|
||||
description="Google test."
|
||||
extra_build_args="GTEST_TESTS=1" />
|
||||
|
||||
<!-- clatd -->
|
||||
<test-native name="clatd"
|
||||
build_path="external/android-clat"
|
||||
description="clatd unit tests." />
|
||||
|
||||
<!-- Libjingle -->
|
||||
<test-native name="libjingle"
|
||||
build_path="vendor/google/libraries/libjingle"
|
||||
description="Libjingle."
|
||||
full_make="true"
|
||||
extra_build_args="LIBJINGLE_TESTS=1" />
|
||||
|
||||
<!-- host java tests -->
|
||||
<test-host name="frameworks-core-host"
|
||||
build_path="frameworks/base/core/tests/hosttests"
|
||||
class="android.content.pm.PackageManagerHostTests"
|
||||
jar_name="FrameworkCoreHostTests.jar" />
|
||||
|
||||
</test-definitions>
|
||||
@@ -1,141 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Copyright (C) 2009 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<!-- Contains the schema definition for Android test definitions xml -->
|
||||
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
targetNamespace="http://schemas.android.com/testrunner/test_defs/1.0"
|
||||
xmlns="http://schemas.android.com/testrunner/test_defs/1.0"
|
||||
elementFormDefault="qualified">
|
||||
|
||||
<xs:element name="test-definitions">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element name="test" type="javaTestType" />
|
||||
<xs:element name="test-native" type="nativeTestType" />
|
||||
<xs:element name="test-host" type="hostTestType" />
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<!-- Generic, abstract test definition. Contains attributes common to all
|
||||
test types. -->
|
||||
<xs:complexType name="testType">
|
||||
|
||||
<!-- Self-descriptive name used to uniquely identify the test. -->
|
||||
<xs:attribute name="name" type="xs:string" use="required" />
|
||||
|
||||
<!-- File system path, relative to Android build root, to this
|
||||
package's Android.mk file.-->
|
||||
<xs:attribute name="build_path" type="xs:string" use="required" />
|
||||
|
||||
<!-- Include test in continuous test system. -->
|
||||
<xs:attribute name="continuous" type="xs:boolean" use="optional"
|
||||
default="false" />
|
||||
|
||||
<!-- Include test as part of named suite. -->
|
||||
<xs:attribute name="suite" type="xs:string" use="optional" />
|
||||
|
||||
<!-- Short description (typically less than 60 characters) about this
|
||||
test. -->
|
||||
<xs:attribute name="description" type="xs:string" use="optional" />
|
||||
|
||||
<!-- Specifies that a full 'make', as opposed to 'mmm' command, is
|
||||
needed to build this test. The build command used will be
|
||||
'make extra_build_args' -->
|
||||
<xs:attribute name="full_make" type="xs:boolean"
|
||||
use="optional" />
|
||||
|
||||
<!-- Extra arguments to append to build command when building this
|
||||
test. -->
|
||||
<xs:attribute name="extra_build_args" type="xs:string"
|
||||
use="optional" />
|
||||
|
||||
</xs:complexType>
|
||||
|
||||
<!-- Java on device instrumentation test.
|
||||
|
||||
The test attributes map to the following commands:
|
||||
(if class is defined)
|
||||
adb shell am instrument -w <package>/<runner>
|
||||
(else)
|
||||
adb shell am instrument -w -e class <class> <package>/<runner>
|
||||
-->
|
||||
<xs:complexType name="javaTestType">
|
||||
<xs:complexContent>
|
||||
<xs:extension base="testType">
|
||||
|
||||
<!-- Android application package that contains the tests. -->
|
||||
<xs:attribute name="package" type="xs:string" use="required" />
|
||||
|
||||
<!-- Fully qualified Java test class to run. -->
|
||||
<xs:attribute name="class" type="xs:string" use="optional" />
|
||||
|
||||
<!-- Fully qualified InstrumentationTestRunner to execute. -->
|
||||
<xs:attribute name="runner" type="xs:string" use="optional"
|
||||
default="android.test.InstrumentationTestRunner" />
|
||||
|
||||
<!-- Build name of Android package this test targets. These
|
||||
targets are defined in the coverage_targets.xml file. Used as
|
||||
basis for code coverage metrics. If omitted, code coverage will
|
||||
not be supported for this test. -->
|
||||
<xs:attribute name="coverage_target" type="xs:string"
|
||||
use="optional" />
|
||||
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
|
||||
<!-- Native (C/C++) on device tests.
|
||||
|
||||
The native test attributes map to the following commands:
|
||||
make <build_path>/Android.mk <extra_build_args>
|
||||
adb sync
|
||||
for test_prog in <tests built>; do
|
||||
adb shell "/system/bin/${test_prog} >/dev/null 2>&1;echo \$?"
|
||||
adb shell "rm /system/bin/${test_prog}"
|
||||
done
|
||||
-->
|
||||
<xs:complexType name="nativeTestType">
|
||||
<xs:complexContent>
|
||||
<xs:extension base="testType" />
|
||||
<!-- no additional attributes -->
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
|
||||
<!-- Host java tests.
|
||||
|
||||
Uses hosttestlib to execute tests on host. Maps to following command:
|
||||
java -cp <libs>:jar_name com.android.hosttest.DeviceTestRunner \
|
||||
<class> -s <device serial> -p <app build path>
|
||||
-->
|
||||
<xs:complexType name="hostTestType">
|
||||
<xs:complexContent>
|
||||
<xs:extension base="testType">
|
||||
|
||||
<!-- The test class to run. Must extend DeviceTestSuite, and
|
||||
implement a public static suite() method that returns a Test to
|
||||
run. -->
|
||||
<xs:attribute name="class" type="xs:string" use="required" />
|
||||
|
||||
<!-- built jar name of host library that includes the tests. -->
|
||||
<xs:attribute name="jar_name" type="xs:string" use="required" />
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
</xs:schema>
|
||||
@@ -1 +0,0 @@
|
||||
__all__ = ['test_defs', 'test_walker']
|
||||
@@ -1,134 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
#
|
||||
# Copyright 2011, The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""TestSuite for running C/C++ Android tests using gtest framework."""
|
||||
|
||||
# python imports
|
||||
import os
|
||||
import re
|
||||
|
||||
# local imports
|
||||
import logger
|
||||
import run_command
|
||||
import test_suite
|
||||
|
||||
|
||||
class GTestSuite(test_suite.AbstractTestSuite):
|
||||
"""A test suite for running gtest on device."""
|
||||
|
||||
def __init__(self):
|
||||
test_suite.AbstractTestSuite.__init__(self)
|
||||
self._target_exec_path = None
|
||||
|
||||
def GetTargetExecPath(self):
|
||||
"""Get the target path to gtest executable."""
|
||||
return self._target_exec_path
|
||||
|
||||
def SetTargetExecPath(self, path):
|
||||
self._target_exec_path = path
|
||||
return self
|
||||
|
||||
def Run(self, options, adb):
|
||||
"""Run the provided gtest test suite.
|
||||
|
||||
Args:
|
||||
options: command line options
|
||||
adb: adb interface
|
||||
"""
|
||||
|
||||
test_class = "*"
|
||||
test_method = "*"
|
||||
if options.test_class is not None:
|
||||
test_class = options.test_class.lstrip()
|
||||
if options.test_method is not None:
|
||||
test_method = options.test_method.lstrip()
|
||||
filter_arg = ""
|
||||
if test_class != "*" or test_method != "*":
|
||||
filter_arg = "--gtest_filter=%s.%s" % (test_class, test_method)
|
||||
|
||||
shell_cmd = adb.PreviewShellCommand(
|
||||
" ".join((self.GetTargetExecPath(), filter_arg)))
|
||||
logger.Log(shell_cmd)
|
||||
if not options.preview:
|
||||
# gtest will log to test results to stdout, so no need to do any
|
||||
# extra processing
|
||||
run_command.RunCommand(shell_cmd, return_output=False)
|
||||
|
||||
|
||||
class GTestFactory(test_suite.AbstractTestFactory):
|
||||
|
||||
def __init__(self, test_root_path, build_path):
|
||||
test_suite.AbstractTestFactory.__init__(self, test_root_path,
|
||||
build_path)
|
||||
|
||||
def CreateTests(self, sub_tests_path=None):
|
||||
"""Create tests found in sub_tests_path.
|
||||
|
||||
Looks for test files matching a pattern, and assumes each one is a separate
|
||||
binary on target.
|
||||
|
||||
Test files must match one of the following pattern:
|
||||
- test_*.[c|cc|cpp]
|
||||
- *_test.[c|cc|cpp]
|
||||
- *_unittest.[c|cc|cpp]
|
||||
- *Tests.[cc|cpp]
|
||||
|
||||
"""
|
||||
if not sub_tests_path:
|
||||
sub_tests_path = self.GetTestRootPath()
|
||||
test_file_list = []
|
||||
if os.path.isfile(sub_tests_path):
|
||||
self._EvaluateFile(test_file_list, os.path.basename(sub_tests_path))
|
||||
else:
|
||||
os.path.walk(sub_tests_path, self._CollectTestSources, test_file_list)
|
||||
# TODO: obtain this from makefile instead of hardcoding
|
||||
target_root_path = os.path.join('/data', 'nativetest')
|
||||
test_suites = []
|
||||
for test_file in test_file_list:
|
||||
logger.SilentLog('Creating gtest suite for file %s' % test_file)
|
||||
suite = GTestSuite()
|
||||
suite.SetBuildPath(self.GetBuildPath())
|
||||
# expect tests in /data/nativetest/test_file/test_file
|
||||
suite.SetTargetExecPath(os.path.join(target_root_path, test_file, test_file))
|
||||
test_suites.append(suite)
|
||||
return test_suites
|
||||
|
||||
def _CollectTestSources(self, test_list, dirname, files):
|
||||
"""For each directory, find tests source file and add them to the list.
|
||||
|
||||
Test files must match one of the following pattern:
|
||||
- test_*.[cc|cpp]
|
||||
- *_test.[cc|cpp]
|
||||
- *_unittest.[cc|cpp]
|
||||
- *Tests.[cc|cpp]
|
||||
|
||||
This method is a callback for os.path.walk.
|
||||
|
||||
Args:
|
||||
test_list: Where new tests should be inserted.
|
||||
dirname: Current directory.
|
||||
files: List of files in the current directory.
|
||||
"""
|
||||
for f in files:
|
||||
self._EvaluateFile(test_list, f)
|
||||
|
||||
def _EvaluateFile(self, test_list, file):
|
||||
(name, ext) = os.path.splitext(file)
|
||||
if ext == ".cc" or ext == ".cpp" or ext == ".c":
|
||||
if re.search("_test$|_test_$|_unittest$|_unittest_$|^test_|Tests$", name):
|
||||
logger.SilentLog("Found native test file %s" % file)
|
||||
test_list.append(name)
|
||||
@@ -1,108 +0,0 @@
|
||||
#!/usr/bin/python2.4
|
||||
#
|
||||
#
|
||||
# Copyright 2009, The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Parser for test definition xml files."""
|
||||
|
||||
# python imports
|
||||
import os
|
||||
|
||||
import errors
|
||||
import logger
|
||||
import run_command
|
||||
import test_suite
|
||||
|
||||
|
||||
class HostTestSuite(test_suite.AbstractTestSuite):
|
||||
"""A test suite for running hosttestlib java tests."""
|
||||
|
||||
_JUNIT_JAR_NAME = "junit.jar"
|
||||
_HOSTTESTLIB_NAME = "hosttestlib.jar"
|
||||
_DDMLIB_NAME = "ddmlib-prebuilt.jar"
|
||||
_TRADEFED_NAME = "tradefed-prebuilt.jar"
|
||||
_lib_names = [_JUNIT_JAR_NAME, _HOSTTESTLIB_NAME, _DDMLIB_NAME, _TRADEFED_NAME]
|
||||
|
||||
_JUNIT_BUILD_PATH = os.path.join("external", "junit")
|
||||
_HOSTTESTLIB_BUILD_PATH = os.path.join("development", "tools", "hosttestlib")
|
||||
_LIB_BUILD_PATHS = [_JUNIT_BUILD_PATH, _HOSTTESTLIB_BUILD_PATH ]
|
||||
|
||||
# main class for running host tests
|
||||
# TODO: should other runners be supported, and make runner an attribute of
|
||||
# the test suite?
|
||||
_TEST_RUNNER = "com.android.hosttest.DeviceTestRunner"
|
||||
|
||||
def __init__(self):
|
||||
test_suite.AbstractTestSuite.__init__(self)
|
||||
self._jar_name = None
|
||||
self._class_name = None
|
||||
|
||||
def GetBuildDependencies(self, options):
|
||||
"""Override parent to tag on building host libs."""
|
||||
return self._LIB_BUILD_PATHS
|
||||
|
||||
def GetClassName(self):
|
||||
return self._class_name
|
||||
|
||||
def SetClassName(self, class_name):
|
||||
self._class_name = class_name
|
||||
return self
|
||||
|
||||
def GetJarName(self):
|
||||
"""Returns the name of the host jar that contains the tests."""
|
||||
return self._jar_name
|
||||
|
||||
def SetJarName(self, jar_name):
|
||||
self._jar_name = jar_name
|
||||
return self
|
||||
|
||||
def Run(self, options, adb_interface):
|
||||
"""Runs the host test.
|
||||
|
||||
Results will be displayed on stdout. Assumes 'java' is on system path.
|
||||
|
||||
Args:
|
||||
options: command line options for running host tests. Expected member
|
||||
fields:
|
||||
host_lib_path: path to directory that contains host library files
|
||||
test_data_path: path to directory that contains test data files
|
||||
preview: if true, do not execute, display commands only
|
||||
adb_interface: reference to device under test
|
||||
|
||||
Raises:
|
||||
errors.AbortError: if fatal error occurs
|
||||
"""
|
||||
# get the serial number of the device under test, so it can be passed to
|
||||
# hosttestlib.
|
||||
serial_number = adb_interface.GetSerialNumber()
|
||||
self._lib_names.append(self.GetJarName())
|
||||
# gather all the host jars that are needed to run tests
|
||||
full_lib_paths = []
|
||||
for lib in self._lib_names:
|
||||
path = os.path.join(options.host_lib_path, lib)
|
||||
# make sure jar file exists on host
|
||||
if not os.path.exists(path):
|
||||
raise errors.AbortError(msg="Could not find jar %s" % path)
|
||||
full_lib_paths.append(path)
|
||||
|
||||
# java -cp <libs> <runner class> <test suite class> -s <device serial>
|
||||
# -p <test data path>
|
||||
cmd = "java -cp %s %s %s -s %s -p %s" % (":".join(full_lib_paths),
|
||||
self._TEST_RUNNER,
|
||||
self.GetClassName(), serial_number,
|
||||
options.test_data_path)
|
||||
logger.Log(cmd)
|
||||
if not options.preview:
|
||||
run_command.RunOnce(cmd, return_output=False)
|
||||
@@ -1,362 +0,0 @@
|
||||
#!/usr/bin/python2.4
|
||||
#
|
||||
#
|
||||
# Copyright 2008, The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""TestSuite definition for Android instrumentation tests."""
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
# local imports
|
||||
import android_manifest
|
||||
from coverage import coverage
|
||||
import errors
|
||||
import logger
|
||||
import test_suite
|
||||
|
||||
|
||||
class InstrumentationTestSuite(test_suite.AbstractTestSuite):
|
||||
"""Represents a java instrumentation test suite definition run on device."""
|
||||
|
||||
DEFAULT_RUNNER = "android.test.InstrumentationTestRunner"
|
||||
|
||||
def __init__(self):
|
||||
test_suite.AbstractTestSuite.__init__(self)
|
||||
self._package_name = None
|
||||
self._runner_name = self.DEFAULT_RUNNER
|
||||
self._class_name = None
|
||||
self._target_name = None
|
||||
self._java_package = None
|
||||
|
||||
def GetPackageName(self):
|
||||
return self._package_name
|
||||
|
||||
def SetPackageName(self, package_name):
|
||||
self._package_name = package_name
|
||||
return self
|
||||
|
||||
def GetRunnerName(self):
|
||||
return self._runner_name
|
||||
|
||||
def SetRunnerName(self, runner_name):
|
||||
self._runner_name = runner_name
|
||||
return self
|
||||
|
||||
def GetClassName(self):
|
||||
return self._class_name
|
||||
|
||||
def SetClassName(self, class_name):
|
||||
self._class_name = class_name
|
||||
return self
|
||||
|
||||
def GetJavaPackageFilter(self):
|
||||
return self._java_package
|
||||
|
||||
def SetJavaPackageFilter(self, java_package_name):
|
||||
"""Configure the suite to only run tests in given java package."""
|
||||
self._java_package = java_package_name
|
||||
return self
|
||||
|
||||
def GetTargetName(self):
|
||||
"""Retrieve module that this test is targeting.
|
||||
|
||||
Used for generating code coverage metrics.
|
||||
Returns:
|
||||
the module target name
|
||||
"""
|
||||
return self._target_name
|
||||
|
||||
def SetTargetName(self, target_name):
|
||||
self._target_name = target_name
|
||||
return self
|
||||
|
||||
def GetBuildDependencies(self, options):
|
||||
if options.coverage_target_path:
|
||||
return [options.coverage_target_path]
|
||||
return []
|
||||
|
||||
def Run(self, options, adb):
|
||||
"""Run the provided test suite.
|
||||
|
||||
Builds up an adb instrument command using provided input arguments.
|
||||
|
||||
Args:
|
||||
options: command line options to provide to test run
|
||||
adb: adb_interface to device under test
|
||||
|
||||
Raises:
|
||||
errors.AbortError: if fatal error occurs
|
||||
"""
|
||||
|
||||
test_class = self.GetClassName()
|
||||
if options.test_class is not None:
|
||||
test_class = options.test_class.lstrip()
|
||||
if test_class.startswith("."):
|
||||
test_class = self.GetPackageName() + test_class
|
||||
if options.test_method is not None:
|
||||
test_class = "%s#%s" % (test_class, options.test_method)
|
||||
|
||||
test_package = self.GetJavaPackageFilter()
|
||||
if options.test_package:
|
||||
test_package = options.test_package
|
||||
|
||||
if test_class and test_package:
|
||||
logger.Log('Error: both class and java package options are specified')
|
||||
|
||||
instrumentation_args = {}
|
||||
if test_class is not None:
|
||||
instrumentation_args["class"] = test_class
|
||||
if test_package:
|
||||
instrumentation_args["package"] = test_package
|
||||
if options.test_size:
|
||||
instrumentation_args["size"] = options.test_size
|
||||
if options.wait_for_debugger:
|
||||
instrumentation_args["debug"] = "true"
|
||||
if options.suite_assign_mode:
|
||||
instrumentation_args["suiteAssignment"] = "true"
|
||||
if options.coverage:
|
||||
instrumentation_args["coverage"] = "true"
|
||||
if options.test_annotation:
|
||||
instrumentation_args["annotation"] = options.test_annotation
|
||||
if options.test_not_annotation:
|
||||
instrumentation_args["notAnnotation"] = options.test_not_annotation
|
||||
if options.preview:
|
||||
adb_cmd = adb.PreviewInstrumentationCommand(
|
||||
package_name=self.GetPackageName(),
|
||||
runner_name=self.GetRunnerName(),
|
||||
raw_mode=options.raw_mode,
|
||||
instrumentation_args=instrumentation_args)
|
||||
logger.Log(adb_cmd)
|
||||
elif options.coverage:
|
||||
coverage_gen = coverage.CoverageGenerator(adb)
|
||||
if options.coverage_target_path:
|
||||
coverage_target = coverage_gen.GetCoverageTargetForPath(options.coverage_target_path)
|
||||
elif self.GetTargetName():
|
||||
coverage_target = coverage_gen.GetCoverageTarget(self.GetTargetName())
|
||||
self._CheckInstrumentationInstalled(adb)
|
||||
# need to parse test output to determine path to coverage file
|
||||
logger.Log("Running in coverage mode, suppressing test output")
|
||||
try:
|
||||
(test_results, status_map) = adb.StartInstrumentationForPackage(
|
||||
package_name=self.GetPackageName(),
|
||||
runner_name=self.GetRunnerName(),
|
||||
timeout_time=60*60,
|
||||
instrumentation_args=instrumentation_args,
|
||||
user=options.user,
|
||||
no_hidden_api_checks=options.no_hidden_api_checks)
|
||||
except errors.InstrumentationError, errors.DeviceUnresponsiveError:
|
||||
return
|
||||
self._PrintTestResults(test_results)
|
||||
device_coverage_path = status_map.get("coverageFilePath", None)
|
||||
if device_coverage_path is None:
|
||||
logger.Log("Error: could not find coverage data on device")
|
||||
return
|
||||
|
||||
coverage_file = coverage_gen.ExtractReport(
|
||||
self.GetName(), coverage_target, device_coverage_path,
|
||||
test_qualifier=options.test_size)
|
||||
if coverage_file is not None:
|
||||
logger.Log("Coverage report generated at %s" % coverage_file)
|
||||
|
||||
else:
|
||||
self._CheckInstrumentationInstalled(adb)
|
||||
adb.StartInstrumentationNoResults(
|
||||
package_name=self.GetPackageName(),
|
||||
runner_name=self.GetRunnerName(),
|
||||
raw_mode=options.raw_mode,
|
||||
instrumentation_args=instrumentation_args,
|
||||
user=options.user,
|
||||
no_hidden_api_checks=options.no_hidden_api_checks)
|
||||
|
||||
def _CheckInstrumentationInstalled(self, adb):
|
||||
if not adb.IsInstrumentationInstalled(self.GetPackageName(),
|
||||
self.GetRunnerName()):
|
||||
msg=("Could not find instrumentation %s/%s on device. Try forcing a "
|
||||
"rebuild by updating a source file, and re-executing runtest." %
|
||||
(self.GetPackageName(), self.GetRunnerName()))
|
||||
raise errors.AbortError(msg=msg)
|
||||
|
||||
def _PrintTestResults(self, test_results):
|
||||
"""Prints a summary of test result data to stdout.
|
||||
|
||||
Args:
|
||||
test_results: a list of am_instrument_parser.TestResult
|
||||
"""
|
||||
total_count = 0
|
||||
error_count = 0
|
||||
fail_count = 0
|
||||
for test_result in test_results:
|
||||
if test_result.GetStatusCode() == -1: # error
|
||||
logger.Log("Error in %s: %s" % (test_result.GetTestName(),
|
||||
test_result.GetFailureReason()))
|
||||
error_count+=1
|
||||
elif test_result.GetStatusCode() == -2: # failure
|
||||
logger.Log("Failure in %s: %s" % (test_result.GetTestName(),
|
||||
test_result.GetFailureReason()))
|
||||
fail_count+=1
|
||||
total_count+=1
|
||||
logger.Log("Tests run: %d, Failures: %d, Errors: %d" %
|
||||
(total_count, fail_count, error_count))
|
||||
|
||||
def HasInstrumentationTest(path):
|
||||
"""Determine if given path defines an instrumentation test.
|
||||
|
||||
Args:
|
||||
path: file system path to instrumentation test.
|
||||
"""
|
||||
manifest_parser = android_manifest.CreateAndroidManifest(path)
|
||||
if manifest_parser:
|
||||
return manifest_parser.GetInstrumentationNames()
|
||||
return False
|
||||
|
||||
class InstrumentationTestFactory(test_suite.AbstractTestFactory):
|
||||
"""A factory for creating InstrumentationTestSuites"""
|
||||
|
||||
def __init__(self, test_root_path, build_path):
|
||||
test_suite.AbstractTestFactory.__init__(self, test_root_path,
|
||||
build_path)
|
||||
|
||||
def CreateTests(self, sub_tests_path=None):
|
||||
"""Create tests found in test_path.
|
||||
|
||||
Will create a single InstrumentationTestSuite based on info found in
|
||||
AndroidManifest.xml found at build_path. Will set additional filters if
|
||||
test_path refers to a java package or java class.
|
||||
"""
|
||||
tests = []
|
||||
class_name_arg = None
|
||||
java_package_name = None
|
||||
if sub_tests_path:
|
||||
# if path is java file, populate class name
|
||||
if self._IsJavaFile(sub_tests_path):
|
||||
class_name_arg = self._GetClassNameFromFile(sub_tests_path)
|
||||
logger.SilentLog('Using java test class %s' % class_name_arg)
|
||||
elif self._IsJavaPackage(sub_tests_path):
|
||||
java_package_name = self._GetPackageNameFromDir(sub_tests_path)
|
||||
logger.SilentLog('Using java package %s' % java_package_name)
|
||||
try:
|
||||
manifest_parser = android_manifest.AndroidManifest(app_path=
|
||||
self.GetTestsRootPath())
|
||||
instrs = manifest_parser.GetInstrumentationNames()
|
||||
if not instrs:
|
||||
logger.Log('Could not find instrumentation declarations in %s at %s' %
|
||||
(android_manifest.AndroidManifest.FILENAME,
|
||||
self.GetBuildPath()))
|
||||
return tests
|
||||
elif len(instrs) > 1:
|
||||
logger.Log("Found multiple instrumentation declarations in %s/%s. "
|
||||
"Only using first declared." %
|
||||
(self.GetBuildPath(),
|
||||
android_manifest.AndroidManifest.FILENAME))
|
||||
instr_name = manifest_parser.GetInstrumentationNames()[0]
|
||||
# escape inner class names
|
||||
instr_name = instr_name.replace('$', '\$')
|
||||
pkg_name = manifest_parser.GetPackageName()
|
||||
if instr_name.find(".") < 0:
|
||||
instr_name = "." + instr_name
|
||||
logger.SilentLog('Found instrumentation %s/%s' % (pkg_name, instr_name))
|
||||
suite = InstrumentationTestSuite()
|
||||
suite.SetPackageName(pkg_name)
|
||||
suite.SetBuildPath(self.GetBuildPath())
|
||||
suite.SetRunnerName(instr_name)
|
||||
suite.SetName(pkg_name)
|
||||
suite.SetClassName(class_name_arg)
|
||||
suite.SetJavaPackageFilter(java_package_name)
|
||||
tests.append(suite)
|
||||
return tests
|
||||
|
||||
except:
|
||||
logger.Log('Could not find or parse %s at %s' %
|
||||
(android_manifest.AndroidManifest.FILENAME,
|
||||
self.GetBuildPath()))
|
||||
return tests
|
||||
|
||||
def _IsJavaFile(self, path):
|
||||
"""Returns true if given file system path is a java file."""
|
||||
return os.path.isfile(path) and self._IsJavaFileName(path)
|
||||
|
||||
def _IsJavaFileName(self, filename):
|
||||
"""Returns true if given file name is a java file name."""
|
||||
return os.path.splitext(filename)[1] == '.java'
|
||||
|
||||
def _IsJavaPackage(self, path):
|
||||
"""Returns true if given file path is a java package.
|
||||
|
||||
Currently assumes if any java file exists in this directory, than it
|
||||
represents a java package.
|
||||
|
||||
Args:
|
||||
path: file system path of directory to check
|
||||
|
||||
Returns:
|
||||
True if path is a java package
|
||||
"""
|
||||
if not os.path.isdir(path):
|
||||
return False
|
||||
for file_name in os.listdir(path):
|
||||
if self._IsJavaFileName(file_name):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _GetClassNameFromFile(self, java_file_path):
|
||||
"""Gets the fully qualified java class name from path.
|
||||
|
||||
Args:
|
||||
java_file_path: file system path of java file
|
||||
|
||||
Returns:
|
||||
fully qualified java class name or None.
|
||||
"""
|
||||
package_name = self._GetPackageNameFromFile(java_file_path)
|
||||
if package_name:
|
||||
filename = os.path.basename(java_file_path)
|
||||
class_name = os.path.splitext(filename)[0]
|
||||
return '%s.%s' % (package_name, class_name)
|
||||
return None
|
||||
|
||||
def _GetPackageNameFromDir(self, path):
|
||||
"""Gets the java package name associated with given directory path.
|
||||
|
||||
Caveat: currently just parses defined java package name from first java
|
||||
file found in directory.
|
||||
|
||||
Args:
|
||||
path: file system path of directory
|
||||
|
||||
Returns:
|
||||
the java package name or None
|
||||
"""
|
||||
for filename in os.listdir(path):
|
||||
if self._IsJavaFileName(filename):
|
||||
return self._GetPackageNameFromFile(os.path.join(path, filename))
|
||||
|
||||
def _GetPackageNameFromFile(self, java_file_path):
|
||||
"""Gets the java package name associated with given java file path.
|
||||
|
||||
Args:
|
||||
java_file_path: file system path of java file
|
||||
|
||||
Returns:
|
||||
the java package name or None
|
||||
"""
|
||||
logger.SilentLog('Looking for java package name in %s' % java_file_path)
|
||||
re_package = re.compile(r'package\s+(.*);')
|
||||
file_handle = open(java_file_path, 'r')
|
||||
for line in file_handle:
|
||||
match = re_package.match(line)
|
||||
if match:
|
||||
return match.group(1)
|
||||
return None
|
||||
@@ -1,185 +0,0 @@
|
||||
#!/usr/bin/python2.4
|
||||
#
|
||||
#
|
||||
# Copyright 2009, The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""TestSuite for running native Android tests."""
|
||||
|
||||
# python imports
|
||||
import os
|
||||
import re
|
||||
|
||||
# local imports
|
||||
import android_build
|
||||
import logger
|
||||
import run_command
|
||||
import test_suite
|
||||
|
||||
|
||||
class NativeTestSuite(test_suite.AbstractTestSuite):
|
||||
"""A test suite for running native aka C/C++ tests on device."""
|
||||
|
||||
def Run(self, options, adb):
|
||||
"""Run the provided *native* test suite.
|
||||
|
||||
The test_suite must contain a build path where the native test
|
||||
files are. Subdirectories are automatically scanned as well.
|
||||
|
||||
Each test's name must have a .cc or .cpp extension and match one
|
||||
of the following patterns:
|
||||
- test_*
|
||||
- *_test.[cc|cpp]
|
||||
- *_unittest.[cc|cpp]
|
||||
A successful test must return 0. Any other value will be considered
|
||||
as an error.
|
||||
|
||||
Args:
|
||||
options: command line options
|
||||
adb: adb interface
|
||||
"""
|
||||
# find all test files, convert unicode names to ascii, take the basename
|
||||
# and drop the .cc/.cpp extension.
|
||||
source_list = []
|
||||
build_path = os.path.join(android_build.GetTop(), self.GetBuildPath())
|
||||
os.path.walk(build_path, self._CollectTestSources, source_list)
|
||||
logger.SilentLog("Tests source %s" % source_list)
|
||||
|
||||
# Host tests are under out/host/<os>-<arch>/bin.
|
||||
host_list = self._FilterOutMissing(android_build.GetHostBin(), source_list)
|
||||
logger.SilentLog("Host tests %s" % host_list)
|
||||
|
||||
# Target tests are under $ANDROID_PRODUCT_OUT/data/nativetest.
|
||||
target_list = self._FilterOutMissing(android_build.GetTargetNativeTestPath(),
|
||||
source_list)
|
||||
logger.SilentLog("Target tests %s" % target_list)
|
||||
|
||||
# Run on the host
|
||||
logger.Log("\nRunning on host")
|
||||
for f in host_list:
|
||||
if run_command.RunHostCommand(f) != 0:
|
||||
logger.Log("%s... failed" % f)
|
||||
else:
|
||||
if run_command.HasValgrind():
|
||||
if run_command.RunHostCommand(f, valgrind=True) == 0:
|
||||
logger.Log("%s... ok\t\t[valgrind: ok]" % f)
|
||||
else:
|
||||
logger.Log("%s... ok\t\t[valgrind: failed]" % f)
|
||||
else:
|
||||
logger.Log("%s... ok\t\t[valgrind: missing]" % f)
|
||||
|
||||
# Run on the device
|
||||
logger.Log("\nRunning on target")
|
||||
for f in target_list:
|
||||
full_path = os.path.join(os.sep, "data", "nativetest", f)
|
||||
|
||||
# Single quotes are needed to prevent the shell splitting it.
|
||||
output = adb.SendShellCommand("'%s 2>&1;echo -n exit code:$?'" %
|
||||
"(cd /sdcard;%s)" % full_path,
|
||||
int(options.timeout))
|
||||
success = output.endswith("exit code:0")
|
||||
logger.Log("%s... %s" % (f, success and "ok" or "failed"))
|
||||
# Print the captured output when the test failed.
|
||||
if not success or options.verbose:
|
||||
pos = output.rfind("exit code")
|
||||
output = output[0:pos]
|
||||
logger.Log(output)
|
||||
|
||||
# Cleanup
|
||||
adb.SendShellCommand("rm %s" % full_path)
|
||||
|
||||
def _CollectTestSources(self, test_list, dirname, files):
|
||||
"""For each directory, find tests source file and add them to the list.
|
||||
|
||||
Test files must match one of the following pattern:
|
||||
- test_*.[cc|cpp]
|
||||
- *_test.[cc|cpp]
|
||||
- *_unittest.[cc|cpp]
|
||||
|
||||
This method is a callback for os.path.walk.
|
||||
|
||||
Args:
|
||||
test_list: Where new tests should be inserted.
|
||||
dirname: Current directory.
|
||||
files: List of files in the current directory.
|
||||
"""
|
||||
for f in files:
|
||||
(name, ext) = os.path.splitext(f)
|
||||
if ext == ".cc" or ext == ".cpp" or ext == ".c":
|
||||
if re.search("_test$|_test_$|_unittest$|_unittest_$|^test_", name):
|
||||
logger.SilentLog("Found %s" % f)
|
||||
test_list.append(str(os.path.join(dirname, f)))
|
||||
|
||||
def _FilterOutMissing(self, path, sources):
|
||||
"""Filter out from the sources list missing tests.
|
||||
|
||||
Sometimes some test source are not built for the target, i.e there
|
||||
is no binary corresponding to the source file. We need to filter
|
||||
these out.
|
||||
|
||||
Args:
|
||||
path: Where the binaries should be.
|
||||
sources: List of tests source path.
|
||||
Returns:
|
||||
A list of relative paths to the test binaries built from the sources.
|
||||
"""
|
||||
binaries = []
|
||||
for f in sources:
|
||||
binary = os.path.basename(f)
|
||||
binary = os.path.splitext(binary)[0]
|
||||
found = self._FindFileRecursively(path, binary)
|
||||
if found:
|
||||
binary = os.path.relpath(os.path.abspath(found),
|
||||
os.path.abspath(path))
|
||||
binaries.append(binary)
|
||||
return binaries
|
||||
|
||||
def _FindFileRecursively(self, path, match):
|
||||
"""Finds the first executable binary in a given path that matches the name.
|
||||
|
||||
Args:
|
||||
path: Where to search for binaries. Can be nested directories.
|
||||
binary: Which binary to search for.
|
||||
Returns:
|
||||
first matched file in the path or None if none is found.
|
||||
"""
|
||||
for root, dirs, files in os.walk(path):
|
||||
for f in files:
|
||||
if f == match:
|
||||
return os.path.join(root, f)
|
||||
for d in dirs:
|
||||
found = self._FindFileRecursively(os.path.join(root, d), match)
|
||||
if found:
|
||||
return found
|
||||
return None
|
||||
|
||||
def _RunHostCommand(self, binary, valgrind=False):
|
||||
"""Run a command on the host (opt using valgrind).
|
||||
|
||||
Runs the host binary and returns the exit code.
|
||||
If successfull, the output (stdout and stderr) are discarded,
|
||||
but printed in case of error.
|
||||
The command can be run under valgrind in which case all the
|
||||
output are always discarded.
|
||||
|
||||
Args:
|
||||
binary: basename of the file to be run. It is expected to be under
|
||||
$ANDROID_HOST_OUT/bin.
|
||||
valgrind: If True the command will be run under valgrind.
|
||||
|
||||
Returns:
|
||||
The command exit code (int)
|
||||
"""
|
||||
full_path = os.path.join(android_build.GetHostBin(), binary)
|
||||
return run_command.RunHostCommand(full_path, valgrind=valgrind)
|
||||
@@ -1,132 +0,0 @@
|
||||
#!/usr/bin/python2.4
|
||||
#
|
||||
#
|
||||
# Copyright 2008, The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Parser for test definition xml files."""
|
||||
|
||||
# Python imports
|
||||
import xml.dom.minidom
|
||||
import xml.parsers
|
||||
|
||||
# local imports
|
||||
import errors
|
||||
import logger
|
||||
import xml_suite_helper
|
||||
|
||||
|
||||
class TestDefinitions(object):
|
||||
"""Accessor for a test definitions xml file data.
|
||||
|
||||
See test_defs.xsd for expected format.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
# dictionary of test name to tests
|
||||
self._testname_map = {}
|
||||
|
||||
def __iter__(self):
|
||||
ordered_list = []
|
||||
for k in sorted(self._testname_map):
|
||||
ordered_list.append(self._testname_map[k])
|
||||
return iter(ordered_list)
|
||||
|
||||
def Parse(self, file_path):
|
||||
"""Parse the test suite data from from given file path.
|
||||
|
||||
Args:
|
||||
file_path: absolute file path to parse
|
||||
Raises:
|
||||
ParseError if file_path cannot be parsed
|
||||
"""
|
||||
try:
|
||||
doc = xml.dom.minidom.parse(file_path)
|
||||
self._ParseDoc(doc)
|
||||
except IOError:
|
||||
logger.Log("test file %s does not exist" % file_path)
|
||||
raise errors.ParseError
|
||||
except xml.parsers.expat.ExpatError:
|
||||
logger.Log("Error Parsing xml file: %s " % file_path)
|
||||
raise errors.ParseError
|
||||
except errors.ParseError, e:
|
||||
logger.Log("Error Parsing xml file: %s Reason: %s" % (file_path, e.msg))
|
||||
raise e
|
||||
|
||||
def ParseString(self, xml_string):
|
||||
"""Alternate parse method that accepts a string of the xml data."""
|
||||
doc = xml.dom.minidom.parseString(xml_string)
|
||||
# TODO: catch exceptions and raise ParseError
|
||||
return self._ParseDoc(doc)
|
||||
|
||||
def _ParseDoc(self, doc):
|
||||
root_element = self._GetRootElement(doc)
|
||||
suite_parser = xml_suite_helper.XmlSuiteParser()
|
||||
for element in root_element.childNodes:
|
||||
if element.nodeType != xml.dom.Node.ELEMENT_NODE:
|
||||
continue
|
||||
test_suite = suite_parser.Parse(element)
|
||||
if test_suite:
|
||||
self._AddTest(test_suite)
|
||||
|
||||
def _GetRootElement(self, doc):
|
||||
root_elements = doc.getElementsByTagName("test-definitions")
|
||||
if len(root_elements) != 1:
|
||||
error_msg = "expected 1 and only one test-definitions tag"
|
||||
raise errors.ParseError(msg=error_msg)
|
||||
return root_elements[0]
|
||||
|
||||
def _AddTest(self, test):
|
||||
"""Adds a test to this TestManifest.
|
||||
|
||||
If a test already exists with the same name, it overrides it.
|
||||
|
||||
Args:
|
||||
test: TestSuite to add
|
||||
"""
|
||||
if self.GetTest(test.GetName()) is not None:
|
||||
logger.SilentLog("Overriding test definition %s" % test.GetName())
|
||||
self._testname_map[test.GetName()] = test
|
||||
|
||||
def GetTests(self):
|
||||
return self._testname_map.values()
|
||||
|
||||
def GetContinuousTests(self):
|
||||
con_tests = []
|
||||
for test in self.GetTests():
|
||||
if test.IsContinuous():
|
||||
con_tests.append(test)
|
||||
return con_tests
|
||||
|
||||
def GetTestsInSuite(self, suite):
|
||||
"""Return list of tests in given suite."""
|
||||
return [t for t in self.GetTests() if t.GetSuite() == suite]
|
||||
|
||||
def GetTest(self, name):
|
||||
return self._testname_map.get(name, None)
|
||||
|
||||
|
||||
def Parse(file_path):
|
||||
"""Parses out a TestDefinitions from given path to xml file.
|
||||
|
||||
Args:
|
||||
file_path: string absolute file path
|
||||
Returns:
|
||||
a TestDefinitions object containing data parsed from file_path
|
||||
Raises:
|
||||
ParseError if xml format is not recognized
|
||||
"""
|
||||
tests_result = TestDefinitions()
|
||||
tests_result.Parse(file_path)
|
||||
return tests_result
|
||||
@@ -1,151 +0,0 @@
|
||||
#!/usr/bin/python2.4
|
||||
#
|
||||
#
|
||||
# Copyright 2009, The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Abstract Android test suite."""
|
||||
|
||||
|
||||
class AbstractTestSuite(object):
|
||||
"""Represents a generic test suite definition.
|
||||
|
||||
TODO: rename this as AbstractTestDef.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self._name = None
|
||||
self._build_path = None
|
||||
self._build_dependencies = []
|
||||
self._is_continuous = False
|
||||
self._suite = None
|
||||
self._description = ''
|
||||
self._extra_build_args = ''
|
||||
self._is_full_make = False
|
||||
self._is_granted_permissions = True
|
||||
|
||||
def GetName(self):
|
||||
return self._name
|
||||
|
||||
def SetName(self, name):
|
||||
self._name = name
|
||||
return self
|
||||
|
||||
def GetBuildPath(self):
|
||||
"""Returns the build path of this test, relative to source tree root."""
|
||||
return self._build_path
|
||||
|
||||
def SetBuildPath(self, build_path):
|
||||
self._build_path = build_path
|
||||
return self
|
||||
|
||||
def GetBuildDependencies(self, options):
|
||||
"""Returns a list of dependent build paths."""
|
||||
return self._build_dependencies
|
||||
|
||||
def SetBuildDependencies(self, build_dependencies):
|
||||
self._build_dependencies = build_dependencies
|
||||
return self
|
||||
|
||||
def IsContinuous(self):
|
||||
"""Returns true if test is part of the continuous test."""
|
||||
return self._is_continuous
|
||||
|
||||
def SetContinuous(self, continuous):
|
||||
self._is_continuous = continuous
|
||||
return self._is_continuous
|
||||
|
||||
def IsGrantedPermissions(self):
|
||||
"""Return true if the test should be granted runtime permissions on install."""
|
||||
return self._is_granted_permissions
|
||||
|
||||
def SetIsGrantedPermissions(self, is_granted_permissions):
|
||||
self._is_granted_permissions = is_granted_permissions
|
||||
return self._is_granted_permissions
|
||||
|
||||
def GetSuite(self):
|
||||
"""Returns the name of test' suite, or None."""
|
||||
return self._suite
|
||||
|
||||
def SetSuite(self, suite):
|
||||
self._suite = suite
|
||||
return self
|
||||
|
||||
def GetDescription(self):
|
||||
"""Returns a description if available, an empty string otherwise."""
|
||||
return self._description
|
||||
|
||||
def SetDescription(self, desc):
|
||||
self._description = desc
|
||||
return self
|
||||
|
||||
def GetExtraBuildArgs(self):
|
||||
"""Returns the extra build args if available, an empty string otherwise."""
|
||||
return self._extra_build_args
|
||||
|
||||
def SetExtraBuildArgs(self, build_args):
|
||||
self._extra_build_args = build_args
|
||||
return self
|
||||
|
||||
def IsFullMake(self):
|
||||
return self._is_full_make
|
||||
|
||||
def SetIsFullMake(self, full_make):
|
||||
self._is_full_make = full_make
|
||||
return self
|
||||
|
||||
def Run(self, options, adb):
|
||||
"""Runs the test.
|
||||
|
||||
Subclasses must implement this.
|
||||
Args:
|
||||
options: global command line options
|
||||
adb: asdb_interface to device under test
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
class AbstractTestFactory(object):
|
||||
"""generic test suite factory."""
|
||||
|
||||
def __init__(self, test_root_path, build_path):
|
||||
"""Creates a test suite factory.
|
||||
|
||||
Args:
|
||||
test_root_path: the filesystem path to the tests build directory
|
||||
upstream_build_path: filesystem path for the directory
|
||||
to build when running tests, relative to the source tree root.
|
||||
"""
|
||||
self._test_root_path = test_root_path
|
||||
self._build_path = build_path
|
||||
|
||||
def GetBuildPath(self):
|
||||
return self._build_path
|
||||
|
||||
def GetTestsRootPath(self):
|
||||
return self._test_root_path
|
||||
|
||||
def CreateTests(self, sub_tests_path=None):
|
||||
"""Creates the tests at given test_path.
|
||||
|
||||
Subclasses must implement this.
|
||||
|
||||
Args:
|
||||
sub_tests_path: the child path of test_root_path containing the tests to
|
||||
run. If unspecified will be set to test_root_path.
|
||||
|
||||
Returns:
|
||||
an array of AbstractTestSuite, or empty AbstractTestSuite if no tests
|
||||
were defined
|
||||
"""
|
||||
raise NotImplementedError
|
||||
@@ -1,263 +0,0 @@
|
||||
#!/usr/bin/python2.4
|
||||
#
|
||||
#
|
||||
# Copyright 2009, The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Utility to find instrumentation test definitions from file system."""
|
||||
|
||||
# python imports
|
||||
import os
|
||||
|
||||
# local imports
|
||||
import android_build
|
||||
import android_mk
|
||||
import gtest
|
||||
import instrumentation_test
|
||||
import logger
|
||||
|
||||
|
||||
class TestWalker(object):
|
||||
"""Finds Android tests from filesystem."""
|
||||
|
||||
def FindTests(self, path):
|
||||
"""Gets list of Android tests found at given path.
|
||||
|
||||
Tests are created from info found in Android.mk and AndroidManifest.xml
|
||||
files relative to the given path.
|
||||
|
||||
Currently supported tests are:
|
||||
- Android application tests run via instrumentation
|
||||
- native C/C++ tests using GTest framework. (note Android.mk must follow
|
||||
expected GTest template)
|
||||
|
||||
FindTests will first scan sub-folders of path for tests. If none are found,
|
||||
it will scan the file system upwards until a valid test Android.mk is found
|
||||
or the Android build root is reached.
|
||||
|
||||
Some sample values for path:
|
||||
- a parent directory containing many tests:
|
||||
ie development/samples will return tests for instrumentation's in ApiDemos,
|
||||
ApiDemos/tests, Notepad/tests etc
|
||||
- a java test class file
|
||||
ie ApiDemos/tests/src/../ApiDemosTest.java will return a test for
|
||||
the instrumentation in ApiDemos/tests, with the class name filter set to
|
||||
ApiDemosTest
|
||||
- a java package directory
|
||||
ie ApiDemos/tests/src/com/example/android/apis will return a test for
|
||||
the instrumentation in ApiDemos/tests, with the java package filter set
|
||||
to com.example.android.apis.
|
||||
|
||||
TODO: add GTest examples
|
||||
|
||||
Args:
|
||||
path: file system path to search
|
||||
|
||||
Returns:
|
||||
list of test suites that support operations defined by
|
||||
test_suite.AbstractTestSuite
|
||||
"""
|
||||
if not os.path.exists(path):
|
||||
logger.Log('%s does not exist' % path)
|
||||
return []
|
||||
realpath = os.path.realpath(path)
|
||||
# ensure path is in ANDROID_BUILD_ROOT
|
||||
self._build_top = os.path.realpath(android_build.GetTop())
|
||||
if not self._IsPathInBuildTree(realpath):
|
||||
logger.Log('%s is not a sub-directory of build root %s' %
|
||||
(path, self._build_top))
|
||||
return []
|
||||
|
||||
# first, assume path is a parent directory, which specifies to run all
|
||||
# tests within this directory
|
||||
tests = self._FindSubTests(realpath, [])
|
||||
if not tests:
|
||||
logger.SilentLog('No tests found within %s, searching upwards' % path)
|
||||
tests = self._FindUpstreamTests(realpath)
|
||||
return tests
|
||||
|
||||
def _IsPathInBuildTree(self, path):
|
||||
"""Return true if given path is within current Android build tree.
|
||||
|
||||
Args:
|
||||
path: absolute file system path
|
||||
|
||||
Returns:
|
||||
True if path is within Android build tree
|
||||
"""
|
||||
return os.path.commonprefix([self._build_top, path]) == self._build_top
|
||||
|
||||
def _MakePathRelativeToBuild(self, path):
|
||||
"""Convert given path to one relative to build tree root.
|
||||
|
||||
Args:
|
||||
path: absolute file system path to convert.
|
||||
|
||||
Returns:
|
||||
The converted path relative to build tree root.
|
||||
|
||||
Raises:
|
||||
ValueError: if path is not within build tree
|
||||
"""
|
||||
if not self._IsPathInBuildTree(path):
|
||||
raise ValueError
|
||||
build_path_len = len(self._build_top) + 1
|
||||
# return string with common build_path removed
|
||||
return path[build_path_len:]
|
||||
|
||||
def _FindSubTests(self, path, tests, upstream_build_path=None):
|
||||
"""Recursively finds all tests within given path.
|
||||
|
||||
Args:
|
||||
path: absolute file system path to check
|
||||
tests: current list of found tests
|
||||
upstream_build_path: the parent directory where Android.mk that builds
|
||||
sub-folders was found
|
||||
|
||||
Returns:
|
||||
updated list of tests
|
||||
"""
|
||||
if not os.path.isdir(path):
|
||||
return tests
|
||||
android_mk_parser = android_mk.CreateAndroidMK(path)
|
||||
if android_mk_parser:
|
||||
build_rel_path = self._MakePathRelativeToBuild(path)
|
||||
if not upstream_build_path:
|
||||
# haven't found a parent makefile which builds this dir. Use current
|
||||
# dir as build path
|
||||
tests.extend(self._CreateSuites(
|
||||
android_mk_parser, path, build_rel_path))
|
||||
else:
|
||||
tests.extend(self._CreateSuites(android_mk_parser, path,
|
||||
upstream_build_path))
|
||||
# TODO: remove this logic, and rely on caller to collapse build
|
||||
# paths via make_tree
|
||||
|
||||
# Try to build as much of original path as possible, so
|
||||
# keep track of upper-most parent directory where Android.mk was found
|
||||
# that has rule to build sub-directory makefiles.
|
||||
# this is also necessary in case of overlapping tests
|
||||
# ie if a test exists at 'foo' directory and 'foo/sub', attempting to
|
||||
# build both 'foo' and 'foo/sub' will fail.
|
||||
|
||||
if android_mk_parser.IncludesMakefilesUnder():
|
||||
# found rule to build sub-directories. The parent path can be used,
|
||||
# or if not set, use current path
|
||||
if not upstream_build_path:
|
||||
upstream_build_path = self._MakePathRelativeToBuild(path)
|
||||
else:
|
||||
upstream_build_path = None
|
||||
for filename in os.listdir(path):
|
||||
self._FindSubTests(os.path.join(path, filename), tests,
|
||||
upstream_build_path)
|
||||
return tests
|
||||
|
||||
def _FindUpstreamTests(self, path):
|
||||
"""Find tests defined upward from given path.
|
||||
|
||||
Args:
|
||||
path: the location to start searching.
|
||||
|
||||
Returns:
|
||||
list of test_suite.AbstractTestSuite found, may be empty
|
||||
"""
|
||||
factory = self._FindUpstreamTestFactory(path)
|
||||
if factory:
|
||||
return factory.CreateTests(sub_tests_path=path)
|
||||
else:
|
||||
return []
|
||||
|
||||
def _GetTestFactory(self, android_mk_parser, path, build_path):
|
||||
"""Get the test factory for given makefile.
|
||||
|
||||
If given path is a valid tests build path, will return the TestFactory
|
||||
for creating tests.
|
||||
|
||||
Args:
|
||||
android_mk_parser: the android mk to evaluate
|
||||
path: the filesystem path of the makefile
|
||||
build_path: filesystem path for the directory
|
||||
to build when running tests, relative to source root.
|
||||
|
||||
Returns:
|
||||
the TestFactory or None if path is not a valid tests build path
|
||||
"""
|
||||
if android_mk_parser.HasGTest():
|
||||
return gtest.GTestFactory(path, build_path)
|
||||
elif instrumentation_test.HasInstrumentationTest(path):
|
||||
return instrumentation_test.InstrumentationTestFactory(path,
|
||||
build_path)
|
||||
else:
|
||||
# somewhat unusual, but will continue searching
|
||||
logger.SilentLog('Found makefile at %s, but did not detect any tests.'
|
||||
% path)
|
||||
|
||||
return None
|
||||
|
||||
def _GetTestFactoryForPath(self, path):
|
||||
"""Get the test factory for given path.
|
||||
|
||||
If given path is a valid tests build path, will return the TestFactory
|
||||
for creating tests.
|
||||
|
||||
Args:
|
||||
path: the filesystem path to evaluate
|
||||
|
||||
Returns:
|
||||
the TestFactory or None if path is not a valid tests build path
|
||||
"""
|
||||
android_mk_parser = android_mk.CreateAndroidMK(path)
|
||||
if android_mk_parser:
|
||||
build_path = self._MakePathRelativeToBuild(path)
|
||||
return self._GetTestFactory(android_mk_parser, path, build_path)
|
||||
else:
|
||||
return None
|
||||
|
||||
def _FindUpstreamTestFactory(self, path):
|
||||
"""Recursively searches filesystem upwards for a test factory.
|
||||
|
||||
Args:
|
||||
path: file system path to search
|
||||
|
||||
Returns:
|
||||
the TestFactory found or None
|
||||
"""
|
||||
factory = self._GetTestFactoryForPath(path)
|
||||
if factory:
|
||||
return factory
|
||||
dirpath = os.path.dirname(path)
|
||||
if self._IsPathInBuildTree(path):
|
||||
return self._FindUpstreamTestFactory(dirpath)
|
||||
logger.Log('A tests Android.mk was not found')
|
||||
return None
|
||||
|
||||
def _CreateSuites(self, android_mk_parser, path, upstream_build_path):
|
||||
"""Creates TestSuites from a AndroidMK.
|
||||
|
||||
Args:
|
||||
android_mk_parser: the AndroidMK
|
||||
path: absolute file system path of the makefile to evaluate
|
||||
upstream_build_path: the build path to use for test. This can be
|
||||
different than the 'path', in cases where an upstream makefile
|
||||
is being used.
|
||||
|
||||
Returns:
|
||||
the list of tests created
|
||||
"""
|
||||
factory = self._GetTestFactory(android_mk_parser, path,
|
||||
build_path=upstream_build_path)
|
||||
if factory:
|
||||
return factory.CreateTests(path)
|
||||
else:
|
||||
return []
|
||||
@@ -1,162 +0,0 @@
|
||||
#!/usr/bin/python2.4
|
||||
#
|
||||
#
|
||||
# Copyright 2009, The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Utility to parse suite info from xml."""
|
||||
|
||||
# Python imports
|
||||
import xml.dom.minidom
|
||||
import xml.parsers
|
||||
|
||||
# local imports
|
||||
import errors
|
||||
import logger
|
||||
import host_test
|
||||
import instrumentation_test
|
||||
import native_test
|
||||
|
||||
|
||||
class XmlSuiteParser(object):
|
||||
"""Parses XML attributes common to all TestSuite's."""
|
||||
|
||||
# common attributes
|
||||
_NAME_ATTR = 'name'
|
||||
_BUILD_ATTR = 'build_path'
|
||||
_CONTINUOUS_ATTR = 'continuous'
|
||||
_SUITE_ATTR = 'suite'
|
||||
_DESCRIPTION_ATTR = 'description'
|
||||
_EXTRA_BUILD_ARGS_ATTR = 'extra_build_args'
|
||||
_FULL_MAKE_ATTR = 'full_make'
|
||||
_GRANTED_PERMISSIONS_ATTR = 'granted_permissions'
|
||||
|
||||
def Parse(self, element):
|
||||
"""Populates common suite attributes from given suite xml element.
|
||||
|
||||
Args:
|
||||
element: xml node to parse
|
||||
Raises:
|
||||
ParseError if a required attribute is missing.
|
||||
Returns:
|
||||
parsed test suite or None
|
||||
"""
|
||||
parser = None
|
||||
if element.nodeName == InstrumentationParser.TAG_NAME:
|
||||
parser = InstrumentationParser()
|
||||
elif element.nodeName == NativeParser.TAG_NAME:
|
||||
parser = NativeParser()
|
||||
elif element.nodeName == HostParser.TAG_NAME:
|
||||
parser = HostParser()
|
||||
else:
|
||||
logger.Log('Unrecognized tag %s found' % element.nodeName)
|
||||
return None
|
||||
test_suite = parser.Parse(element)
|
||||
return test_suite
|
||||
|
||||
def _ParseCommonAttributes(self, suite_element, test_suite):
|
||||
test_suite.SetName(self._ParseAttribute(suite_element, self._NAME_ATTR,
|
||||
True))
|
||||
test_suite.SetBuildPath(self._ParseAttribute(suite_element,
|
||||
self._BUILD_ATTR, True))
|
||||
test_suite.SetContinuous(self._ParseAttribute(suite_element,
|
||||
self._CONTINUOUS_ATTR,
|
||||
False, default_value=False))
|
||||
test_suite.SetIsGrantedPermissions(self._ParseAttribute(suite_element,
|
||||
self._GRANTED_PERMISSIONS_ATTR,
|
||||
False, default_value=True))
|
||||
test_suite.SetSuite(self._ParseAttribute(suite_element, self._SUITE_ATTR, False,
|
||||
default_value=None))
|
||||
test_suite.SetDescription(self._ParseAttribute(suite_element,
|
||||
self._DESCRIPTION_ATTR,
|
||||
False,
|
||||
default_value=''))
|
||||
test_suite.SetExtraBuildArgs(self._ParseAttribute(
|
||||
suite_element, self._EXTRA_BUILD_ARGS_ATTR, False, default_value=''))
|
||||
test_suite.SetIsFullMake(self._ParseAttribute(
|
||||
suite_element, self._FULL_MAKE_ATTR, False, default_value=False))
|
||||
|
||||
|
||||
def _ParseAttribute(self, suite_element, attribute_name, mandatory,
|
||||
default_value=None):
|
||||
if suite_element.hasAttribute(attribute_name):
|
||||
value = suite_element.getAttribute(attribute_name)
|
||||
if default_value in (True, False):
|
||||
value = value.lower() == "true"
|
||||
elif mandatory:
|
||||
error_msg = ('Could not find attribute %s in %s' %
|
||||
(attribute_name, self.TAG_NAME))
|
||||
raise errors.ParseError(msg=error_msg)
|
||||
else:
|
||||
value = default_value
|
||||
return value
|
||||
|
||||
|
||||
class InstrumentationParser(XmlSuiteParser):
|
||||
"""Parses instrumentation suite attributes from xml."""
|
||||
|
||||
# for legacy reasons, the xml tag name for java (device) tests is 'test'
|
||||
TAG_NAME = 'test'
|
||||
|
||||
_PKG_ATTR = 'package'
|
||||
_RUNNER_ATTR = 'runner'
|
||||
_CLASS_ATTR = 'class'
|
||||
_TARGET_ATTR = 'coverage_target'
|
||||
|
||||
def Parse(self, suite_element):
|
||||
"""Creates suite and populate with data from xml element."""
|
||||
suite = instrumentation_test.InstrumentationTestSuite()
|
||||
XmlSuiteParser._ParseCommonAttributes(self, suite_element, suite)
|
||||
suite.SetPackageName(self._ParseAttribute(suite_element, self._PKG_ATTR,
|
||||
True))
|
||||
suite.SetRunnerName(self._ParseAttribute(
|
||||
suite_element, self._RUNNER_ATTR, False,
|
||||
instrumentation_test.InstrumentationTestSuite.DEFAULT_RUNNER))
|
||||
suite.SetClassName(self._ParseAttribute(suite_element, self._CLASS_ATTR,
|
||||
False))
|
||||
suite.SetTargetName(self._ParseAttribute(suite_element, self._TARGET_ATTR,
|
||||
False))
|
||||
return suite
|
||||
|
||||
|
||||
class NativeParser(XmlSuiteParser):
|
||||
"""Parses native suite attributes from xml."""
|
||||
|
||||
TAG_NAME = 'test-native'
|
||||
|
||||
def Parse(self, suite_element):
|
||||
"""Creates suite and populate with data from xml element."""
|
||||
suite = native_test.NativeTestSuite()
|
||||
XmlSuiteParser._ParseCommonAttributes(self, suite_element, suite)
|
||||
return suite
|
||||
|
||||
|
||||
class HostParser(XmlSuiteParser):
|
||||
"""Parses host suite attributes from xml."""
|
||||
|
||||
TAG_NAME = 'test-host'
|
||||
|
||||
_CLASS_ATTR = 'class'
|
||||
# TODO: consider obsoleting in favor of parsing the Android.mk to find the
|
||||
# jar name
|
||||
_JAR_ATTR = 'jar_name'
|
||||
|
||||
def Parse(self, suite_element):
|
||||
"""Creates suite and populate with data from xml element."""
|
||||
suite = host_test.HostTestSuite()
|
||||
XmlSuiteParser._ParseCommonAttributes(self, suite_element, suite)
|
||||
suite.SetClassName(self._ParseAttribute(suite_element, self._CLASS_ATTR,
|
||||
True))
|
||||
suite.SetJarName(self._ParseAttribute(suite_element, self._JAR_ATTR, True))
|
||||
return suite
|
||||
@@ -1,30 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2009 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<!-- Sample manifest file used for unit testing -->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.example.android.tests">
|
||||
|
||||
<application>
|
||||
<uses-library android:name="android.test.runner" />
|
||||
</application>
|
||||
|
||||
<instrumentation android:name="android.test.InstrumentationTestRunner"
|
||||
android:targetPackage="com.example.android"
|
||||
android:label="Tests"/>
|
||||
|
||||
</manifest>
|
||||
@@ -1,46 +0,0 @@
|
||||
# Copyright (C) 2011 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Build the unit tests.
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
# Build the unit tests.
|
||||
test_src_files := $(call find-subdir-files, *.cpp)
|
||||
|
||||
shared_libraries := \
|
||||
libz \
|
||||
liblog \
|
||||
libcutils \
|
||||
libutils \
|
||||
|
||||
static_libraries := \
|
||||
libgtest \
|
||||
libgtest_main
|
||||
|
||||
c_includes := \
|
||||
external/gtest/include \
|
||||
|
||||
module_tags := eng tests
|
||||
|
||||
$(foreach file,$(test_src_files), \
|
||||
$(eval include $(CLEAR_VARS)) \
|
||||
$(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
|
||||
$(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \
|
||||
$(eval LOCAL_C_INCLUDES := $(c_includes)) \
|
||||
$(eval LOCAL_SRC_FILES := $(file)) \
|
||||
$(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
|
||||
$(eval LOCAL_MODULE_TAGS := $(module_tags)) \
|
||||
$(eval include $(BUILD_EXECUTABLE)) \
|
||||
)
|
||||
@@ -1,28 +0,0 @@
|
||||
# Copyright (C) 2011 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
# We only want this apk build for tests.
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
LOCAL_JAVA_LIBRARIES := foo android.test.runner
|
||||
# Include all test java files.
|
||||
LOCAL_SRC_FILES := $(call all-java-files-under, src)
|
||||
LOCAL_PACKAGE_NAME := ApiDemosTests
|
||||
LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
|
||||
LOCAL_LICENSE_CONDITIONS := notice
|
||||
LOCAL_INSTRUMENTATION_FOR := ApiDemos
|
||||
LOCAL_SDK_VERSION := current
|
||||
|
||||
include $(BUILD_PACKAGE)
|
||||
@@ -1,23 +0,0 @@
|
||||
# Copyright (C) 2011 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
test_module := foo
|
||||
LOCAL_MODULE := $(test_module)
|
||||
LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
|
||||
LOCAL_LICENSE_CONDITIONS := notice
|
||||
recursive_var := $(recursive_var)
|
||||
LOCAL_MODULE_TAGS := tags
|
||||
LOCAL_SRC_FILES := src
|
||||
include $(BUILD_NATIVE_TEST)
|
||||
@@ -1,201 +0,0 @@
|
||||
#!/usr/bin/python2.4
|
||||
#
|
||||
#
|
||||
# Copyright 2009, The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import sys
|
||||
import unittest
|
||||
sys.path.append('../..')
|
||||
|
||||
from testrunner import am_instrument_parser
|
||||
|
||||
|
||||
class AmParserTest(unittest.TestCase):
|
||||
|
||||
def testParseAmInstResult(self):
|
||||
result="""INSTRUMENTATION_RESULT: performance.java_size=4871
|
||||
INSTRUMENTATION_RESULT: stream=
|
||||
Error: Failed to generate emma coverage.
|
||||
INSTRUMENTATION_RESULT: performance.cpu_time=33846
|
||||
INSTRUMENTATION_CODE: -1
|
||||
"""
|
||||
bundle_dict = \
|
||||
am_instrument_parser._ParseInstrumentationFinishedBundle(result)
|
||||
self.assertEquals(4871, bundle_dict['java_size'])
|
||||
self.assertEquals(33846, bundle_dict['cpu_time'])
|
||||
self.assertEquals("\nError: Failed to generate emma coverage.",
|
||||
bundle_dict['stream'])
|
||||
|
||||
def testParseAmInstStatus(self):
|
||||
# numtests before id
|
||||
segment1 = """INSTRUMENTATION_STATUS: stream=
|
||||
INSTRUMENTATION_STATUS: test=testLaunchComplexActivity
|
||||
INSTRUMENTATION_STATUS: class=LaunchPerformanceTest
|
||||
INSTRUMENTATION_STATUS: current=1
|
||||
INSTRUMENTATION_STATUS: numtests=2
|
||||
INSTRUMENTATION_STATUS: id=InstrumentationTestRunner
|
||||
INSTRUMENTATION_STATUS_CODE: 1"""
|
||||
segment2 = """INSTRUMENTATION_STATUS: stream=.
|
||||
INSTRUMENTATION_STATUS: test=testLaunchComplexActivity
|
||||
INSTRUMENTATION_STATUS: performance.cpu_time=866
|
||||
INSTRUMENTATION_STATUS: performance.execution_time=1242
|
||||
INSTRUMENTATION_STATUS: class=LaunchPerformanceTest
|
||||
INSTRUMENTATION_STATUS: current=1
|
||||
INSTRUMENTATION_STATUS: numtests=2
|
||||
INSTRUMENTATION_STATUS: id=InstrumentationTestRunner
|
||||
INSTRUMENTATION_STATUS_CODE: 0"""
|
||||
# numtests after id
|
||||
segment3 = """INSTRUMENTATION_STATUS: stream=
|
||||
INSTRUMENTATION_STATUS: test=testLaunchSimpleActivity
|
||||
INSTRUMENTATION_STATUS: class=LaunchPerformanceTest
|
||||
INSTRUMENTATION_STATUS: current=2
|
||||
INSTRUMENTATION_STATUS: id=InstrumentationTestRunner
|
||||
INSTRUMENTATION_STATUS: numtests=8
|
||||
INSTRUMENTATION_STATUS_CODE: 1"""
|
||||
segment4 = """INSTRUMENTATION_STATUS: stream=.
|
||||
INSTRUMENTATION_STATUS: test=testLaunchSimpleActivity
|
||||
INSTRUMENTATION_STATUS: performance.cpu_time=590
|
||||
INSTRUMENTATION_STATUS: performance.execution_time=1122
|
||||
INSTRUMENTATION_STATUS: class=LaunchPerformanceTest
|
||||
INSTRUMENTATION_STATUS: current=2
|
||||
INSTRUMENTATION_STATUS: id=InstrumentationTestRunner
|
||||
INSTRUMENTATION_STATUS: numtests=8
|
||||
INSTRUMENTATION_STATUS_CODE: 0"""
|
||||
|
||||
result = am_instrument_parser.TestResult(segment1)
|
||||
map = result.GetResultFields()
|
||||
self.assertEquals('testLaunchComplexActivity', map['test'])
|
||||
self.assertEquals('LaunchPerformanceTest', map['class'])
|
||||
self.assertEquals('1', map['current'])
|
||||
self.assertEquals('2', map['numtests'])
|
||||
self.assertEquals('InstrumentationTestRunner', map['id'])
|
||||
self.assertEquals(1, result.GetStatusCode())
|
||||
|
||||
result = am_instrument_parser.TestResult(segment2)
|
||||
map = result.GetResultFields()
|
||||
self.assertEquals('testLaunchComplexActivity', map['test'])
|
||||
self.assertEquals('866', map['cpu_time'])
|
||||
self.assertEquals('1242', map['execution_time'])
|
||||
self.assertEquals('LaunchPerformanceTest', map['class'])
|
||||
self.assertEquals('1', map['current'])
|
||||
self.assertEquals('2', map['numtests'])
|
||||
self.assertEquals('InstrumentationTestRunner', map['id'])
|
||||
self.assertEquals(0, result.GetStatusCode())
|
||||
|
||||
result = am_instrument_parser.TestResult(segment3)
|
||||
map = result.GetResultFields()
|
||||
self.assertEquals('testLaunchSimpleActivity', map['test'])
|
||||
self.assertEquals('LaunchPerformanceTest', map['class'])
|
||||
self.assertEquals('2', map['current'])
|
||||
self.assertEquals('8', map['numtests'])
|
||||
self.assertEquals('InstrumentationTestRunner', map['id'])
|
||||
self.assertEquals(1, result.GetStatusCode())
|
||||
|
||||
result = am_instrument_parser.TestResult(segment4)
|
||||
map = result.GetResultFields()
|
||||
self.assertEquals('testLaunchSimpleActivity', map['test'])
|
||||
self.assertEquals('590', map['cpu_time'])
|
||||
self.assertEquals('1122', map['execution_time'])
|
||||
self.assertEquals('LaunchPerformanceTest', map['class'])
|
||||
self.assertEquals('2', map['current'])
|
||||
self.assertEquals('8', map['numtests'])
|
||||
self.assertEquals('InstrumentationTestRunner', map['id'])
|
||||
self.assertEquals(0, result.GetStatusCode())
|
||||
|
||||
def testParseAmInstOutput(self):
|
||||
result = """INSTRUMENTATION_STATUS: class=LaunchPerformanceTestCase
|
||||
INSTRUMENTATION_STATUS: current=1
|
||||
INSTRUMENTATION_STATUS: id=InstrumentationTestRunner
|
||||
INSTRUMENTATION_STATUS: numtests=2
|
||||
INSTRUMENTATION_STATUS: stream=
|
||||
LaunchPerformanceTestCase:
|
||||
INSTRUMENTATION_STATUS: test=testLaunchComplexActivity
|
||||
INSTRUMENTATION_STATUS_CODE: 1
|
||||
INSTRUMENTATION_STATUS: class=LaunchPerformanceTestCase
|
||||
INSTRUMENTATION_STATUS: current=1
|
||||
INSTRUMENTATION_STATUS: id=InstrumentationTestRunner
|
||||
INSTRUMENTATION_STATUS: numtests=2
|
||||
INSTRUMENTATION_STATUS: performance.cpu_time=866
|
||||
INSTRUMENTATION_STATUS: performance.execution_time=1242
|
||||
INSTRUMENTATION_STATUS: stream=.
|
||||
INSTRUMENTATION_STATUS: test=testLaunchComplexActivity
|
||||
INSTRUMENTATION_STATUS_CODE: 0
|
||||
INSTRUMENTATION_STATUS: class=LaunchPerformanceTestCase
|
||||
INSTRUMENTATION_STATUS: current=2
|
||||
INSTRUMENTATION_STATUS: id=InstrumentationTestRunner
|
||||
INSTRUMENTATION_STATUS: numtests=2
|
||||
INSTRUMENTATION_STATUS: stream=
|
||||
INSTRUMENTATION_STATUS: test=testLaunchSimpleActivity
|
||||
INSTRUMENTATION_STATUS_CODE: 1
|
||||
INSTRUMENTATION_STATUS: class=LaunchPerformanceTestCase
|
||||
INSTRUMENTATION_STATUS: current=2
|
||||
INSTRUMENTATION_STATUS: id=InstrumentationTestRunner
|
||||
INSTRUMENTATION_STATUS: numtests=2
|
||||
INSTRUMENTATION_STATUS: performance.cpu_time=590
|
||||
INSTRUMENTATION_STATUS: performance.execution_time=1122
|
||||
INSTRUMENTATION_STATUS: stream=.
|
||||
INSTRUMENTATION_STATUS: test=testLaunchSimpleActivity
|
||||
INSTRUMENTATION_STATUS_CODE: 0
|
||||
INSTRUMENTATION_RESULT: performance.cpu_time=829
|
||||
INSTRUMENTATION_RESULT: performance.execution_time=1708
|
||||
INSTRUMENTATION_RESULT: performance.gc_invocation_count=0
|
||||
INSTRUMENTATION_RESULT: performance.global_alloc_count=2848
|
||||
INSTRUMENTATION_RESULT: performance.global_alloc_size=193079
|
||||
INSTRUMENTATION_RESULT: performance.global_freed_count=1207
|
||||
INSTRUMENTATION_RESULT: performance.global_freed_size=93040
|
||||
INSTRUMENTATION_RESULT: performance.java_allocated=2175
|
||||
INSTRUMENTATION_RESULT: performance.java_free=580
|
||||
INSTRUMENTATION_RESULT: performance.java_private_dirty=740
|
||||
INSTRUMENTATION_RESULT: performance.java_pss=1609
|
||||
INSTRUMENTATION_RESULT: performance.java_shared_dirty=3860
|
||||
INSTRUMENTATION_RESULT: performance.java_size=2755
|
||||
INSTRUMENTATION_RESULT: performance.native_allocated=2585
|
||||
INSTRUMENTATION_RESULT: performance.native_free=34
|
||||
INSTRUMENTATION_RESULT: performance.native_private_dirty=632
|
||||
INSTRUMENTATION_RESULT: performance.native_pss=701
|
||||
INSTRUMENTATION_RESULT: performance.native_shared_dirty=1164
|
||||
INSTRUMENTATION_RESULT: performance.native_size=2620
|
||||
INSTRUMENTATION_RESULT: performance.other_private_dirty=896
|
||||
INSTRUMENTATION_RESULT: performance.other_pss=1226
|
||||
INSTRUMENTATION_RESULT: performance.other_shared_dirty=804
|
||||
INSTRUMENTATION_RESULT: performance.pre_received_transactions=-1
|
||||
INSTRUMENTATION_RESULT: performance.pre_sent_transactions=-1
|
||||
INSTRUMENTATION_RESULT: performance.received_transactions=-1
|
||||
INSTRUMENTATION_RESULT: performance.sent_transactions=-1
|
||||
INSTRUMENTATION_RESULT: stream=
|
||||
Test results for InstrumentationTestRunner=..
|
||||
Time: 2.413
|
||||
|
||||
OK (2 tests)
|
||||
|
||||
|
||||
INSTRUMENTATION_CODE: -1
|
||||
"""
|
||||
(results_list, perf_dict) = \
|
||||
am_instrument_parser.ParseAmInstrumentOutput(result)
|
||||
self.assertEquals(829, perf_dict['cpu_time'])
|
||||
self.assertEquals(2848, perf_dict['global_alloc_count'])
|
||||
self.assertEquals(93040, perf_dict['global_freed_size'])
|
||||
self.assertEquals(740, perf_dict['java_private_dirty'])
|
||||
self.assertEquals(2755, perf_dict['java_size'])
|
||||
self.assertEquals(632, perf_dict['native_private_dirty'])
|
||||
self.assertEquals(2620, perf_dict['native_size'])
|
||||
self.assertEquals(804, perf_dict['other_shared_dirty'])
|
||||
self.assertEquals(-1, perf_dict['received_transactions'])
|
||||
self.assertTrue(len(perf_dict['stream']) > 50)
|
||||
self.assertEquals('-1', perf_dict['code'])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -1,42 +0,0 @@
|
||||
#!/usr/bin/python2.4
|
||||
#
|
||||
#
|
||||
# Copyright 2009, The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import sys
|
||||
import unittest
|
||||
sys.path.append('../..')
|
||||
|
||||
from testrunner import android_manifest
|
||||
|
||||
|
||||
class AndroidManifestTest(unittest.TestCase):
|
||||
"""Unit tests for AndroidManifest."""
|
||||
|
||||
def setUp(self):
|
||||
"""Create android_mainfest for testing from sample file."""
|
||||
self._manifest = android_manifest.AndroidManifest(app_path='.')
|
||||
|
||||
def testGetPackageName(self):
|
||||
self.assertEquals('com.example.android.tests',
|
||||
self._manifest.GetPackageName())
|
||||
|
||||
def testGetInstrumentationNames(self):
|
||||
self.assertEquals(['android.test.InstrumentationTestRunner'],
|
||||
self._manifest.GetInstrumentationNames())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -1,90 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
#
|
||||
# Copyright 2011, The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import sys
|
||||
import unittest
|
||||
sys.path.append('../..')
|
||||
|
||||
from testrunner import android_mk
|
||||
|
||||
|
||||
class AndroidMKTest(unittest.TestCase):
|
||||
"""Unit tests for AndroidMK."""
|
||||
|
||||
def testHasGTest(self):
|
||||
"""Test for AndroidMK.HasGTest."""
|
||||
mk_parser = android_mk.CreateAndroidMK(path='.',
|
||||
filename='Android_native.mk')
|
||||
self.assertTrue(mk_parser.HasGTest())
|
||||
|
||||
def testHasGTest_lib(self):
|
||||
"""Test for AndroidMK.HasGTest."""
|
||||
mk_parser = android_mk.CreateAndroidMK(path='.',
|
||||
filename='Android_gtestlib.mk')
|
||||
self.assertTrue(mk_parser.HasGTest())
|
||||
|
||||
def testHasGTest_false(self):
|
||||
"""Negative test for AndroidMK.HasGTest."""
|
||||
mk_parser = android_mk.CreateAndroidMK(path='.', filename='Android_java.mk')
|
||||
self.assertFalse(mk_parser.HasGTest())
|
||||
|
||||
def testHasJavaLibrary(self):
|
||||
"""Test for AndroidMK.HasJavaLibrary."""
|
||||
mk_parser = android_mk.CreateAndroidMK(path='.',
|
||||
filename='Android_java.mk')
|
||||
self.assertTrue(mk_parser.HasJavaLibrary('android.test.runner'))
|
||||
|
||||
def testHasJavaLibrary_missing(self):
|
||||
"""Negative test for AndroidMK.HasJavaLibrary.
|
||||
|
||||
Test behavior when LOCAL_JAVA_LIBARIES rule is not present in makefile.
|
||||
"""
|
||||
mk_parser = android_mk.CreateAndroidMK(path='.',
|
||||
filename='Android_native.mk')
|
||||
self.assertFalse(mk_parser.HasJavaLibrary('android.test.runner'))
|
||||
|
||||
def testHasJavaLibrary_false(self):
|
||||
"""Negative test for AndroidMK.HasJavaLibrary.
|
||||
|
||||
Test behavior when LOCAL_JAVA_LIBARIES rule is present, but does not list
|
||||
given library.
|
||||
"""
|
||||
mk_parser = android_mk.CreateAndroidMK(path='.', filename='Android_java.mk')
|
||||
self.assertFalse(mk_parser.HasJavaLibrary('doesntexist'))
|
||||
|
||||
def testGetExpandedVariable(self):
|
||||
"""Test for AndroidMK.GetExpandedVariable.
|
||||
"""
|
||||
mk_parser = android_mk.CreateAndroidMK(path='.',
|
||||
filename='Android_native.mk')
|
||||
self.assertEquals('foo', mk_parser.GetExpandedVariable('LOCAL_MODULE'))
|
||||
|
||||
def testGetExpandedVariable_loop(self):
|
||||
"""Test for AndroidMK.GetExpandedVariable where variable expansion loops
|
||||
"""
|
||||
mk_parser = android_mk.CreateAndroidMK(path='.',
|
||||
filename='Android_native.mk')
|
||||
try:
|
||||
mk_parser.GetExpandedVariable('recursive_var')
|
||||
self.assertTrue(False)
|
||||
except RuntimeError:
|
||||
# expected
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user