|
1 #!/usr/bin/env python |
|
2 # Copyright (c) 2009 Google Inc. All rights reserved. |
|
3 # Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org) |
|
4 # |
|
5 # Redistribution and use in source and binary forms, with or without |
|
6 # modification, are permitted provided that the following conditions are |
|
7 # met: |
|
8 # |
|
9 # * Redistributions of source code must retain the above copyright |
|
10 # notice, this list of conditions and the following disclaimer. |
|
11 # * Redistributions in binary form must reproduce the above |
|
12 # copyright notice, this list of conditions and the following disclaimer |
|
13 # in the documentation and/or other materials provided with the |
|
14 # distribution. |
|
15 # * Neither the name of Google Inc. nor the names of its |
|
16 # contributors may be used to endorse or promote products derived from |
|
17 # this software without specific prior written permission. |
|
18 # |
|
19 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
20 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
21 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
22 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
23 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
24 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
25 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
26 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
27 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
28 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
29 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
30 |
|
31 import logging |
|
32 import os |
|
33 import sys |
|
34 |
|
35 # Do not import anything from webkitpy prior to cleaning webkitpy of |
|
36 # orphaned *.pyc files. This ensures that no orphaned *.pyc files are |
|
37 # accidentally imported during the course of this script. |
|
38 # |
|
39 # Also, do not import or execute any Python code incompatible with |
|
40 # Python 2.4 until after execution of the init() method below. |
|
41 |
|
42 |
|
43 _log = logging.getLogger("test-webkitpy") |
|
44 |
|
45 |
|
46 # Verbose logging is useful for debugging test-webkitpy code that runs |
|
47 # before the actual unit tests -- things like autoinstall downloading and |
|
48 # unit-test auto-detection logic. This is different from verbose logging |
|
49 # of the unit tests themselves (i.e. the unittest module's --verbose flag). |
|
50 def configure_logging(is_verbose_logging): |
|
51 """Configure the root logger. |
|
52 |
|
53 Configure the root logger not to log any messages from webkitpy -- |
|
54 except for messages from the autoinstall module. Also set the |
|
55 logging level as described below. |
|
56 |
|
57 Args: |
|
58 is_verbose_logging: A boolean value of whether logging should be |
|
59 verbose. If this parameter is true, the logging |
|
60 level for the handler on the root logger is set to |
|
61 logging.DEBUG. Otherwise, it is set to logging.INFO. |
|
62 |
|
63 """ |
|
64 # Don't use the Python ternary operator here so that this method will |
|
65 # work with Python 2.4. |
|
66 if is_verbose_logging: |
|
67 logging_level = logging.DEBUG |
|
68 else: |
|
69 logging_level = logging.INFO |
|
70 |
|
71 handler = logging.StreamHandler(sys.stderr) |
|
72 # We constrain the level on the handler rather than on the root |
|
73 # logger itself. This is probably better because the handler is |
|
74 # configured and known only to this module, whereas the root logger |
|
75 # is an object shared (and potentially modified) by many modules. |
|
76 # Modifying the handler, then, is less intrusive and less likely to |
|
77 # interfere with modifications made by other modules (e.g. in unit |
|
78 # tests). |
|
79 handler.setLevel(logging_level) |
|
80 formatter = logging.Formatter("%(name)s: %(levelname)-8s %(message)s") |
|
81 handler.setFormatter(formatter) |
|
82 |
|
83 logger = logging.getLogger() |
|
84 logger.addHandler(handler) |
|
85 logger.setLevel(logging.NOTSET) |
|
86 |
|
87 # Filter out most webkitpy messages. |
|
88 # |
|
89 # Messages can be selectively re-enabled for this script by updating |
|
90 # this method accordingly. |
|
91 def filter(record): |
|
92 """Filter out autoinstall and non-third-party webkitpy messages.""" |
|
93 # FIXME: Figure out a way not to use strings here, for example by |
|
94 # using syntax like webkitpy.test.__name__. We want to be |
|
95 # sure not to import any non-Python 2.4 code, though, until |
|
96 # after the version-checking code has executed. |
|
97 if (record.name.startswith("webkitpy.common.system.autoinstall") or |
|
98 record.name.startswith("webkitpy.test")): |
|
99 return True |
|
100 if record.name.startswith("webkitpy"): |
|
101 return False |
|
102 return True |
|
103 |
|
104 testing_filter = logging.Filter() |
|
105 testing_filter.filter = filter |
|
106 |
|
107 # Display a message so developers are not mystified as to why |
|
108 # logging does not work in the unit tests. |
|
109 _log.info("Suppressing most webkitpy logging while running unit tests.") |
|
110 handler.addFilter(testing_filter) |
|
111 |
|
112 |
|
113 def _clean_pyc_files(dir_to_clean, paths_not_to_log): |
|
114 """Delete from a directory all .pyc files that have no .py file. |
|
115 |
|
116 Args: |
|
117 dir_to_clean: The path to the directory to clean. |
|
118 paths_not_to_log: A list of paths to .pyc files whose deletions should |
|
119 not be logged. This list should normally include |
|
120 only test .pyc files. |
|
121 |
|
122 """ |
|
123 _log.debug("Cleaning orphaned *.pyc files from: %s" % dir_to_clean) |
|
124 |
|
125 # Normalize paths not to log. |
|
126 paths_not_to_log = [os.path.abspath(path) for path in paths_not_to_log] |
|
127 |
|
128 for dir_path, dir_names, file_names in os.walk(dir_to_clean): |
|
129 for file_name in file_names: |
|
130 if file_name.endswith(".pyc") and file_name[:-1] not in file_names: |
|
131 file_path = os.path.join(dir_path, file_name) |
|
132 if os.path.abspath(file_path) not in paths_not_to_log: |
|
133 _log.info("Deleting orphan *.pyc file: %s" % file_path) |
|
134 os.remove(file_path) |
|
135 |
|
136 |
|
137 # As a substitute for a unit test, this method tests _clean_pyc_files() |
|
138 # in addition to calling it. We chose not to use the unittest module |
|
139 # because _clean_pyc_files() is called only once and is not used elsewhere. |
|
140 def _clean_webkitpy_with_test(): |
|
141 webkitpy_dir = os.path.join(os.path.dirname(__file__), "webkitpy") |
|
142 |
|
143 # The test .pyc file is-- |
|
144 # webkitpy/python24/TEMP_test-webkitpy_test_pyc_file.pyc. |
|
145 test_path = os.path.join(webkitpy_dir, "python24", |
|
146 "TEMP_test-webkitpy_test_pyc_file.pyc") |
|
147 |
|
148 test_file = open(test_path, "w") |
|
149 try: |
|
150 test_file.write("Test .pyc file generated by test-webkitpy.") |
|
151 finally: |
|
152 test_file.close() |
|
153 |
|
154 # Confirm that the test file exists so that when we check that it does |
|
155 # not exist, the result is meaningful. |
|
156 if not os.path.exists(test_path): |
|
157 raise Exception("Test .pyc file not created: %s" % test_path) |
|
158 |
|
159 _clean_pyc_files(webkitpy_dir, [test_path]) |
|
160 |
|
161 if os.path.exists(test_path): |
|
162 raise Exception("Test .pyc file not deleted: %s" % test_path) |
|
163 |
|
164 |
|
165 def init(command_args): |
|
166 """Execute code prior to importing from webkitpy.unittests. |
|
167 |
|
168 Args: |
|
169 command_args: The list of command-line arguments -- usually |
|
170 sys.argv[1:]. |
|
171 |
|
172 """ |
|
173 verbose_logging_flag = "--verbose-logging" |
|
174 is_verbose_logging = verbose_logging_flag in command_args |
|
175 if is_verbose_logging: |
|
176 # Remove the flag so it doesn't cause unittest.main() to error out. |
|
177 # |
|
178 # FIXME: Get documentation for the --verbose-logging flag to show |
|
179 # up in the usage instructions, which are currently generated |
|
180 # by unittest.main(). It's possible that this will require |
|
181 # re-implementing the option parser for unittest.main() |
|
182 # since there may not be an easy way to modify its existing |
|
183 # option parser. |
|
184 sys.argv.remove(verbose_logging_flag) |
|
185 |
|
186 configure_logging(is_verbose_logging) |
|
187 _log.debug("Verbose WebKit logging enabled.") |
|
188 |
|
189 # We clean orphaned *.pyc files from webkitpy prior to importing from |
|
190 # webkitpy to make sure that no import statements falsely succeed. |
|
191 # This helps to check that import statements have been updated correctly |
|
192 # after any file moves. Otherwise, incorrect import statements can |
|
193 # be masked. |
|
194 # |
|
195 # For example, if webkitpy/python24/versioning.py were moved to a |
|
196 # different location without changing any import statements, and if |
|
197 # the corresponding .pyc file were left behind without deleting it, |
|
198 # then "import webkitpy.python24.versioning" would continue to succeed |
|
199 # even though it would fail for someone checking out a fresh copy |
|
200 # of the source tree. This is because of a Python feature: |
|
201 # |
|
202 # "It is possible to have a file called spam.pyc (or spam.pyo when -O |
|
203 # is used) without a file spam.py for the same module. This can be used |
|
204 # to distribute a library of Python code in a form that is moderately |
|
205 # hard to reverse engineer." |
|
206 # |
|
207 # ( http://docs.python.org/tutorial/modules.html#compiled-python-files ) |
|
208 # |
|
209 # Deleting the orphaned .pyc file prior to importing, however, would |
|
210 # cause an ImportError to occur on import as desired. |
|
211 _clean_webkitpy_with_test() |
|
212 |
|
213 import webkitpy.python24.versioning as versioning |
|
214 |
|
215 versioning.check_version(log=_log) |
|
216 |
|
217 (comparison, current_version, minimum_version) = \ |
|
218 versioning.compare_version() |
|
219 |
|
220 if comparison > 0: |
|
221 # Then the current version is later than the minimum version. |
|
222 message = ("You are testing webkitpy with a Python version (%s) " |
|
223 "higher than the minimum version (%s) it was meant " |
|
224 "to support." % (current_version, minimum_version)) |
|
225 _log.warn(message) |
|
226 |
|
227 |
|
228 if __name__ == "__main__": |
|
229 |
|
230 init(sys.argv[1:]) |
|
231 |
|
232 # We import the unit test code after init() to ensure that any |
|
233 # Python version warnings are displayed in case an error occurs |
|
234 # while interpreting webkitpy.unittests. This also allows |
|
235 # logging to be configured prior to importing -- for example to |
|
236 # enable the display of autoinstall logging.log messages while |
|
237 # running the unit tests. |
|
238 from webkitpy.test.main import Tester |
|
239 |
|
240 Tester().run_tests(sys.argv) |