Upstream version 11.40.277.0
[platform/framework/web/crosswalk.git] / src / build / android / pylib / host_driven / test_case.py
1 # Copyright 2013 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 """Base class for host-driven test cases.
6
7 This test case is intended to serve as the base class for any host-driven
8 test cases. It is similar to the Python unitttest module in that test cases
9 inherit from this class and add methods which will be run as tests.
10
11 When a HostDrivenTestCase object is instantiated, its purpose is to run only one
12 test method in the derived class. The test runner gives it the name of the test
13 method the instance will run. The test runner calls SetUp with the device ID
14 which the test method will run against. The test runner runs the test method
15 itself, collecting the result, and calls TearDown.
16
17 Tests can perform arbitrary Python commands and asserts in test methods. Tests
18 that run instrumentation tests can make use of the _RunJavaTestFilters helper
19 function to trigger Java tests and convert results into a single host-driven
20 test result.
21 """
22
23 import logging
24 import os
25 import time
26
27 from pylib import constants
28 from pylib import forwarder
29 from pylib import valgrind_tools
30 from pylib.base import base_test_result
31 from pylib.device import device_utils
32 from pylib.instrumentation import test_package
33 from pylib.instrumentation import test_result
34 from pylib.instrumentation import test_runner
35
36 # aka the parent of com.google.android
37 BASE_ROOT = 'src' + os.sep
38
39
40 class HostDrivenTestCase(object):
41   """Base class for host-driven test cases."""
42
43   _HOST_DRIVEN_TAG = 'HostDriven'
44
45   def __init__(self, test_name, instrumentation_options=None):
46     """Create a test case initialized to run |test_name|.
47
48     Args:
49       test_name: The name of the method to run as the test.
50       instrumentation_options: An InstrumentationOptions object.
51     """
52     class_name = self.__class__.__name__
53     self.adb = None
54     self.cleanup_test_files = False
55     self.device = None
56     self.device_id = ''
57     self.has_forwarded_ports = False
58     self.instrumentation_options = instrumentation_options
59     self.ports_to_forward = []
60     self.shard_index = 0
61
62     # Use tagged_name when creating results, so that we can identify host-driven
63     # tests in the overall results.
64     self.test_name = test_name
65     self.qualified_name = '%s.%s' % (class_name, self.test_name)
66     self.tagged_name = '%s_%s' % (self._HOST_DRIVEN_TAG, self.qualified_name)
67
68   # TODO(bulach): make ports_to_forward not optional and move the Forwarder
69   # mapping here.
70   def SetUp(self, device, shard_index,
71             cleanup_test_files, ports_to_forward=None):
72     if not ports_to_forward:
73       ports_to_forward = []
74     self.device_id = device
75     self.shard_index = shard_index
76     self.device = device_utils.DeviceUtils(self.device_id)
77     self.adb = self.device.old_interface
78     self.cleanup_test_files = cleanup_test_files
79     if ports_to_forward:
80       self.ports_to_forward = ports_to_forward
81
82   def TearDown(self):
83     pass
84
85   # TODO(craigdh): Remove GetOutDir once references have been removed
86   # downstream.
87   @staticmethod
88   def GetOutDir():
89     return constants.GetOutDirectory()
90
91   def Run(self):
92     logging.info('Running host-driven test: %s', self.tagged_name)
93     # Get the test method on the derived class and execute it
94     return getattr(self, self.test_name)()
95
96   @staticmethod
97   def __GetHostForwarderLog():
98     return ('-- Begin Full HostForwarder log\n'
99             '%s\n'
100             '--End Full HostForwarder log\n' % forwarder.Forwarder.GetHostLog())
101
102   def __StartForwarder(self):
103     logging.warning('Forwarding %s %s', self.ports_to_forward,
104                     self.has_forwarded_ports)
105     if self.ports_to_forward and not self.has_forwarded_ports:
106       self.has_forwarded_ports = True
107       tool = valgrind_tools.CreateTool(None, self.device)
108       forwarder.Forwarder.Map([(port, port) for port in self.ports_to_forward],
109                               self.device, tool)
110
111   def __RunJavaTest(self, test, test_pkg, additional_flags=None):
112     """Runs a single Java test in a Java TestRunner.
113
114     Args:
115       test: Fully qualified test name (ex. foo.bar.TestClass#testMethod)
116       test_pkg: TestPackage object.
117       additional_flags: A list of additional flags to add to the command line.
118
119     Returns:
120       TestRunResults object with a single test result.
121     """
122     # TODO(bulach): move this to SetUp() stage.
123     self.__StartForwarder()
124
125     java_test_runner = test_runner.TestRunner(self.instrumentation_options,
126                                               self.device_id,
127                                               self.shard_index, test_pkg,
128                                               additional_flags=additional_flags)
129     try:
130       java_test_runner.SetUp()
131       return java_test_runner.RunTest(test)[0]
132     finally:
133       java_test_runner.TearDown()
134
135   def _RunJavaTestFilters(self, test_filters, additional_flags=None):
136     """Calls a list of tests and stops at the first test failure.
137
138     This method iterates until either it encounters a non-passing test or it
139     exhausts the list of tests. Then it returns the appropriate overall result.
140
141     Test cases may make use of this method internally to assist in running
142     instrumentation tests. This function relies on instrumentation_options
143     being defined.
144
145     Args:
146       test_filters: A list of Java test filters.
147       additional_flags: A list of addition flags to add to the command line.
148
149     Returns:
150       A TestRunResults object containing an overall result for this set of Java
151       tests. If any Java tests do not pass, this is a fail overall.
152     """
153     test_type = base_test_result.ResultType.PASS
154     log = ''
155
156     test_pkg = test_package.TestPackage(
157         self.instrumentation_options.test_apk_path,
158         self.instrumentation_options.test_apk_jar_path,
159         self.instrumentation_options.test_support_apk_path)
160
161     start_ms = int(time.time()) * 1000
162     done = False
163     for test_filter in test_filters:
164       tests = test_pkg.GetAllMatchingTests(None, None, test_filter)
165       # Filters should always result in >= 1 test.
166       if len(tests) == 0:
167         raise Exception('Java test filter "%s" returned no tests.'
168                         % test_filter)
169       for test in tests:
170         # We're only running one test at a time, so this TestRunResults object
171         # will hold only one result.
172         java_result = self.__RunJavaTest(test, test_pkg, additional_flags)
173         assert len(java_result.GetAll()) == 1
174         if not java_result.DidRunPass():
175           result = java_result.GetNotPass().pop()
176           log = result.GetLog()
177           log += self.__GetHostForwarderLog()
178           test_type = result.GetType()
179           done = True
180           break
181       if done:
182         break
183     duration_ms = int(time.time()) * 1000 - start_ms
184
185     overall_result = base_test_result.TestRunResults()
186     overall_result.AddResult(
187         test_result.InstrumentationTestResult(
188             self.tagged_name, test_type, start_ms, duration_ms, log=log))
189     return overall_result
190
191   def __str__(self):
192     return self.tagged_name
193
194   def __repr__(self):
195     return self.tagged_name