Make coverage work without test defs.
Change-Id: I946df038e97dc5c2f40a4610d4076e13ab6bde37
This commit is contained in:
@@ -115,6 +115,10 @@ class AndroidMK(object):
|
|||||||
"""
|
"""
|
||||||
return identifier in self._includes
|
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):
|
def HasJavaLibrary(self, library_name):
|
||||||
"""Check if library is specified as a local java library in makefile.
|
"""Check if library is specified as a local java library in makefile.
|
||||||
|
|
||||||
@@ -168,4 +172,4 @@ def CreateAndroidMK(path, filename=AndroidMK.FILENAME):
|
|||||||
mk._ParseMK(mk_path)
|
mk._ParseMK(mk_path)
|
||||||
return mk
|
return mk
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|||||||
1
testrunner/coverage/__init__.py
Normal file
1
testrunner/coverage/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
__all__ = ['coverage', 'coverage_targets', 'coverage_target']
|
||||||
@@ -24,6 +24,8 @@ import os
|
|||||||
|
|
||||||
# local imports
|
# local imports
|
||||||
import android_build
|
import android_build
|
||||||
|
import android_mk
|
||||||
|
import coverage_target
|
||||||
import coverage_targets
|
import coverage_targets
|
||||||
import errors
|
import errors
|
||||||
import logger
|
import logger
|
||||||
@@ -62,7 +64,9 @@ class CoverageGenerator(object):
|
|||||||
self._adb = adb_interface
|
self._adb = adb_interface
|
||||||
self._targets_manifest = self._ReadTargets()
|
self._targets_manifest = self._ReadTargets()
|
||||||
|
|
||||||
def ExtractReport(self, test_suite,
|
def ExtractReport(self,
|
||||||
|
test_suite_name,
|
||||||
|
target,
|
||||||
device_coverage_path,
|
device_coverage_path,
|
||||||
output_path=None,
|
output_path=None,
|
||||||
test_qualifier=None):
|
test_qualifier=None):
|
||||||
@@ -70,7 +74,8 @@ class CoverageGenerator(object):
|
|||||||
|
|
||||||
Assumes test has just been executed.
|
Assumes test has just been executed.
|
||||||
Args:
|
Args:
|
||||||
test_suite: TestSuite to generate coverage data for
|
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
|
device_coverage_path: location of coverage file on device
|
||||||
output_path: path to place output files in. If None will use
|
output_path: path to place output files in. If None will use
|
||||||
<android_root_path>/<_COVERAGE_REPORT_PATH>/<target>/<test[-qualifier]>
|
<android_root_path>/<_COVERAGE_REPORT_PATH>/<target>/<test[-qualifier]>
|
||||||
@@ -81,12 +86,12 @@ class CoverageGenerator(object):
|
|||||||
absolute file path string of generated html report file.
|
absolute file path string of generated html report file.
|
||||||
"""
|
"""
|
||||||
if output_path is None:
|
if output_path is None:
|
||||||
report_name = test_suite.GetName()
|
report_name = test_suite_name
|
||||||
if test_qualifier:
|
if test_qualifier:
|
||||||
report_name = report_name + "-" + test_qualifier
|
report_name = report_name + "-" + test_qualifier
|
||||||
output_path = os.path.join(self._root_path,
|
output_path = os.path.join(self._root_path,
|
||||||
self._COVERAGE_REPORT_PATH,
|
self._COVERAGE_REPORT_PATH,
|
||||||
test_suite.GetTargetName(),
|
target.GetName(),
|
||||||
report_name)
|
report_name)
|
||||||
|
|
||||||
coverage_local_name = "%s.%s" % (report_name,
|
coverage_local_name = "%s.%s" % (report_name,
|
||||||
@@ -97,15 +102,8 @@ class CoverageGenerator(object):
|
|||||||
|
|
||||||
report_path = os.path.join(output_path,
|
report_path = os.path.join(output_path,
|
||||||
report_name)
|
report_name)
|
||||||
target = self._targets_manifest.GetTarget(test_suite.GetTargetName())
|
return self._GenerateReport(report_path, coverage_local_path, [target],
|
||||||
if target is None:
|
do_src=True)
|
||||||
msg = ["Error: test %s references undefined target %s."
|
|
||||||
% (test_suite.GetName(), test_suite.GetTargetName())]
|
|
||||||
msg.append(" Ensure target is defined in %s" % self._TARGET_DEF_FILE)
|
|
||||||
logger.Log("".join(msg))
|
|
||||||
else:
|
|
||||||
return self._GenerateReport(report_path, coverage_local_path, [target],
|
|
||||||
do_src=True)
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _GenerateReport(self, report_path, coverage_file_path, targets,
|
def _GenerateReport(self, report_path, coverage_file_path, targets,
|
||||||
@@ -283,12 +281,34 @@ class CoverageGenerator(object):
|
|||||||
self._CombineTestCoverage()
|
self._CombineTestCoverage()
|
||||||
self._CombineTargetCoverage()
|
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():
|
def EnableCoverageBuild():
|
||||||
"""Enable building an Android target with code coverage instrumentation."""
|
"""Enable building an Android target with code coverage instrumentation."""
|
||||||
os.environ["EMMA_INSTRUMENT"] = "true"
|
os.environ["EMMA_INSTRUMENT"] = "true"
|
||||||
|
|
||||||
|
|
||||||
def Run():
|
def Run():
|
||||||
"""Does coverage operations based on command line args."""
|
"""Does coverage operations based on command line args."""
|
||||||
# TODO: do we want to support combining coverage for a single target
|
# TODO: do we want to support combining coverage for a single target
|
||||||
48
testrunner/coverage/coverage_target.py
Normal file
48
testrunner/coverage/coverage_target.py
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
#
|
||||||
|
# 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
|
||||||
|
|
||||||
@@ -3,130 +3,124 @@
|
|||||||
#
|
#
|
||||||
# Copyright 2008, The Android Open Source Project
|
# Copyright 2008, The Android Open Source Project
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
# You may obtain a copy of the License at
|
# You may obtain a copy of the License at
|
||||||
#
|
#
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
#
|
#
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
import xml.dom.minidom
|
import xml.dom.minidom
|
||||||
import xml.parsers
|
import xml.parsers
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
import coverage_target
|
||||||
import logger
|
import logger
|
||||||
import errors
|
import errors
|
||||||
|
|
||||||
class CoverageTargets:
|
class CoverageTargets:
|
||||||
"""Accessor for the code coverage target xml file
|
"""Accessor for the code coverage target xml file
|
||||||
Expects the following format:
|
Expects the following format:
|
||||||
<targets>
|
<targets>
|
||||||
<target
|
<target
|
||||||
name=""
|
name=""
|
||||||
type="JAVA_LIBRARIES|APPS"
|
type="JAVA_LIBRARIES|APPS"
|
||||||
build_path=""
|
build_path=""
|
||||||
|
|
||||||
[<src path=""/>] (0..*) - These are relative to build_path. If missing,
|
[<src path=""/>] (0..*) - These are relative to build_path. If missing,
|
||||||
assumes 'src'
|
assumes 'src'
|
||||||
>/target>
|
>/target>
|
||||||
|
|
||||||
TODO: add more format checking
|
TODO: add more format checking
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_TARGET_TAG_NAME = 'coverage_target'
|
_TARGET_TAG_NAME = 'coverage_target'
|
||||||
|
_NAME_ATTR = 'name'
|
||||||
|
_TYPE_ATTR = 'type'
|
||||||
|
_BUILD_ATTR = 'build_path'
|
||||||
|
_SRC_TAG = 'src'
|
||||||
|
_PATH_ATTR = 'path'
|
||||||
|
|
||||||
def __init__(self, ):
|
def __init__(self, ):
|
||||||
self._target_map= {}
|
self._target_map= {}
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return iter(self._target_map.values())
|
return iter(self._target_map.values()
|
||||||
|
|
||||||
def Parse(self, file_path):
|
def Parse(self, file_path):
|
||||||
"""Parse the coverage target data from from given file path, and add it to
|
"""Parse the coverage target data from from given file path, and add it to
|
||||||
the current object
|
the current object
|
||||||
Args:
|
Args:
|
||||||
file_path: absolute file path to parse
|
file_path: absolute file path to parse
|
||||||
Raises:
|
Raises:
|
||||||
errors.ParseError if file_path cannot be parsed
|
errors.ParseError if file_path cannot be parsed
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
doc = xml.dom.minidom.parse(file_path)
|
doc = xml.dom.minidom.parse(file_path)
|
||||||
except IOError:
|
except IOError:
|
||||||
# Error: The results file does not exist
|
# Error: The results file does not exist
|
||||||
logger.Log('Results file %s does not exist' % file_path)
|
logger.Log('Results file %s does not exist' % file_path)
|
||||||
raise errors.ParseError
|
raise errors.ParseError
|
||||||
except xml.parsers.expat.ExpatError:
|
except xml.parsers.expat.ExpatError:
|
||||||
logger.Log('Error Parsing xml file: %s ' % file_path)
|
logger.Log('Error Parsing xml file: %s ' % file_path)
|
||||||
raise errors.ParseError
|
raise errors.ParseError
|
||||||
|
|
||||||
target_elements = doc.getElementsByTagName(self._TARGET_TAG_NAME)
|
target_elements = doc.getElementsByTagName(self._TARGET_TAG_NAME)
|
||||||
|
|
||||||
for target_element in target_elements:
|
for target_element in target_elements:
|
||||||
target = CoverageTarget(target_element)
|
target = coverage_target.CoverageTarget()
|
||||||
|
self._ParseCoverageTarget(target, target_element)
|
||||||
self._AddTarget(target)
|
self._AddTarget(target)
|
||||||
|
|
||||||
def _AddTarget(self, target):
|
def _AddTarget(self, target):
|
||||||
self._target_map[target.GetName()] = target
|
self._target_map[target.GetName()] = target
|
||||||
|
|
||||||
def GetBuildTargets(self):
|
def GetBuildTargets(self):
|
||||||
""" returns list of target names """
|
""" returns list of target names """
|
||||||
build_targets = []
|
build_targets = []
|
||||||
for target in self:
|
for target in self:
|
||||||
build_targets.append(target.GetName())
|
build_targets.append(target.GetName())
|
||||||
return build_targets
|
return build_targets
|
||||||
|
|
||||||
def GetTargets(self):
|
def GetTargets(self):
|
||||||
""" returns list of CoverageTarget"""
|
""" returns list of CoverageTarget"""
|
||||||
return self._target_map.values()
|
return self._target_map.values()
|
||||||
|
|
||||||
def GetTarget(self, name):
|
def GetTarget(self, name):
|
||||||
""" returns CoverageTarget for given name. None if not found """
|
""" returns CoverageTarget for given name. None if not found """
|
||||||
try:
|
try:
|
||||||
return self._target_map[name]
|
return self._target_map[name]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
class CoverageTarget:
|
def _ParseCoverageTarget(self, target, target_element):
|
||||||
""" Represents one coverage target definition parsed from xml """
|
"""Parse coverage data from XML.
|
||||||
|
|
||||||
_NAME_ATTR = 'name'
|
Args:
|
||||||
_TYPE_ATTR = 'type'
|
target: the Coverage object to populate
|
||||||
_BUILD_ATTR = 'build_path'
|
target_element: the XML element to get data from
|
||||||
_SRC_TAG = 'src'
|
"""
|
||||||
_PATH_ATTR = 'path'
|
target.SetName(target_element.getAttribute(self._NAME_ATTR))
|
||||||
|
target.SetType(target_element.getAttribute(self._TYPE_ATTR))
|
||||||
def __init__(self, target_element):
|
target.SetBuildPath(target_element.getAttribute(self._BUILD_ATTR))
|
||||||
self._name = target_element.getAttribute(self._NAME_ATTR)
|
|
||||||
self._type = target_element.getAttribute(self._TYPE_ATTR)
|
|
||||||
self._build_path = target_element.getAttribute(self._BUILD_ATTR)
|
|
||||||
self._paths = []
|
self._paths = []
|
||||||
self._ParsePaths(target_element)
|
self._ParsePaths(target, target_element)
|
||||||
|
|
||||||
def GetName(self):
|
|
||||||
return self._name
|
|
||||||
|
|
||||||
def GetPaths(self):
|
def _ParsePaths(self, target, target_element):
|
||||||
return self._paths
|
|
||||||
|
|
||||||
def GetType(self):
|
|
||||||
return self._type
|
|
||||||
|
|
||||||
def GetBuildPath(self):
|
|
||||||
return self._build_path
|
|
||||||
|
|
||||||
def _ParsePaths(self, target_element):
|
|
||||||
src_elements = target_element.getElementsByTagName(self._SRC_TAG)
|
src_elements = target_element.getElementsByTagName(self._SRC_TAG)
|
||||||
if len(src_elements) <= 0:
|
if len(src_elements) <= 0:
|
||||||
# no src tags specified. Assume build_path + src
|
# no src tags specified. Assume build_path + src
|
||||||
self._paths.append(os.path.join(self.GetBuildPath(), "src"))
|
target.AddPath(os.path.join(target.GetBuildPath(), "src"))
|
||||||
for src_element in src_elements:
|
for src_element in src_elements:
|
||||||
rel_path = src_element.getAttribute(self._PATH_ATTR)
|
rel_path = src_element.getAttribute(self._PATH_ATTR)
|
||||||
self._paths.append(os.path.join(self.GetBuildPath(), rel_path))
|
target.AddPath(os.path.join(target.GetBuildPath(), rel_path))
|
||||||
|
|
||||||
|
|
||||||
def Parse(xml_file_path):
|
def Parse(xml_file_path):
|
||||||
"""parses out a file_path class from given path to xml"""
|
"""parses out a file_path class from given path to xml"""
|
||||||
targets = CoverageTargets()
|
targets = CoverageTargets()
|
||||||
116
testrunner/make_tree.py
Normal file
116
testrunner/make_tree.py
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
#
|
||||||
|
# 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
|
||||||
|
|
||||||
@@ -41,9 +41,10 @@ import time
|
|||||||
# local imports
|
# local imports
|
||||||
import adb_interface
|
import adb_interface
|
||||||
import android_build
|
import android_build
|
||||||
import coverage
|
from coverage import coverage
|
||||||
import errors
|
import errors
|
||||||
import logger
|
import logger
|
||||||
|
import make_tree
|
||||||
import run_command
|
import run_command
|
||||||
from test_defs import test_defs
|
from test_defs import test_defs
|
||||||
from test_defs import test_walker
|
from test_defs import test_walker
|
||||||
@@ -143,6 +144,9 @@ class TestRunner(object):
|
|||||||
parser.add_option("-o", "--coverage", dest="coverage",
|
parser.add_option("-o", "--coverage", dest="coverage",
|
||||||
default=False, action="store_true",
|
default=False, action="store_true",
|
||||||
help="Generate code coverage metrics for test(s)")
|
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("-x", "--path", dest="test_path",
|
parser.add_option("-x", "--path", dest="test_path",
|
||||||
help="Run test(s) at given file system path")
|
help="Run test(s) at given file system path")
|
||||||
parser.add_option("-t", "--all-tests", dest="all_tests",
|
parser.add_option("-t", "--all-tests", dest="all_tests",
|
||||||
@@ -191,6 +195,9 @@ class TestRunner(object):
|
|||||||
if self._options.verbose:
|
if self._options.verbose:
|
||||||
logger.SetVerbose(True)
|
logger.SetVerbose(True)
|
||||||
|
|
||||||
|
if self._options.coverage_target_path:
|
||||||
|
self._options.coverage = True
|
||||||
|
|
||||||
self._known_tests = self._ReadTests()
|
self._known_tests = self._ReadTests()
|
||||||
|
|
||||||
self._options.host_lib_path = android_build.GetHostLibraryPath()
|
self._options.host_lib_path = android_build.GetHostLibraryPath()
|
||||||
@@ -241,23 +248,24 @@ class TestRunner(object):
|
|||||||
self._TurnOffVerifier(tests)
|
self._TurnOffVerifier(tests)
|
||||||
self._DoFullBuild(tests)
|
self._DoFullBuild(tests)
|
||||||
|
|
||||||
target_set = []
|
target_tree = make_tree.MakeTree()
|
||||||
|
|
||||||
extra_args_set = []
|
extra_args_set = []
|
||||||
for test_suite in tests:
|
for test_suite in tests:
|
||||||
self._AddBuildTarget(test_suite, target_set, extra_args_set)
|
self._AddBuildTarget(test_suite, target_tree, extra_args_set)
|
||||||
|
|
||||||
if not self._options.preview:
|
if not self._options.preview:
|
||||||
self._adb.EnableAdbRoot()
|
self._adb.EnableAdbRoot()
|
||||||
else:
|
else:
|
||||||
logger.Log("adb root")
|
logger.Log("adb root")
|
||||||
if target_set:
|
|
||||||
|
if not target_tree.IsEmpty():
|
||||||
if self._options.coverage:
|
if self._options.coverage:
|
||||||
coverage.EnableCoverageBuild()
|
coverage.EnableCoverageBuild()
|
||||||
target_set.append("external/emma/Android.mk")
|
target_tree.AddPath("external/emma")
|
||||||
# TODO: detect if external/emma exists
|
|
||||||
|
|
||||||
target_build_string = " ".join(target_set)
|
target_list = target_tree.GetPrunedMakeList()
|
||||||
|
target_build_string = " ".join(target_list)
|
||||||
extra_args_string = " ".join(extra_args_set)
|
extra_args_string = " ".join(extra_args_set)
|
||||||
|
|
||||||
# mmm cannot be used from python, so perform a similar operation using
|
# mmm cannot be used from python, so perform a similar operation using
|
||||||
@@ -330,22 +338,18 @@ class TestRunner(object):
|
|||||||
os.chdir(old_dir)
|
os.chdir(old_dir)
|
||||||
self._DoInstall(output)
|
self._DoInstall(output)
|
||||||
|
|
||||||
def _AddBuildTarget(self, test_suite, target_set, extra_args_set):
|
def _AddBuildTarget(self, test_suite, target_tree, extra_args_set):
|
||||||
if not test_suite.IsFullMake():
|
if not test_suite.IsFullMake():
|
||||||
build_dir = test_suite.GetBuildPath()
|
build_dir = test_suite.GetBuildPath()
|
||||||
if self._AddBuildTargetPath(build_dir, target_set):
|
if self._AddBuildTargetPath(build_dir, target_tree):
|
||||||
extra_args_set.append(test_suite.GetExtraBuildArgs())
|
extra_args_set.append(test_suite.GetExtraBuildArgs())
|
||||||
for path in test_suite.GetBuildDependencies(self._options):
|
for path in test_suite.GetBuildDependencies(self._options):
|
||||||
self._AddBuildTargetPath(path, target_set)
|
self._AddBuildTargetPath(path, target_tree)
|
||||||
|
|
||||||
def _AddBuildTargetPath(self, build_dir, target_set):
|
def _AddBuildTargetPath(self, build_dir, target_tree):
|
||||||
if build_dir is not None:
|
if build_dir is not None:
|
||||||
build_file_path = os.path.join(build_dir, "Android.mk")
|
target_tree.AddPath(build_dir)
|
||||||
if os.path.isfile(os.path.join(self._root_path, build_file_path)):
|
return True
|
||||||
target_set.append(build_file_path)
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
logger.Log("%s has no Android.mk, skipping" % build_dir)
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _GetTestsToRun(self):
|
def _GetTestsToRun(self):
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import re
|
|||||||
|
|
||||||
# local imports
|
# local imports
|
||||||
import android_manifest
|
import android_manifest
|
||||||
import coverage
|
from coverage import coverage
|
||||||
import errors
|
import errors
|
||||||
import logger
|
import logger
|
||||||
import test_suite
|
import test_suite
|
||||||
@@ -84,6 +84,8 @@ class InstrumentationTestSuite(test_suite.AbstractTestSuite):
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
def GetBuildDependencies(self, options):
|
def GetBuildDependencies(self, options):
|
||||||
|
if options.coverage_target_path:
|
||||||
|
return [options.coverage_target_path]
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def Run(self, options, adb):
|
def Run(self, options, adb):
|
||||||
@@ -140,6 +142,10 @@ class InstrumentationTestSuite(test_suite.AbstractTestSuite):
|
|||||||
logger.Log(adb_cmd)
|
logger.Log(adb_cmd)
|
||||||
elif options.coverage:
|
elif options.coverage:
|
||||||
coverage_gen = coverage.CoverageGenerator(adb)
|
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)
|
self._CheckInstrumentationInstalled(adb)
|
||||||
# need to parse test output to determine path to coverage file
|
# need to parse test output to determine path to coverage file
|
||||||
logger.Log("Running in coverage mode, suppressing test output")
|
logger.Log("Running in coverage mode, suppressing test output")
|
||||||
@@ -158,7 +164,8 @@ class InstrumentationTestSuite(test_suite.AbstractTestSuite):
|
|||||||
return
|
return
|
||||||
|
|
||||||
coverage_file = coverage_gen.ExtractReport(
|
coverage_file = coverage_gen.ExtractReport(
|
||||||
self, device_coverage_path, test_qualifier=options.test_size)
|
self.GetName(), coverage_target, device_coverage_path,
|
||||||
|
test_qualifier=options.test_size)
|
||||||
if coverage_file is not None:
|
if coverage_file is not None:
|
||||||
logger.Log("Coverage report generated at %s" % coverage_file)
|
logger.Log("Coverage report generated at %s" % coverage_file)
|
||||||
|
|
||||||
@@ -171,10 +178,10 @@ class InstrumentationTestSuite(test_suite.AbstractTestSuite):
|
|||||||
instrumentation_args)
|
instrumentation_args)
|
||||||
|
|
||||||
def _CheckInstrumentationInstalled(self, adb):
|
def _CheckInstrumentationInstalled(self, adb):
|
||||||
if not adb.IsInstrumentationInstalled(self.GetPackageName(),
|
if not adb.IsInstrumentationInstalled(self.GetPackageName(),
|
||||||
self.GetRunnerName()):
|
self.GetRunnerName()):
|
||||||
msg=("Could not find instrumentation %s/%s on device. Try forcing a "
|
msg=("Could not find instrumentation %s/%s on device. Try forcing a "
|
||||||
"rebuild by updating a source file, and re-executing runtest." %
|
"rebuild by updating a source file, and re-executing runtest." %
|
||||||
(self.GetPackageName(), self.GetRunnerName()))
|
(self.GetPackageName(), self.GetRunnerName()))
|
||||||
raise errors.AbortError(msg=msg)
|
raise errors.AbortError(msg=msg)
|
||||||
|
|
||||||
|
|||||||
@@ -141,6 +141,9 @@ class TestWalker(object):
|
|||||||
else:
|
else:
|
||||||
tests.extend(self._CreateSuites(android_mk_parser, path,
|
tests.extend(self._CreateSuites(android_mk_parser, path,
|
||||||
upstream_build_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
|
# Try to build as much of original path as possible, so
|
||||||
# keep track of upper-most parent directory where Android.mk was found
|
# keep track of upper-most parent directory where Android.mk was found
|
||||||
# that has rule to build sub-directory makefiles.
|
# that has rule to build sub-directory makefiles.
|
||||||
@@ -148,7 +151,7 @@ class TestWalker(object):
|
|||||||
# ie if a test exists at 'foo' directory and 'foo/sub', attempting to
|
# ie if a test exists at 'foo' directory and 'foo/sub', attempting to
|
||||||
# build both 'foo' and 'foo/sub' will fail.
|
# build both 'foo' and 'foo/sub' will fail.
|
||||||
|
|
||||||
if android_mk_parser.HasInclude('call all-makefiles-under,$(LOCAL_PATH)'):
|
if android_mk_parser.IncludesMakefilesUnder():
|
||||||
# found rule to build sub-directories. The parent path can be used,
|
# found rule to build sub-directories. The parent path can be used,
|
||||||
# or if not set, use current path
|
# or if not set, use current path
|
||||||
if not upstream_build_path:
|
if not upstream_build_path:
|
||||||
|
|||||||
Reference in New Issue
Block a user