OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. |
| 5 # vim: set ts=2 sw=2 et sts=2 ai: |
| 6 # |
| 7 |
| 8 """Compiler version checking tool unit tests. |
| 9 |
| 10 Checks the tool correctly imports environment variables from the environment. |
| 11 """ |
| 12 |
| 13 import doctest |
| 14 import re |
| 15 import subprocess |
| 16 import sys |
| 17 import time |
| 18 import unittest |
| 19 |
| 20 import compiler_version |
| 21 |
| 22 |
| 23 class CallFake(dict): |
| 24 """Fake for the call function in compiler_version. |
| 25 |
| 26 You set the stdout and stderr to return on a given command line as follows; |
| 27 >>> fake_call = CallFake() |
| 28 >>> fake_call['.*regex.*'] = (0, 'stdout', 'stderr') |
| 29 >>> fake_call('cmdline matching regex') |
| 30 (0, 'stdout', 'stderr') |
| 31 |
| 32 The empty string will match any commadline; |
| 33 >>> fake_call = CallFake() |
| 34 >>> fake_call[''] = (0, 'stdout', 'stderr') |
| 35 >>> fake_call('cmdline matching regex') |
| 36 (0, 'stdout', 'stderr') |
| 37 |
| 38 You can have multiple options and the fake can be called multiple times; |
| 39 >>> fake_call = CallFake() |
| 40 >>> fake_call['.*regex one.*'] = (0, 'stdout 1', 'stderr 1') |
| 41 >>> fake_call['.*regex two.*'] = (1, 'stdout 2', 'stderr 2') |
| 42 >>> fake_call('cmdline matching regex one') |
| 43 (0, 'stdout 1', 'stderr 1') |
| 44 >>> fake_call('cmdline matching regex one') |
| 45 (0, 'stdout 1', 'stderr 1') |
| 46 >>> fake_call('cmdline matching regex two') |
| 47 (1, 'stdout 2', 'stderr 2') |
| 48 |
| 49 At least one regex must match the calling command line otherwise an |
| 50 AssertionError is thrown; |
| 51 >>> fake_call = CallFake() |
| 52 >>> fake_call['.*regex one.*'] = (0, 'stdout 1', 'stderr 1') |
| 53 >>> fake_call('non-matching cmdline') |
| 54 Traceback (most recent call last): |
| 55 ... |
| 56 AssertionError: No fakes matched command line 'non-matching cmdline' |
| 57 Possible matches where: |
| 58 '.*regex one.*' |
| 59 |
| 60 You should make sure only one option matches however! |
| 61 >>> fake_call = CallFake() |
| 62 >>> fake_call['.*match one.*'] = (0, 'stdout 1', 'stderr 1') |
| 63 >>> fake_call['.*match two.*'] = (0, 'stdout 2', 'stderr 2') |
| 64 >>> fake_call('match one and match two') |
| 65 Traceback (most recent call last): |
| 66 ... |
| 67 AssertionError: Multiple fakes matched command line 'match one and match two' |
| 68 '.*match two.*' |
| 69 '.*match one.*' |
| 70 """ |
| 71 |
| 72 def __setitem__(self, key, value): |
| 73 key_re = re.compile(key) |
| 74 assert isinstance(value, tuple) and len(value) == 3, "[%r] = %r" % ( |
| 75 key, value) |
| 76 dict.__setitem__(self, key_re, (value[0], value[1], value[2])) |
| 77 |
| 78 def __call__(self, cmdline): |
| 79 matches = [] |
| 80 for key in self: |
| 81 if key.search(cmdline): |
| 82 matches.append((key, self[key])) |
| 83 |
| 84 if len(matches) > 1: |
| 85 raise AssertionError("Multiple fakes matched command line %r\n%s" % ( |
| 86 cmdline, "\n".join("%r" % re.pattern for re, r in matches))) |
| 87 elif not matches: |
| 88 raise AssertionError( |
| 89 "No fakes matched command line %r\nPossible matches where:\n%s" % ( |
| 90 cmdline, "\n".join("%r" % re.pattern for re in self))) |
| 91 |
| 92 return matches[0][-1] |
| 93 |
| 94 |
| 95 class CallTest(unittest.TestCase): |
| 96 def test_timeout(self): |
| 97 starttime = time.time() |
| 98 compiler_version.call("sleep 5") |
| 99 endtime = time.time() |
| 100 self.assertLess(endtime - starttime, 1) |
| 101 |
| 102 def test_unknown(self): |
| 103 retcode, test_output, test_error = compiler_version.call( |
| 104 "command_that_doesnt_exist") |
| 105 self.assertEqual(retcode, 127) |
| 106 |
| 107 def test_true(self): |
| 108 retcode, test_output, test_error = compiler_version.call("true") |
| 109 self.assertEqual(retcode, 0) |
| 110 |
| 111 def test_false(self): |
| 112 retcode, test_output, test_error = compiler_version.call("false") |
| 113 self.assertEqual(retcode, 1) |
| 114 |
| 115 |
| 116 class VersionTestBase(unittest.TestCase): |
| 117 def check_version_strings(self, tool_string, call_match, version_strings): |
| 118 assert len(tool_string) |
| 119 assert len(call_match) |
| 120 assert len(version_strings) |
| 121 |
| 122 for version_string, expected_version in version_strings: |
| 123 call_fake = CallFake() |
| 124 call_fake[call_match] = (0, version_string, "") |
| 125 call_fake["^((?!%s).)*$" % call_match] = (-1, "STDOUT", "STDERR") |
| 126 |
| 127 actual_version = compiler_version.GetVersion( |
| 128 "COMPILER", tool_string, call=call_fake) |
| 129 self.assertEqual(actual_version, expected_version) |
| 130 |
| 131 |
| 132 class AssemblerTest(VersionTestBase): |
| 133 def test_clang_no_integrated_assembler(self): |
| 134 """Clang needs the no-integrated-assembler option to get system assembler. |
| 135 """ |
| 136 error = subprocess.CalledProcessError(1, "", "") |
| 137 |
| 138 # Older clang's use the "-no-integrated-as" option and fail otherwise. |
| 139 call_fake = CallFake() |
| 140 call_fake[".* -no-integrated-as .*"] = ( |
| 141 0, "GNU assembler (GNU Binutils) 2.24", "") |
| 142 call_fake["^((?!-no-integrated-as).)*$"] = ( |
| 143 -1, "STDOUT", "STDERR") |
| 144 self.assertEqual( |
| 145 "224", compiler_version.GetVersion( |
| 146 "COMPILER", "assembler", call=call_fake)) |
| 147 |
| 148 # Newer clang's use the "-fno-integrated-as" option and fail otherwise. |
| 149 call_fake = CallFake() |
| 150 call_fake[".* -fno-integrated-as .*"] = ( |
| 151 0, "GNU assembler (GNU Binutils for Ubuntu) 2.22", "") |
| 152 call_fake["^((?!-fno-integrated-as).)*$"] = ( |
| 153 -1, "STDOUT", "STDERR") |
| 154 self.assertEqual( |
| 155 "222", compiler_version.GetVersion( |
| 156 "COMPILER", "assembler", call=call_fake)) |
| 157 |
| 158 def test_version_strings_linux(self): |
| 159 """The assembler version string is customized by a number of distributions. |
| 160 """ |
| 161 self.check_version_strings("assembler", "-Xassembler -version", [ |
| 162 # Unmodified |
| 163 ("GNU assembler (GNU Binutils) 2.24", "224"), |
| 164 # Ubuntu short version |
| 165 ("GNU assembler (GNU Binutils for Ubuntu) 2.22", "222"), |
| 166 # Ubuntu long version |
| 167 ("GNU assembler version 2.22 (x86_64-linux-gnu) using BFD version (GNU B
inutils for Ubuntu) 2.22", "222"), # noqa |
| 168 # Fedora short version |
| 169 ("GNU assembler version 2.23.2", "223") |
| 170 ]) |
| 171 |
| 172 def test_version_strings_mac(self): |
| 173 call_fake = CallFake() |
| 174 call_fake["-Xassembler -v"] = ( |
| 175 0, "Apple Inc version cctools-836, GNU assembler version 1.38", "") |
| 176 call_fake["^((?!-Xassembler -v).)*$"] = (-1, "STDOUT", "STDERR") |
| 177 actual_version = compiler_version.GetVersion( |
| 178 "COMPILER", "assembler", call=call_fake) |
| 179 self.assertEqual(actual_version, "138") |
| 180 |
| 181 |
| 182 class LinkerTest(VersionTestBase): |
| 183 |
| 184 def test_version_strings_linux_bfd(self): |
| 185 self.check_version_strings("linker", "-Xlinker -v", [ |
| 186 ("GNU ld (GNU Binutils) 2.24", "224"), # Unmodified |
| 187 ("GNU ld (GNU Binutils for Ubuntu) 2.22", "222"), # Ubuntu |
| 188 ("GNU ld version 2.23.2", "223"), # Fedora |
| 189 ]) |
| 190 |
| 191 def test_version_strings_linux_gold(self): |
| 192 self.check_version_strings("linker", "-Xlinker -v", [ |
| 193 ("GNU gold (GNU Binutils 2.24) 1.11", "111"), # Unmodified |
| 194 ("GNU gold (GNU Binutils for Ubuntu 2.22) 1.9", "109"), # Ubuntu |
| 195 ("GNU gold (version 2.23.2) 1.10", "110"), # Fedora |
| 196 ]) |
| 197 |
| 198 @unittest.skip('mac not yet supported') |
| 199 def test_version_strings_mac(self): |
| 200 self.check_version_strings("linker", "-Xlinker -v", [ |
| 201 ("""\ |
| 202 @(#)PROGRAM:ld PROJECT:ld64-134.9 |
| 203 configured to support archs: armv6 armv7 armv7s i386 x86_64 |
| 204 LTO support using: LLVM version 3.1svn, from Apple Clang 4.1 (build 421.11.66) |
| 205 """, "") |
| 206 ]) |
| 207 |
| 208 |
| 209 class CompilerTest(VersionTestBase): |
| 210 def test_failure(self): |
| 211 call_fake = CallFake() |
| 212 call_fake[".*"] = (-1, "STDOUT", "STDERR") |
| 213 |
| 214 self.assertRaises( |
| 215 compiler_version.GetVersionError, |
| 216 compiler_version.GetVersion, "COMPILER", "compiler", call=call_fake) |
| 217 |
| 218 def test_version_strings_gcc(self): |
| 219 self.check_version_strings("compiler", ".*", [ |
| 220 ("""\ |
| 221 gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3 |
| 222 Copyright (C) 2011 Free Software Foundation, Inc. |
| 223 This is free software; see the source for copying conditions. There is NO |
| 224 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| 225 """, "46"), |
| 226 ("""\ |
| 227 gcc (Ubuntu/Linaro 4.8.2-2ubuntu1) 4.8.2 |
| 228 Copyright (C) 2011 Free Software Foundation, Inc. |
| 229 This is free software; see the source for copying conditions. There is NO |
| 230 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| 231 """, "48"), |
| 232 ]) |
| 233 |
| 234 def test_version_strings_clang(self): |
| 235 self.check_version_strings("compiler", ".*", [ |
| 236 # clang from third_party/llvm-build/Release+Asserts/bin/clang++ |
| 237 ("""\ |
| 238 clang version 3.5 (trunk 198389) |
| 239 Target: x86_64-unknown-linux-gnu |
| 240 Thread model: posix |
| 241 """, "35"), |
| 242 # clang on Mac OS X |
| 243 ("""\ |
| 244 Apple clang version 4.1 (tags/Apple/clang-421.11.66) (based on LLVM 3.1svn) |
| 245 Target: x86_64-apple-darwin13.1.0 |
| 246 Thread model: posix |
| 247 """, "41"), |
| 248 ]) |
| 249 |
| 250 |
| 251 # assertRegexpMatches is only available on Python 2.7 or newer |
| 252 @unittest.skipIf(sys.version_info < (2, 7, 0), 'Python 2.7 or newer required.') |
| 253 class RealTest(unittest.TestCase): |
| 254 """Test on the local system. |
| 255 |
| 256 Can't check the exact version, as we don't know what the system has. We do |
| 257 check that the version makes sense though. |
| 258 """ |
| 259 def test_compiler(self): |
| 260 version = compiler_version.GetVersion("c++", "compiler") |
| 261 self.assertRegexpMatches(version, "\d+") |
| 262 |
| 263 def test_linker(self): |
| 264 version = compiler_version.GetVersion("c++", "linker") |
| 265 self.assertRegexpMatches(version, "\d+") |
| 266 |
| 267 def test_assembler(self): |
| 268 version = compiler_version.GetVersion("c++", "assembler") |
| 269 self.assertRegexpMatches(version, "\d+") |
| 270 |
| 271 def test_thirdparty_clang(self): |
| 272 version = compiler_version.GetVersion( |
| 273 "third_party/llvm-build/Release+Asserts/bin/clang++", |
| 274 "compiler") |
| 275 self.assertRegexpMatches(version, "\d+") |
| 276 |
| 277 |
| 278 class EnvironmentTest(unittest.TestCase): |
| 279 def test_cxx(self): |
| 280 env = {} |
| 281 self.assertEqual(compiler_version.FindCompiler(env), "c++") |
| 282 |
| 283 env = {"CXX": "COMPILER"} |
| 284 self.assertEqual(compiler_version.FindCompiler(env), "COMPILER") |
| 285 |
| 286 def test_cxxflags(self): |
| 287 env = {"CXXFLAGS": "CXXFLAGS"} |
| 288 self.assertEqual(compiler_version.FindCompiler(env), "c++ CXXFLAGS") |
| 289 |
| 290 env = { |
| 291 "CXX": "COMPILER", |
| 292 "CXXFLAGS": "CXXFLAGS", |
| 293 } |
| 294 self.assertEqual(compiler_version.FindCompiler(env), "COMPILER CXXFLAGS") |
| 295 |
| 296 |
| 297 if __name__ == '__main__': |
| 298 import doctest |
| 299 doctest.testmod() |
| 300 |
| 301 # exit is only available on Python 2.7 or newer |
| 302 try: |
| 303 unittest.main(exit=False) |
| 304 except TypeError: |
| 305 unittest.main() |
| 306 |
| 307 print |
| 308 print "Your compiler is version:", |
| 309 compiler_version.main([]) |
| 310 print "Your linker is version:", |
| 311 compiler_version.main(["linker"]) |
| 312 print "Your assembler is version:", |
| 313 compiler_version.main(["assembler"]) |
OLD | NEW |