1from __future__ import print_function, division, absolute_import 2from fontTools.misc.py23 import * 3from fontTools.misc.textTools import deHexStr 4import filecmp 5import tempfile 6from subprocess import check_call 7import sys 8import os 9import unittest 10 11from fontTools.misc.py23 import ( 12 round2, round3, isclose, redirect_stdout, redirect_stderr) 13 14 15PIPE_SCRIPT = """\ 16import sys 17binary_stdin = open(sys.stdin.fileno(), mode='rb', closefd=False) 18binary_stdout = open(sys.stdout.fileno(), mode='wb', closefd=False) 19binary_stdout.write(binary_stdin.read()) 20""" 21 22# the string contains a mix of line endings, plus the Win "EOF" charater (0x1A) 23# 'hello\rworld\r\n\x1a\r\n' 24TEST_BIN_DATA = deHexStr( 25 "68 65 6c 6c 6f 0d 77 6f 72 6c 64 0d 0a 1a 0d 0a" 26) 27 28class OpenFuncWrapperTest(unittest.TestCase): 29 30 @staticmethod 31 def make_temp(data): 32 with tempfile.NamedTemporaryFile(delete=False) as f: 33 f.write(tobytes(data)) 34 return f.name 35 36 def diff_piped(self, data, import_statement): 37 script = self.make_temp("\n".join([import_statement, PIPE_SCRIPT])) 38 datafile = self.make_temp(data) 39 try: 40 with open(datafile, 'rb') as infile, \ 41 tempfile.NamedTemporaryFile(delete=False) as outfile: 42 env = dict(os.environ) 43 env["PYTHONPATH"] = os.pathsep.join(sys.path) 44 check_call( 45 [sys.executable, script], stdin=infile, stdout=outfile, 46 env=env) 47 result = not filecmp.cmp(infile.name, outfile.name, shallow=False) 48 finally: 49 os.remove(script) 50 os.remove(datafile) 51 os.remove(outfile.name) 52 return result 53 54 def test_binary_pipe_py23_open_wrapper(self): 55 if self.diff_piped( 56 TEST_BIN_DATA, "from fontTools.misc.py23 import open"): 57 self.fail("Input and output data differ!") 58 59 def test_binary_pipe_built_in_io_open(self): 60 if sys.version_info.major < 3 and sys.platform == 'win32': 61 # On Windows Python 2.x, the piped input and output data are 62 # expected to be different when using io.open, because of issue 63 # https://bugs.python.org/issue10841. 64 expected = True 65 else: 66 expected = False 67 result = self.diff_piped(TEST_BIN_DATA, "from io import open") 68 self.assertEqual(result, expected) 69 70 71class Round2Test(unittest.TestCase): 72 """ 73 Test cases taken from cpython 2.7 test suite: 74 75 https://github.com/python/cpython/blob/2.7/Lib/test/test_float.py#L748 76 77 Excludes the test cases that are not supported when using the `decimal` 78 module's `quantize` method. 79 """ 80 81 def test_second_argument_type(self): 82 # floats should be illegal 83 self.assertRaises(TypeError, round2, 3.14159, 2.0) 84 85 def test_halfway_cases(self): 86 # Halfway cases need special attention, since the current 87 # implementation has to deal with them specially. Note that 88 # 2.x rounds halfway values up (i.e., away from zero) while 89 # 3.x does round-half-to-even. 90 self.assertAlmostEqual(round2(0.125, 2), 0.13) 91 self.assertAlmostEqual(round2(0.375, 2), 0.38) 92 self.assertAlmostEqual(round2(0.625, 2), 0.63) 93 self.assertAlmostEqual(round2(0.875, 2), 0.88) 94 self.assertAlmostEqual(round2(-0.125, 2), -0.13) 95 self.assertAlmostEqual(round2(-0.375, 2), -0.38) 96 self.assertAlmostEqual(round2(-0.625, 2), -0.63) 97 self.assertAlmostEqual(round2(-0.875, 2), -0.88) 98 99 self.assertAlmostEqual(round2(0.25, 1), 0.3) 100 self.assertAlmostEqual(round2(0.75, 1), 0.8) 101 self.assertAlmostEqual(round2(-0.25, 1), -0.3) 102 self.assertAlmostEqual(round2(-0.75, 1), -0.8) 103 104 self.assertEqual(round2(-6.5, 0), -7.0) 105 self.assertEqual(round2(-5.5, 0), -6.0) 106 self.assertEqual(round2(-1.5, 0), -2.0) 107 self.assertEqual(round2(-0.5, 0), -1.0) 108 self.assertEqual(round2(0.5, 0), 1.0) 109 self.assertEqual(round2(1.5, 0), 2.0) 110 self.assertEqual(round2(2.5, 0), 3.0) 111 self.assertEqual(round2(3.5, 0), 4.0) 112 self.assertEqual(round2(4.5, 0), 5.0) 113 self.assertEqual(round2(5.5, 0), 6.0) 114 self.assertEqual(round2(6.5, 0), 7.0) 115 116 # same but without an explicit second argument; in 3.x these 117 # will give integers 118 self.assertEqual(round2(-6.5), -7.0) 119 self.assertEqual(round2(-5.5), -6.0) 120 self.assertEqual(round2(-1.5), -2.0) 121 self.assertEqual(round2(-0.5), -1.0) 122 self.assertEqual(round2(0.5), 1.0) 123 self.assertEqual(round2(1.5), 2.0) 124 self.assertEqual(round2(2.5), 3.0) 125 self.assertEqual(round2(3.5), 4.0) 126 self.assertEqual(round2(4.5), 5.0) 127 self.assertEqual(round2(5.5), 6.0) 128 self.assertEqual(round2(6.5), 7.0) 129 130 self.assertEqual(round2(-25.0, -1), -30.0) 131 self.assertEqual(round2(-15.0, -1), -20.0) 132 self.assertEqual(round2(-5.0, -1), -10.0) 133 self.assertEqual(round2(5.0, -1), 10.0) 134 self.assertEqual(round2(15.0, -1), 20.0) 135 self.assertEqual(round2(25.0, -1), 30.0) 136 self.assertEqual(round2(35.0, -1), 40.0) 137 self.assertEqual(round2(45.0, -1), 50.0) 138 self.assertEqual(round2(55.0, -1), 60.0) 139 self.assertEqual(round2(65.0, -1), 70.0) 140 self.assertEqual(round2(75.0, -1), 80.0) 141 self.assertEqual(round2(85.0, -1), 90.0) 142 self.assertEqual(round2(95.0, -1), 100.0) 143 self.assertEqual(round2(12325.0, -1), 12330.0) 144 self.assertEqual(round2(0, -1), 0.0) 145 146 self.assertEqual(round2(350.0, -2), 400.0) 147 self.assertEqual(round2(450.0, -2), 500.0) 148 149 self.assertAlmostEqual(round2(0.5e21, -21), 1e21) 150 self.assertAlmostEqual(round2(1.5e21, -21), 2e21) 151 self.assertAlmostEqual(round2(2.5e21, -21), 3e21) 152 self.assertAlmostEqual(round2(5.5e21, -21), 6e21) 153 self.assertAlmostEqual(round2(8.5e21, -21), 9e21) 154 155 self.assertAlmostEqual(round2(-1.5e22, -22), -2e22) 156 self.assertAlmostEqual(round2(-0.5e22, -22), -1e22) 157 self.assertAlmostEqual(round2(0.5e22, -22), 1e22) 158 self.assertAlmostEqual(round2(1.5e22, -22), 2e22) 159 160 161class Round3Test(unittest.TestCase): 162 """ Same as above but results adapted for Python 3 round() """ 163 164 def test_second_argument_type(self): 165 # floats should be illegal 166 self.assertRaises(TypeError, round3, 3.14159, 2.0) 167 168 # None should be allowed 169 self.assertEqual(round3(1.0, None), 1) 170 # the following would raise an error with the built-in Python3.5 round: 171 # TypeError: 'NoneType' object cannot be interpreted as an integer 172 self.assertEqual(round3(1, None), 1) 173 174 def test_halfway_cases(self): 175 self.assertAlmostEqual(round3(0.125, 2), 0.12) 176 self.assertAlmostEqual(round3(0.375, 2), 0.38) 177 self.assertAlmostEqual(round3(0.625, 2), 0.62) 178 self.assertAlmostEqual(round3(0.875, 2), 0.88) 179 self.assertAlmostEqual(round3(-0.125, 2), -0.12) 180 self.assertAlmostEqual(round3(-0.375, 2), -0.38) 181 self.assertAlmostEqual(round3(-0.625, 2), -0.62) 182 self.assertAlmostEqual(round3(-0.875, 2), -0.88) 183 184 self.assertAlmostEqual(round3(0.25, 1), 0.2) 185 self.assertAlmostEqual(round3(0.75, 1), 0.8) 186 self.assertAlmostEqual(round3(-0.25, 1), -0.2) 187 self.assertAlmostEqual(round3(-0.75, 1), -0.8) 188 189 self.assertEqual(round3(-6.5, 0), -6.0) 190 self.assertEqual(round3(-5.5, 0), -6.0) 191 self.assertEqual(round3(-1.5, 0), -2.0) 192 self.assertEqual(round3(-0.5, 0), 0.0) 193 self.assertEqual(round3(0.5, 0), 0.0) 194 self.assertEqual(round3(1.5, 0), 2.0) 195 self.assertEqual(round3(2.5, 0), 2.0) 196 self.assertEqual(round3(3.5, 0), 4.0) 197 self.assertEqual(round3(4.5, 0), 4.0) 198 self.assertEqual(round3(5.5, 0), 6.0) 199 self.assertEqual(round3(6.5, 0), 6.0) 200 201 # same but without an explicit second argument; in 2.x these 202 # will give floats 203 self.assertEqual(round3(-6.5), -6) 204 self.assertEqual(round3(-5.5), -6) 205 self.assertEqual(round3(-1.5), -2.0) 206 self.assertEqual(round3(-0.5), 0) 207 self.assertEqual(round3(0.5), 0) 208 self.assertEqual(round3(1.5), 2) 209 self.assertEqual(round3(2.5), 2) 210 self.assertEqual(round3(3.5), 4) 211 self.assertEqual(round3(4.5), 4) 212 self.assertEqual(round3(5.5), 6) 213 self.assertEqual(round3(6.5), 6) 214 215 # no ndigits and input is already an integer: output == input 216 rv = round3(1) 217 self.assertEqual(rv, 1) 218 self.assertTrue(isinstance(rv, int)) 219 rv = round3(1.0) 220 self.assertEqual(rv, 1) 221 self.assertTrue(isinstance(rv, int)) 222 223 self.assertEqual(round3(-25.0, -1), -20.0) 224 self.assertEqual(round3(-15.0, -1), -20.0) 225 self.assertEqual(round3(-5.0, -1), 0.0) 226 self.assertEqual(round3(5.0, -1), 0.0) 227 self.assertEqual(round3(15.0, -1), 20.0) 228 self.assertEqual(round3(25.0, -1), 20.0) 229 self.assertEqual(round3(35.0, -1), 40.0) 230 self.assertEqual(round3(45.0, -1), 40.0) 231 self.assertEqual(round3(55.0, -1), 60.0) 232 self.assertEqual(round3(65.0, -1), 60.0) 233 self.assertEqual(round3(75.0, -1), 80.0) 234 self.assertEqual(round3(85.0, -1), 80.0) 235 self.assertEqual(round3(95.0, -1), 100.0) 236 self.assertEqual(round3(12325.0, -1), 12320.0) 237 self.assertEqual(round3(0, -1), 0.0) 238 239 self.assertEqual(round3(350.0, -2), 400.0) 240 self.assertEqual(round3(450.0, -2), 400.0) 241 242 self.assertAlmostEqual(round3(0.5e21, -21), 0.0) 243 self.assertAlmostEqual(round3(1.5e21, -21), 2e21) 244 self.assertAlmostEqual(round3(2.5e21, -21), 2e21) 245 self.assertAlmostEqual(round3(5.5e21, -21), 6e21) 246 self.assertAlmostEqual(round3(8.5e21, -21), 8e21) 247 248 self.assertAlmostEqual(round3(-1.5e22, -22), -2e22) 249 self.assertAlmostEqual(round3(-0.5e22, -22), 0.0) 250 self.assertAlmostEqual(round3(0.5e22, -22), 0.0) 251 self.assertAlmostEqual(round3(1.5e22, -22), 2e22) 252 253 254NAN = float('nan') 255INF = float('inf') 256NINF = float('-inf') 257 258 259class IsCloseTests(unittest.TestCase): 260 """ 261 Tests taken from Python 3.5 test_math.py: 262 https://hg.python.org/cpython/file/v3.5.2/Lib/test/test_math.py 263 """ 264 isclose = staticmethod(isclose) 265 266 def assertIsClose(self, a, b, *args, **kwargs): 267 self.assertTrue( 268 self.isclose(a, b, *args, **kwargs), 269 msg="%s and %s should be close!" % (a, b)) 270 271 def assertIsNotClose(self, a, b, *args, **kwargs): 272 self.assertFalse( 273 self.isclose(a, b, *args, **kwargs), 274 msg="%s and %s should not be close!" % (a, b)) 275 276 def assertAllClose(self, examples, *args, **kwargs): 277 for a, b in examples: 278 self.assertIsClose(a, b, *args, **kwargs) 279 280 def assertAllNotClose(self, examples, *args, **kwargs): 281 for a, b in examples: 282 self.assertIsNotClose(a, b, *args, **kwargs) 283 284 def test_negative_tolerances(self): 285 # ValueError should be raised if either tolerance is less than zero 286 with self.assertRaises(ValueError): 287 self.assertIsClose(1, 1, rel_tol=-1e-100) 288 with self.assertRaises(ValueError): 289 self.assertIsClose(1, 1, rel_tol=1e-100, abs_tol=-1e10) 290 291 def test_identical(self): 292 # identical values must test as close 293 identical_examples = [ 294 (2.0, 2.0), 295 (0.1e200, 0.1e200), 296 (1.123e-300, 1.123e-300), 297 (12345, 12345.0), 298 (0.0, -0.0), 299 (345678, 345678)] 300 self.assertAllClose(identical_examples, rel_tol=0.0, abs_tol=0.0) 301 302 def test_eight_decimal_places(self): 303 # examples that are close to 1e-8, but not 1e-9 304 eight_decimal_places_examples = [ 305 (1e8, 1e8 + 1), 306 (-1e-8, -1.000000009e-8), 307 (1.12345678, 1.12345679)] 308 self.assertAllClose(eight_decimal_places_examples, rel_tol=1e-8) 309 self.assertAllNotClose(eight_decimal_places_examples, rel_tol=1e-9) 310 311 def test_near_zero(self): 312 # values close to zero 313 near_zero_examples = [ 314 (1e-9, 0.0), 315 (-1e-9, 0.0), 316 (-1e-150, 0.0)] 317 # these should not be close to any rel_tol 318 self.assertAllNotClose(near_zero_examples, rel_tol=0.9) 319 # these should be close to abs_tol=1e-8 320 self.assertAllClose(near_zero_examples, abs_tol=1e-8) 321 322 def test_identical_infinite(self): 323 # these are close regardless of tolerance -- i.e. they are equal 324 self.assertIsClose(INF, INF) 325 self.assertIsClose(INF, INF, abs_tol=0.0) 326 self.assertIsClose(NINF, NINF) 327 self.assertIsClose(NINF, NINF, abs_tol=0.0) 328 329 def test_inf_ninf_nan(self): 330 # these should never be close (following IEEE 754 rules for equality) 331 not_close_examples = [ 332 (NAN, NAN), 333 (NAN, 1e-100), 334 (1e-100, NAN), 335 (INF, NAN), 336 (NAN, INF), 337 (INF, NINF), 338 (INF, 1.0), 339 (1.0, INF), 340 (INF, 1e308), 341 (1e308, INF)] 342 # use largest reasonable tolerance 343 self.assertAllNotClose(not_close_examples, abs_tol=0.999999999999999) 344 345 def test_zero_tolerance(self): 346 # test with zero tolerance 347 zero_tolerance_close_examples = [ 348 (1.0, 1.0), 349 (-3.4, -3.4), 350 (-1e-300, -1e-300)] 351 self.assertAllClose(zero_tolerance_close_examples, rel_tol=0.0) 352 353 zero_tolerance_not_close_examples = [ 354 (1.0, 1.000000000000001), 355 (0.99999999999999, 1.0), 356 (1.0e200, .999999999999999e200)] 357 self.assertAllNotClose(zero_tolerance_not_close_examples, rel_tol=0.0) 358 359 def test_assymetry(self): 360 # test the assymetry example from PEP 485 361 self.assertAllClose([(9, 10), (10, 9)], rel_tol=0.1) 362 363 def test_integers(self): 364 # test with integer values 365 integer_examples = [ 366 (100000001, 100000000), 367 (123456789, 123456788)] 368 369 self.assertAllClose(integer_examples, rel_tol=1e-8) 370 self.assertAllNotClose(integer_examples, rel_tol=1e-9) 371 372 def test_decimals(self): 373 # test with Decimal values 374 from decimal import Decimal 375 376 decimal_examples = [ 377 (Decimal('1.00000001'), Decimal('1.0')), 378 (Decimal('1.00000001e-20'), Decimal('1.0e-20')), 379 (Decimal('1.00000001e-100'), Decimal('1.0e-100'))] 380 self.assertAllClose(decimal_examples, rel_tol=1e-8) 381 self.assertAllNotClose(decimal_examples, rel_tol=1e-9) 382 383 def test_fractions(self): 384 # test with Fraction values 385 from fractions import Fraction 386 387 # could use some more examples here! 388 fraction_examples = [(Fraction(1, 100000000) + 1, Fraction(1))] 389 self.assertAllClose(fraction_examples, rel_tol=1e-8) 390 self.assertAllNotClose(fraction_examples, rel_tol=1e-9) 391 392 393@unittest.skipUnless( 394 (sys.version_info[0] == 2 and sys.maxunicode < 0x10FFFF), 395 "requires 'narrow' Python 2.7 build") 396class NarrowUnicodeBuildTest(unittest.TestCase): 397 398 def test_unichr(self): 399 from __builtin__ import unichr as narrow_unichr 400 401 self.assertRaises( 402 ValueError, 403 narrow_unichr, 0xFFFF + 1) 404 405 self.assertEqual(unichr(1114111), u'\U0010FFFF') 406 407 self.assertRaises( 408 ValueError, 409 unichr, 0x10FFFF + 1) 410 411 def test_byteord(self): 412 from __builtin__ import ord as narrow_ord 413 414 self.assertRaises( 415 TypeError, 416 narrow_ord, u'\U00010000') 417 418 self.assertEqual(byteord(u'\U00010000'), 0xFFFF + 1) 419 self.assertEqual(byteord(u'\U0010FFFF'), 1114111) 420 421 422class TestRedirectStream: 423 424 redirect_stream = None 425 orig_stream = None 426 427 def test_no_redirect_in_init(self): 428 orig_stdout = getattr(sys, self.orig_stream) 429 self.redirect_stream(None) 430 self.assertIs(getattr(sys, self.orig_stream), orig_stdout) 431 432 def test_redirect_to_string_io(self): 433 f = StringIO() 434 msg = "Consider an API like help(), which prints directly to stdout" 435 orig_stdout = getattr(sys, self.orig_stream) 436 with self.redirect_stream(f): 437 print(msg, file=getattr(sys, self.orig_stream)) 438 self.assertIs(getattr(sys, self.orig_stream), orig_stdout) 439 s = f.getvalue().strip() 440 self.assertEqual(s, msg) 441 442 def test_enter_result_is_target(self): 443 f = StringIO() 444 with self.redirect_stream(f) as enter_result: 445 self.assertIs(enter_result, f) 446 447 def test_cm_is_reusable(self): 448 f = StringIO() 449 write_to_f = self.redirect_stream(f) 450 orig_stdout = getattr(sys, self.orig_stream) 451 with write_to_f: 452 print("Hello", end=" ", file=getattr(sys, self.orig_stream)) 453 with write_to_f: 454 print("World!", file=getattr(sys, self.orig_stream)) 455 self.assertIs(getattr(sys, self.orig_stream), orig_stdout) 456 s = f.getvalue() 457 self.assertEqual(s, "Hello World!\n") 458 459 def test_cm_is_reentrant(self): 460 f = StringIO() 461 write_to_f = self.redirect_stream(f) 462 orig_stdout = getattr(sys, self.orig_stream) 463 with write_to_f: 464 print("Hello", end=" ", file=getattr(sys, self.orig_stream)) 465 with write_to_f: 466 print("World!", file=getattr(sys, self.orig_stream)) 467 self.assertIs(getattr(sys, self.orig_stream), orig_stdout) 468 s = f.getvalue() 469 self.assertEqual(s, "Hello World!\n") 470 471 472class TestRedirectStdout(TestRedirectStream, unittest.TestCase): 473 474 redirect_stream = redirect_stdout 475 orig_stream = "stdout" 476 477 478class TestRedirectStderr(TestRedirectStream, unittest.TestCase): 479 480 redirect_stream = redirect_stderr 481 orig_stream = "stderr" 482 483 484if __name__ == "__main__": 485 sys.exit(unittest.main()) 486