You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
263 lines
8.6 KiB
263 lines
8.6 KiB
#!/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 []
|
|
|