Home | History | Annotate | Download | only in test_defs
      1 #!/usr/bin/python2.4
      2 #
      3 #
      4 # Copyright 2008, The Android Open Source Project
      5 #
      6 # Licensed under the Apache License, Version 2.0 (the "License");
      7 # you may not use this file except in compliance with the License.
      8 # You may obtain a copy of the License at
      9 #
     10 #     http://www.apache.org/licenses/LICENSE-2.0
     11 #
     12 # Unless required by applicable law or agreed to in writing, software
     13 # distributed under the License is distributed on an "AS IS" BASIS,
     14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15 # See the License for the specific language governing permissions and
     16 # limitations under the License.
     17 
     18 """TestSuite definition for Android instrumentation tests."""
     19 
     20 import os
     21 import re
     22 
     23 # local imports
     24 import android_manifest
     25 import coverage
     26 import errors
     27 import logger
     28 import test_suite
     29 
     30 
     31 class InstrumentationTestSuite(test_suite.AbstractTestSuite):
     32   """Represents a java instrumentation test suite definition run on device."""
     33 
     34   DEFAULT_RUNNER = "android.test.InstrumentationTestRunner"
     35 
     36   # dependency on libcore (used for Emma)
     37   _LIBCORE_BUILD_PATH = "libcore"
     38 
     39   def __init__(self):
     40     test_suite.AbstractTestSuite.__init__(self)
     41     self._package_name = None
     42     self._runner_name = self.DEFAULT_RUNNER
     43     self._class_name = None
     44     self._target_name = None
     45     self._java_package = None
     46 
     47   def GetPackageName(self):
     48     return self._package_name
     49 
     50   def SetPackageName(self, package_name):
     51     self._package_name = package_name
     52     return self
     53 
     54   def GetRunnerName(self):
     55     return self._runner_name
     56 
     57   def SetRunnerName(self, runner_name):
     58     self._runner_name = runner_name
     59     return self
     60 
     61   def GetClassName(self):
     62     return self._class_name
     63 
     64   def SetClassName(self, class_name):
     65     self._class_name = class_name
     66     return self
     67 
     68   def GetJavaPackageFilter(self):
     69     return self._java_package
     70 
     71   def SetJavaPackageFilter(self, java_package_name):
     72     """Configure the suite to only run tests in given java package."""
     73     self._java_package = java_package_name
     74     return self
     75 
     76   def GetTargetName(self):
     77     """Retrieve module that this test is targeting.
     78 
     79     Used for generating code coverage metrics.
     80     Returns:
     81       the module target name
     82     """
     83     return self._target_name
     84 
     85   def SetTargetName(self, target_name):
     86     self._target_name = target_name
     87     return self
     88 
     89   def GetBuildDependencies(self, options):
     90     if options.coverage:
     91       return [self._LIBCORE_BUILD_PATH]
     92     return []
     93 
     94   def Run(self, options, adb):
     95     """Run the provided test suite.
     96 
     97     Builds up an adb instrument command using provided input arguments.
     98 
     99     Args:
    100       options: command line options to provide to test run
    101       adb: adb_interface to device under test
    102 
    103     Raises:
    104       errors.AbortError: if fatal error occurs
    105     """
    106 
    107     test_class = self.GetClassName()
    108     if options.test_class is not None:
    109       test_class = options.test_class.lstrip()
    110       if test_class.startswith("."):
    111         test_class = self.GetPackageName() + test_class
    112     if options.test_method is not None:
    113       test_class = "%s#%s" % (test_class, options.test_method)
    114 
    115     test_package = self.GetJavaPackageFilter()
    116     if options.test_package:
    117       test_package = options.test_package
    118 
    119     if test_class and test_package:
    120       logger.Log('Error: both class and java package options are specified')
    121 
    122     instrumentation_args = {}
    123     if test_class is not None:
    124       instrumentation_args["class"] = test_class
    125     if test_package:
    126       instrumentation_args["package"] = test_package
    127     if options.test_size:
    128       instrumentation_args["size"] = options.test_size
    129     if options.wait_for_debugger:
    130       instrumentation_args["debug"] = "true"
    131     if options.suite_assign_mode:
    132       instrumentation_args["suiteAssignment"] = "true"
    133     if options.coverage:
    134       instrumentation_args["coverage"] = "true"
    135     if options.test_annotation:
    136       instrumentation_args["annotation"] = options.test_annotation
    137     if options.test_not_annotation:
    138       instrumentation_args["notAnnotation"] = options.test_not_annotation
    139     if options.preview:
    140       adb_cmd = adb.PreviewInstrumentationCommand(
    141           package_name=self.GetPackageName(),
    142           runner_name=self.GetRunnerName(),
    143           raw_mode=options.raw_mode,
    144           instrumentation_args=instrumentation_args)
    145       logger.Log(adb_cmd)
    146     elif options.coverage:
    147       coverage_gen = coverage.CoverageGenerator(adb)
    148       adb.WaitForInstrumentation(self.GetPackageName(),
    149                                  self.GetRunnerName())
    150       # need to parse test output to determine path to coverage file
    151       logger.Log("Running in coverage mode, suppressing test output")
    152       try:
    153         (test_results, status_map) = adb.StartInstrumentationForPackage(
    154             package_name=self.GetPackageName(),
    155             runner_name=self.GetRunnerName(),
    156             timeout_time=60*60,
    157             instrumentation_args=instrumentation_args)
    158       except errors.InstrumentationError, errors.DeviceUnresponsiveError:
    159         return
    160       self._PrintTestResults(test_results)
    161       device_coverage_path = status_map.get("coverageFilePath", None)
    162       if device_coverage_path is None:
    163         logger.Log("Error: could not find coverage data on device")
    164         return
    165 
    166       coverage_file = coverage_gen.ExtractReport(
    167           self, device_coverage_path, test_qualifier=options.test_size)
    168       if coverage_file is not None:
    169         logger.Log("Coverage report generated at %s" % coverage_file)
    170     else:
    171       adb.WaitForInstrumentation(self.GetPackageName(),
    172                                  self.GetRunnerName())
    173       adb.StartInstrumentationNoResults(
    174           package_name=self.GetPackageName(),
    175           runner_name=self.GetRunnerName(),
    176           raw_mode=options.raw_mode,
    177           instrumentation_args=instrumentation_args)
    178 
    179   def _PrintTestResults(self, test_results):
    180     """Prints a summary of test result data to stdout.
    181 
    182     Args:
    183       test_results: a list of am_instrument_parser.TestResult
    184     """
    185     total_count = 0
    186     error_count = 0
    187     fail_count = 0
    188     for test_result in test_results:
    189       if test_result.GetStatusCode() == -1:  # error
    190         logger.Log("Error in %s: %s" % (test_result.GetTestName(),
    191                                         test_result.GetFailureReason()))
    192         error_count+=1
    193       elif test_result.GetStatusCode() == -2:  # failure
    194         logger.Log("Failure in %s: %s" % (test_result.GetTestName(),
    195                                           test_result.GetFailureReason()))
    196         fail_count+=1
    197       total_count+=1
    198     logger.Log("Tests run: %d, Failures: %d, Errors: %d" %
    199                (total_count, fail_count, error_count))
    200 
    201 
    202 def HasInstrumentationTest(path):
    203   """Determine if given path defines an instrumentation test.
    204 
    205   Args:
    206     path: file system path to instrumentation test.
    207   """
    208   manifest_parser = android_manifest.CreateAndroidManifest(path)
    209   if manifest_parser:
    210     return manifest_parser.GetInstrumentationNames()
    211   return False
    212 
    213 class InstrumentationTestFactory(test_suite.AbstractTestFactory):
    214   """A factory for creating InstrumentationTestSuites"""
    215 
    216   def __init__(self, test_root_path, build_path):
    217     test_suite.AbstractTestFactory.__init__(self, test_root_path,
    218                                             build_path)
    219 
    220   def CreateTests(self, sub_tests_path=None):
    221     """Create tests found in test_path.
    222 
    223     Will create a single InstrumentationTestSuite based on info found in
    224     AndroidManifest.xml found at build_path. Will set additional filters if
    225     test_path refers to a java package or java class.
    226     """
    227     tests = []
    228     class_name_arg = None
    229     java_package_name = None
    230     if sub_tests_path:
    231       # if path is java file, populate class name
    232       if self._IsJavaFile(sub_tests_path):
    233         class_name_arg = self._GetClassNameFromFile(sub_tests_path)
    234         logger.SilentLog('Using java test class %s' % class_name_arg)
    235       elif self._IsJavaPackage(sub_tests_path):
    236         java_package_name = self._GetPackageNameFromDir(sub_tests_path)
    237         logger.SilentLog('Using java package %s' % java_package_name)
    238     try:
    239       manifest_parser = android_manifest.AndroidManifest(app_path=
    240                                                          self.GetTestsRootPath())
    241       instrs = manifest_parser.GetInstrumentationNames()
    242       if not instrs:
    243         logger.Log('Could not find instrumentation declarations in %s at %s' %
    244                    (android_manifest.AndroidManifest.FILENAME,
    245                     self.GetBuildPath()))
    246         return tests
    247 
    248       for instr_name in manifest_parser.GetInstrumentationNames():
    249         pkg_name = manifest_parser.GetPackageName()
    250         if instr_name.find(".") < 0:
    251           instr_name = "." + instr_name
    252         logger.SilentLog('Found instrumentation %s/%s' % (pkg_name, instr_name))
    253         suite = InstrumentationTestSuite()
    254         suite.SetPackageName(pkg_name)
    255         suite.SetBuildPath(self.GetBuildPath())
    256         suite.SetRunnerName(instr_name)
    257         suite.SetName(pkg_name)
    258         suite.SetClassName(class_name_arg)
    259         suite.SetJavaPackageFilter(java_package_name)
    260         # this is a bit of a hack, assume if 'com.android.cts' is in
    261         # package name, this is a cts test
    262         # this logic can be removed altogether when cts tests no longer require
    263         # custom build steps
    264         if suite.GetPackageName().startswith('com.android.cts'):
    265           suite.SetSuite('cts')
    266         tests.append(suite)
    267       return tests
    268 
    269     except:
    270       logger.Log('Could not find or parse %s at %s' %
    271                  (android_manifest.AndroidManifest.FILENAME,
    272                   self.GetBuildPath()))
    273     return tests
    274 
    275   def _IsJavaFile(self, path):
    276     """Returns true if given file system path is a java file."""
    277     return os.path.isfile(path) and self._IsJavaFileName(path)
    278 
    279   def _IsJavaFileName(self, filename):
    280     """Returns true if given file name is a java file name."""
    281     return os.path.splitext(filename)[1] == '.java'
    282 
    283   def _IsJavaPackage(self, path):
    284     """Returns true if given file path is a java package.
    285 
    286     Currently assumes if any java file exists in this directory, than it
    287     represents a java package.
    288 
    289     Args:
    290       path: file system path of directory to check
    291 
    292     Returns:
    293       True if path is a java package
    294     """
    295     if not os.path.isdir(path):
    296       return False
    297     for file_name in os.listdir(path):
    298       if self._IsJavaFileName(file_name):
    299         return True
    300     return False
    301 
    302   def _GetClassNameFromFile(self, java_file_path):
    303     """Gets the fully qualified java class name from path.
    304 
    305     Args:
    306       java_file_path: file system path of java file
    307 
    308     Returns:
    309       fully qualified java class name or None.
    310     """
    311     package_name = self._GetPackageNameFromFile(java_file_path)
    312     if package_name:
    313       filename = os.path.basename(java_file_path)
    314       class_name = os.path.splitext(filename)[0]
    315       return '%s.%s' % (package_name, class_name)
    316     return None
    317 
    318   def _GetPackageNameFromDir(self, path):
    319     """Gets the java package name associated with given directory path.
    320 
    321     Caveat: currently just parses defined java package name from first java
    322     file found in directory.
    323 
    324     Args:
    325       path: file system path of directory
    326 
    327     Returns:
    328       the java package name or None
    329     """
    330     for filename in os.listdir(path):
    331       if self._IsJavaFileName(filename):
    332         return self._GetPackageNameFromFile(os.path.join(path, filename))
    333 
    334   def _GetPackageNameFromFile(self, java_file_path):
    335     """Gets the java package name associated with given java file path.
    336 
    337     Args:
    338       java_file_path: file system path of java file
    339 
    340     Returns:
    341       the java package name or None
    342     """
    343     logger.SilentLog('Looking for java package name in %s' % java_file_path)
    344     re_package = re.compile(r'package\s+(.*);')
    345     file_handle = open(java_file_path, 'r')
    346     for line in file_handle:
    347       match = re_package.match(line)
    348       if match:
    349         return match.group(1)
    350     return None
    351