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