python/mozbuild/mozpack/test/test_files.py

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

michael@0 1 # This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 # License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
michael@0 4
michael@0 5 from mozbuild.util import ensureParentDir
michael@0 6
michael@0 7 from mozpack.errors import ErrorMessage
michael@0 8 from mozpack.files import (
michael@0 9 AbsoluteSymlinkFile,
michael@0 10 DeflatedFile,
michael@0 11 Dest,
michael@0 12 ExistingFile,
michael@0 13 FileFinder,
michael@0 14 File,
michael@0 15 GeneratedFile,
michael@0 16 JarFinder,
michael@0 17 ManifestFile,
michael@0 18 MinifiedJavaScript,
michael@0 19 MinifiedProperties,
michael@0 20 PreprocessedFile,
michael@0 21 XPTFile,
michael@0 22 )
michael@0 23 from mozpack.mozjar import (
michael@0 24 JarReader,
michael@0 25 JarWriter,
michael@0 26 )
michael@0 27 from mozpack.chrome.manifest import (
michael@0 28 ManifestContent,
michael@0 29 ManifestResource,
michael@0 30 ManifestLocale,
michael@0 31 ManifestOverride,
michael@0 32 )
michael@0 33 import unittest
michael@0 34 import mozfile
michael@0 35 import mozunit
michael@0 36 import os
michael@0 37 import random
michael@0 38 import string
michael@0 39 import sys
michael@0 40 import mozpack.path
michael@0 41 from tempfile import mkdtemp
michael@0 42 from io import BytesIO
michael@0 43 from xpt import Typelib
michael@0 44
michael@0 45
michael@0 46 class TestWithTmpDir(unittest.TestCase):
michael@0 47 def setUp(self):
michael@0 48 self.tmpdir = mkdtemp()
michael@0 49
michael@0 50 self.symlink_supported = False
michael@0 51
michael@0 52 if not hasattr(os, 'symlink'):
michael@0 53 return
michael@0 54
michael@0 55 dummy_path = self.tmppath('dummy_file')
michael@0 56 with open(dummy_path, 'a'):
michael@0 57 pass
michael@0 58
michael@0 59 try:
michael@0 60 os.symlink(dummy_path, self.tmppath('dummy_symlink'))
michael@0 61 os.remove(self.tmppath('dummy_symlink'))
michael@0 62 except EnvironmentError:
michael@0 63 pass
michael@0 64 finally:
michael@0 65 os.remove(dummy_path)
michael@0 66
michael@0 67 self.symlink_supported = True
michael@0 68
michael@0 69
michael@0 70 def tearDown(self):
michael@0 71 mozfile.rmtree(self.tmpdir)
michael@0 72
michael@0 73 def tmppath(self, relpath):
michael@0 74 return os.path.normpath(os.path.join(self.tmpdir, relpath))
michael@0 75
michael@0 76
michael@0 77 class MockDest(BytesIO, Dest):
michael@0 78 def __init__(self):
michael@0 79 BytesIO.__init__(self)
michael@0 80 self.mode = None
michael@0 81
michael@0 82 def read(self, length=-1):
michael@0 83 if self.mode != 'r':
michael@0 84 self.seek(0)
michael@0 85 self.mode = 'r'
michael@0 86 return BytesIO.read(self, length)
michael@0 87
michael@0 88 def write(self, data):
michael@0 89 if self.mode != 'w':
michael@0 90 self.seek(0)
michael@0 91 self.truncate(0)
michael@0 92 self.mode = 'w'
michael@0 93 return BytesIO.write(self, data)
michael@0 94
michael@0 95 def exists(self):
michael@0 96 return True
michael@0 97
michael@0 98 def close(self):
michael@0 99 if self.mode:
michael@0 100 self.mode = None
michael@0 101
michael@0 102
michael@0 103 class DestNoWrite(Dest):
michael@0 104 def write(self, data):
michael@0 105 raise RuntimeError
michael@0 106
michael@0 107
michael@0 108 class TestDest(TestWithTmpDir):
michael@0 109 def test_dest(self):
michael@0 110 dest = Dest(self.tmppath('dest'))
michael@0 111 self.assertFalse(dest.exists())
michael@0 112 dest.write('foo')
michael@0 113 self.assertTrue(dest.exists())
michael@0 114 dest.write('foo')
michael@0 115 self.assertEqual(dest.read(4), 'foof')
michael@0 116 self.assertEqual(dest.read(), 'oo')
michael@0 117 self.assertEqual(dest.read(), '')
michael@0 118 dest.write('bar')
michael@0 119 self.assertEqual(dest.read(4), 'bar')
michael@0 120 dest.close()
michael@0 121 self.assertEqual(dest.read(), 'bar')
michael@0 122 dest.write('foo')
michael@0 123 dest.close()
michael@0 124 dest.write('qux')
michael@0 125 self.assertEqual(dest.read(), 'qux')
michael@0 126
michael@0 127 rand = ''.join(random.choice(string.letters) for i in xrange(131597))
michael@0 128 samples = [
michael@0 129 '',
michael@0 130 'test',
michael@0 131 'fooo',
michael@0 132 'same',
michael@0 133 'same',
michael@0 134 'Different and longer',
michael@0 135 rand,
michael@0 136 rand,
michael@0 137 rand[:-1] + '_',
michael@0 138 'test'
michael@0 139 ]
michael@0 140
michael@0 141
michael@0 142 class TestFile(TestWithTmpDir):
michael@0 143 def test_file(self):
michael@0 144 '''
michael@0 145 Check that File.copy yields the proper content in the destination file
michael@0 146 in all situations that trigger different code paths:
michael@0 147 - different content
michael@0 148 - different content of the same size
michael@0 149 - same content
michael@0 150 - long content
michael@0 151 '''
michael@0 152 src = self.tmppath('src')
michael@0 153 dest = self.tmppath('dest')
michael@0 154
michael@0 155 for content in samples:
michael@0 156 with open(src, 'wb') as tmp:
michael@0 157 tmp.write(content)
michael@0 158 # Ensure the destination file, when it exists, is older than the
michael@0 159 # source
michael@0 160 if os.path.exists(dest):
michael@0 161 time = os.path.getmtime(src) - 1
michael@0 162 os.utime(dest, (time, time))
michael@0 163 f = File(src)
michael@0 164 f.copy(dest)
michael@0 165 self.assertEqual(content, open(dest, 'rb').read())
michael@0 166 self.assertEqual(content, f.open().read())
michael@0 167 self.assertEqual(content, f.open().read())
michael@0 168
michael@0 169 def test_file_dest(self):
michael@0 170 '''
michael@0 171 Similar to test_file, but for a destination object instead of
michael@0 172 a destination file. This ensures the destination object is being
michael@0 173 used properly by File.copy, ensuring that other subclasses of Dest
michael@0 174 will work.
michael@0 175 '''
michael@0 176 src = self.tmppath('src')
michael@0 177 dest = MockDest()
michael@0 178
michael@0 179 for content in samples:
michael@0 180 with open(src, 'wb') as tmp:
michael@0 181 tmp.write(content)
michael@0 182 f = File(src)
michael@0 183 f.copy(dest)
michael@0 184 self.assertEqual(content, dest.getvalue())
michael@0 185
michael@0 186 def test_file_open(self):
michael@0 187 '''
michael@0 188 Test whether File.open returns an appropriately reset file object.
michael@0 189 '''
michael@0 190 src = self.tmppath('src')
michael@0 191 content = ''.join(samples)
michael@0 192 with open(src, 'wb') as tmp:
michael@0 193 tmp.write(content)
michael@0 194
michael@0 195 f = File(src)
michael@0 196 self.assertEqual(content[:42], f.open().read(42))
michael@0 197 self.assertEqual(content, f.open().read())
michael@0 198
michael@0 199 def test_file_no_write(self):
michael@0 200 '''
michael@0 201 Test various conditions where File.copy is expected not to write
michael@0 202 in the destination file.
michael@0 203 '''
michael@0 204 src = self.tmppath('src')
michael@0 205 dest = self.tmppath('dest')
michael@0 206
michael@0 207 with open(src, 'wb') as tmp:
michael@0 208 tmp.write('test')
michael@0 209
michael@0 210 # Initial copy
michael@0 211 f = File(src)
michael@0 212 f.copy(dest)
michael@0 213
michael@0 214 # Ensure subsequent copies won't trigger writes
michael@0 215 f.copy(DestNoWrite(dest))
michael@0 216 self.assertEqual('test', open(dest, 'rb').read())
michael@0 217
michael@0 218 # When the source file is newer, but with the same content, no copy
michael@0 219 # should occur
michael@0 220 time = os.path.getmtime(src) - 1
michael@0 221 os.utime(dest, (time, time))
michael@0 222 f.copy(DestNoWrite(dest))
michael@0 223 self.assertEqual('test', open(dest, 'rb').read())
michael@0 224
michael@0 225 # When the source file is older than the destination file, even with
michael@0 226 # different content, no copy should occur.
michael@0 227 with open(src, 'wb') as tmp:
michael@0 228 tmp.write('fooo')
michael@0 229 time = os.path.getmtime(dest) - 1
michael@0 230 os.utime(src, (time, time))
michael@0 231 f.copy(DestNoWrite(dest))
michael@0 232 self.assertEqual('test', open(dest, 'rb').read())
michael@0 233
michael@0 234 # Double check that under conditions where a copy occurs, we would get
michael@0 235 # an exception.
michael@0 236 time = os.path.getmtime(src) - 1
michael@0 237 os.utime(dest, (time, time))
michael@0 238 self.assertRaises(RuntimeError, f.copy, DestNoWrite(dest))
michael@0 239
michael@0 240 # skip_if_older=False is expected to force a copy in this situation.
michael@0 241 f.copy(dest, skip_if_older=False)
michael@0 242 self.assertEqual('fooo', open(dest, 'rb').read())
michael@0 243
michael@0 244
michael@0 245 class TestAbsoluteSymlinkFile(TestWithTmpDir):
michael@0 246 def test_absolute_relative(self):
michael@0 247 AbsoluteSymlinkFile('/foo')
michael@0 248
michael@0 249 with self.assertRaisesRegexp(ValueError, 'Symlink target not absolute'):
michael@0 250 AbsoluteSymlinkFile('./foo')
michael@0 251
michael@0 252 def test_symlink_file(self):
michael@0 253 source = self.tmppath('test_path')
michael@0 254 with open(source, 'wt') as fh:
michael@0 255 fh.write('Hello world')
michael@0 256
michael@0 257 s = AbsoluteSymlinkFile(source)
michael@0 258 dest = self.tmppath('symlink')
michael@0 259 self.assertTrue(s.copy(dest))
michael@0 260
michael@0 261 if self.symlink_supported:
michael@0 262 self.assertTrue(os.path.islink(dest))
michael@0 263 link = os.readlink(dest)
michael@0 264 self.assertEqual(link, source)
michael@0 265 else:
michael@0 266 self.assertTrue(os.path.isfile(dest))
michael@0 267 content = open(dest).read()
michael@0 268 self.assertEqual(content, 'Hello world')
michael@0 269
michael@0 270 def test_replace_file_with_symlink(self):
michael@0 271 # If symlinks are supported, an existing file should be replaced by a
michael@0 272 # symlink.
michael@0 273 source = self.tmppath('test_path')
michael@0 274 with open(source, 'wt') as fh:
michael@0 275 fh.write('source')
michael@0 276
michael@0 277 dest = self.tmppath('dest')
michael@0 278 with open(dest, 'a'):
michael@0 279 pass
michael@0 280
michael@0 281 s = AbsoluteSymlinkFile(source)
michael@0 282 s.copy(dest, skip_if_older=False)
michael@0 283
michael@0 284 if self.symlink_supported:
michael@0 285 self.assertTrue(os.path.islink(dest))
michael@0 286 link = os.readlink(dest)
michael@0 287 self.assertEqual(link, source)
michael@0 288 else:
michael@0 289 self.assertTrue(os.path.isfile(dest))
michael@0 290 content = open(dest).read()
michael@0 291 self.assertEqual(content, 'source')
michael@0 292
michael@0 293 def test_replace_symlink(self):
michael@0 294 if not self.symlink_supported:
michael@0 295 return
michael@0 296
michael@0 297 source = self.tmppath('source')
michael@0 298 with open(source, 'a'):
michael@0 299 pass
michael@0 300
michael@0 301 dest = self.tmppath('dest')
michael@0 302
michael@0 303 os.symlink(self.tmppath('bad'), dest)
michael@0 304 self.assertTrue(os.path.islink(dest))
michael@0 305
michael@0 306 s = AbsoluteSymlinkFile(source)
michael@0 307 self.assertTrue(s.copy(dest))
michael@0 308
michael@0 309 self.assertTrue(os.path.islink(dest))
michael@0 310 link = os.readlink(dest)
michael@0 311 self.assertEqual(link, source)
michael@0 312
michael@0 313 def test_noop(self):
michael@0 314 if not hasattr(os, 'symlink'):
michael@0 315 return
michael@0 316
michael@0 317 source = self.tmppath('source')
michael@0 318 dest = self.tmppath('dest')
michael@0 319
michael@0 320 with open(source, 'a'):
michael@0 321 pass
michael@0 322
michael@0 323 os.symlink(source, dest)
michael@0 324 link = os.readlink(dest)
michael@0 325 self.assertEqual(link, source)
michael@0 326
michael@0 327 s = AbsoluteSymlinkFile(source)
michael@0 328 self.assertFalse(s.copy(dest))
michael@0 329
michael@0 330 link = os.readlink(dest)
michael@0 331 self.assertEqual(link, source)
michael@0 332
michael@0 333 class TestPreprocessedFile(TestWithTmpDir):
michael@0 334 def test_preprocess(self):
michael@0 335 '''
michael@0 336 Test that copying the file invokes the preprocessor
michael@0 337 '''
michael@0 338 src = self.tmppath('src')
michael@0 339 dest = self.tmppath('dest')
michael@0 340
michael@0 341 with open(src, 'wb') as tmp:
michael@0 342 tmp.write('#ifdef FOO\ntest\n#endif')
michael@0 343
michael@0 344 f = PreprocessedFile(src, depfile_path=None, marker='#', defines={'FOO': True})
michael@0 345 self.assertTrue(f.copy(dest))
michael@0 346
michael@0 347 self.assertEqual('test\n', open(dest, 'rb').read())
michael@0 348
michael@0 349 def test_preprocess_file_no_write(self):
michael@0 350 '''
michael@0 351 Test various conditions where PreprocessedFile.copy is expected not to
michael@0 352 write in the destination file.
michael@0 353 '''
michael@0 354 src = self.tmppath('src')
michael@0 355 dest = self.tmppath('dest')
michael@0 356 depfile = self.tmppath('depfile')
michael@0 357
michael@0 358 with open(src, 'wb') as tmp:
michael@0 359 tmp.write('#ifdef FOO\ntest\n#endif')
michael@0 360
michael@0 361 # Initial copy
michael@0 362 f = PreprocessedFile(src, depfile_path=depfile, marker='#', defines={'FOO': True})
michael@0 363 self.assertTrue(f.copy(dest))
michael@0 364
michael@0 365 # Ensure subsequent copies won't trigger writes
michael@0 366 self.assertFalse(f.copy(DestNoWrite(dest)))
michael@0 367 self.assertEqual('test\n', open(dest, 'rb').read())
michael@0 368
michael@0 369 # When the source file is older than the destination file, even with
michael@0 370 # different content, no copy should occur.
michael@0 371 with open(src, 'wb') as tmp:
michael@0 372 tmp.write('#ifdef FOO\nfooo\n#endif')
michael@0 373 time = os.path.getmtime(dest) - 1
michael@0 374 os.utime(src, (time, time))
michael@0 375 self.assertFalse(f.copy(DestNoWrite(dest)))
michael@0 376 self.assertEqual('test\n', open(dest, 'rb').read())
michael@0 377
michael@0 378 # skip_if_older=False is expected to force a copy in this situation.
michael@0 379 self.assertTrue(f.copy(dest, skip_if_older=False))
michael@0 380 self.assertEqual('fooo\n', open(dest, 'rb').read())
michael@0 381
michael@0 382 def test_preprocess_file_dependencies(self):
michael@0 383 '''
michael@0 384 Test that the preprocess runs if the dependencies of the source change
michael@0 385 '''
michael@0 386 src = self.tmppath('src')
michael@0 387 dest = self.tmppath('dest')
michael@0 388 incl = self.tmppath('incl')
michael@0 389 deps = self.tmppath('src.pp')
michael@0 390
michael@0 391 with open(src, 'wb') as tmp:
michael@0 392 tmp.write('#ifdef FOO\ntest\n#endif')
michael@0 393
michael@0 394 with open(incl, 'wb') as tmp:
michael@0 395 tmp.write('foo bar')
michael@0 396
michael@0 397 # Initial copy
michael@0 398 f = PreprocessedFile(src, depfile_path=deps, marker='#', defines={'FOO': True})
michael@0 399 self.assertTrue(f.copy(dest))
michael@0 400
michael@0 401 # Update the source so it #includes the include file.
michael@0 402 with open(src, 'wb') as tmp:
michael@0 403 tmp.write('#include incl\n')
michael@0 404 time = os.path.getmtime(dest) + 1
michael@0 405 os.utime(src, (time, time))
michael@0 406 self.assertTrue(f.copy(dest))
michael@0 407 self.assertEqual('foo bar', open(dest, 'rb').read())
michael@0 408
michael@0 409 # If one of the dependencies changes, the file should be updated. The
michael@0 410 # mtime of the dependency is set after the destination file, to avoid
michael@0 411 # both files having the same time.
michael@0 412 with open(incl, 'wb') as tmp:
michael@0 413 tmp.write('quux')
michael@0 414 time = os.path.getmtime(dest) + 1
michael@0 415 os.utime(incl, (time, time))
michael@0 416 self.assertTrue(f.copy(dest))
michael@0 417 self.assertEqual('quux', open(dest, 'rb').read())
michael@0 418
michael@0 419 # Perform one final copy to confirm that we don't run the preprocessor
michael@0 420 # again. We update the mtime of the destination so it's newer than the
michael@0 421 # input files. This would "just work" if we weren't changing
michael@0 422 time = os.path.getmtime(incl) + 1
michael@0 423 os.utime(dest, (time, time))
michael@0 424 self.assertFalse(f.copy(DestNoWrite(dest)))
michael@0 425
michael@0 426 def test_replace_symlink(self):
michael@0 427 '''
michael@0 428 Test that if the destination exists, and is a symlink, the target of
michael@0 429 the symlink is not overwritten by the preprocessor output.
michael@0 430 '''
michael@0 431 if not self.symlink_supported:
michael@0 432 return
michael@0 433
michael@0 434 source = self.tmppath('source')
michael@0 435 dest = self.tmppath('dest')
michael@0 436 pp_source = self.tmppath('pp_in')
michael@0 437 deps = self.tmppath('deps')
michael@0 438
michael@0 439 with open(source, 'a'):
michael@0 440 pass
michael@0 441
michael@0 442 os.symlink(source, dest)
michael@0 443 self.assertTrue(os.path.islink(dest))
michael@0 444
michael@0 445 with open(pp_source, 'wb') as tmp:
michael@0 446 tmp.write('#define FOO\nPREPROCESSED')
michael@0 447
michael@0 448 f = PreprocessedFile(pp_source, depfile_path=deps, marker='#',
michael@0 449 defines={'FOO': True})
michael@0 450 self.assertTrue(f.copy(dest))
michael@0 451
michael@0 452 self.assertEqual('PREPROCESSED', open(dest, 'rb').read())
michael@0 453 self.assertFalse(os.path.islink(dest))
michael@0 454 self.assertEqual('', open(source, 'rb').read())
michael@0 455
michael@0 456 class TestExistingFile(TestWithTmpDir):
michael@0 457 def test_required_missing_dest(self):
michael@0 458 with self.assertRaisesRegexp(ErrorMessage, 'Required existing file'):
michael@0 459 f = ExistingFile(required=True)
michael@0 460 f.copy(self.tmppath('dest'))
michael@0 461
michael@0 462 def test_required_existing_dest(self):
michael@0 463 p = self.tmppath('dest')
michael@0 464 with open(p, 'a'):
michael@0 465 pass
michael@0 466
michael@0 467 f = ExistingFile(required=True)
michael@0 468 f.copy(p)
michael@0 469
michael@0 470 def test_optional_missing_dest(self):
michael@0 471 f = ExistingFile(required=False)
michael@0 472 f.copy(self.tmppath('dest'))
michael@0 473
michael@0 474 def test_optional_existing_dest(self):
michael@0 475 p = self.tmppath('dest')
michael@0 476 with open(p, 'a'):
michael@0 477 pass
michael@0 478
michael@0 479 f = ExistingFile(required=False)
michael@0 480 f.copy(p)
michael@0 481
michael@0 482
michael@0 483 class TestGeneratedFile(TestWithTmpDir):
michael@0 484 def test_generated_file(self):
michael@0 485 '''
michael@0 486 Check that GeneratedFile.copy yields the proper content in the
michael@0 487 destination file in all situations that trigger different code paths
michael@0 488 (see TestFile.test_file)
michael@0 489 '''
michael@0 490 dest = self.tmppath('dest')
michael@0 491
michael@0 492 for content in samples:
michael@0 493 f = GeneratedFile(content)
michael@0 494 f.copy(dest)
michael@0 495 self.assertEqual(content, open(dest, 'rb').read())
michael@0 496
michael@0 497 def test_generated_file_open(self):
michael@0 498 '''
michael@0 499 Test whether GeneratedFile.open returns an appropriately reset file
michael@0 500 object.
michael@0 501 '''
michael@0 502 content = ''.join(samples)
michael@0 503 f = GeneratedFile(content)
michael@0 504 self.assertEqual(content[:42], f.open().read(42))
michael@0 505 self.assertEqual(content, f.open().read())
michael@0 506
michael@0 507 def test_generated_file_no_write(self):
michael@0 508 '''
michael@0 509 Test various conditions where GeneratedFile.copy is expected not to
michael@0 510 write in the destination file.
michael@0 511 '''
michael@0 512 dest = self.tmppath('dest')
michael@0 513
michael@0 514 # Initial copy
michael@0 515 f = GeneratedFile('test')
michael@0 516 f.copy(dest)
michael@0 517
michael@0 518 # Ensure subsequent copies won't trigger writes
michael@0 519 f.copy(DestNoWrite(dest))
michael@0 520 self.assertEqual('test', open(dest, 'rb').read())
michael@0 521
michael@0 522 # When using a new instance with the same content, no copy should occur
michael@0 523 f = GeneratedFile('test')
michael@0 524 f.copy(DestNoWrite(dest))
michael@0 525 self.assertEqual('test', open(dest, 'rb').read())
michael@0 526
michael@0 527 # Double check that under conditions where a copy occurs, we would get
michael@0 528 # an exception.
michael@0 529 f = GeneratedFile('fooo')
michael@0 530 self.assertRaises(RuntimeError, f.copy, DestNoWrite(dest))
michael@0 531
michael@0 532
michael@0 533 class TestDeflatedFile(TestWithTmpDir):
michael@0 534 def test_deflated_file(self):
michael@0 535 '''
michael@0 536 Check that DeflatedFile.copy yields the proper content in the
michael@0 537 destination file in all situations that trigger different code paths
michael@0 538 (see TestFile.test_file)
michael@0 539 '''
michael@0 540 src = self.tmppath('src.jar')
michael@0 541 dest = self.tmppath('dest')
michael@0 542
michael@0 543 contents = {}
michael@0 544 with JarWriter(src) as jar:
michael@0 545 for content in samples:
michael@0 546 name = ''.join(random.choice(string.letters)
michael@0 547 for i in xrange(8))
michael@0 548 jar.add(name, content, compress=True)
michael@0 549 contents[name] = content
michael@0 550
michael@0 551 for j in JarReader(src):
michael@0 552 f = DeflatedFile(j)
michael@0 553 f.copy(dest)
michael@0 554 self.assertEqual(contents[j.filename], open(dest, 'rb').read())
michael@0 555
michael@0 556 def test_deflated_file_open(self):
michael@0 557 '''
michael@0 558 Test whether DeflatedFile.open returns an appropriately reset file
michael@0 559 object.
michael@0 560 '''
michael@0 561 src = self.tmppath('src.jar')
michael@0 562 content = ''.join(samples)
michael@0 563 with JarWriter(src) as jar:
michael@0 564 jar.add('content', content)
michael@0 565
michael@0 566 f = DeflatedFile(JarReader(src)['content'])
michael@0 567 self.assertEqual(content[:42], f.open().read(42))
michael@0 568 self.assertEqual(content, f.open().read())
michael@0 569
michael@0 570 def test_deflated_file_no_write(self):
michael@0 571 '''
michael@0 572 Test various conditions where DeflatedFile.copy is expected not to
michael@0 573 write in the destination file.
michael@0 574 '''
michael@0 575 src = self.tmppath('src.jar')
michael@0 576 dest = self.tmppath('dest')
michael@0 577
michael@0 578 with JarWriter(src) as jar:
michael@0 579 jar.add('test', 'test')
michael@0 580 jar.add('test2', 'test')
michael@0 581 jar.add('fooo', 'fooo')
michael@0 582
michael@0 583 jar = JarReader(src)
michael@0 584 # Initial copy
michael@0 585 f = DeflatedFile(jar['test'])
michael@0 586 f.copy(dest)
michael@0 587
michael@0 588 # Ensure subsequent copies won't trigger writes
michael@0 589 f.copy(DestNoWrite(dest))
michael@0 590 self.assertEqual('test', open(dest, 'rb').read())
michael@0 591
michael@0 592 # When using a different file with the same content, no copy should
michael@0 593 # occur
michael@0 594 f = DeflatedFile(jar['test2'])
michael@0 595 f.copy(DestNoWrite(dest))
michael@0 596 self.assertEqual('test', open(dest, 'rb').read())
michael@0 597
michael@0 598 # Double check that under conditions where a copy occurs, we would get
michael@0 599 # an exception.
michael@0 600 f = DeflatedFile(jar['fooo'])
michael@0 601 self.assertRaises(RuntimeError, f.copy, DestNoWrite(dest))
michael@0 602
michael@0 603
michael@0 604 class TestManifestFile(TestWithTmpDir):
michael@0 605 def test_manifest_file(self):
michael@0 606 f = ManifestFile('chrome')
michael@0 607 f.add(ManifestContent('chrome', 'global', 'toolkit/content/global/'))
michael@0 608 f.add(ManifestResource('chrome', 'gre-resources', 'toolkit/res/'))
michael@0 609 f.add(ManifestResource('chrome/pdfjs', 'pdfjs', './'))
michael@0 610 f.add(ManifestContent('chrome/pdfjs', 'pdfjs', 'pdfjs'))
michael@0 611 f.add(ManifestLocale('chrome', 'browser', 'en-US',
michael@0 612 'en-US/locale/browser/'))
michael@0 613
michael@0 614 f.copy(self.tmppath('chrome.manifest'))
michael@0 615 self.assertEqual(open(self.tmppath('chrome.manifest')).readlines(), [
michael@0 616 'content global toolkit/content/global/\n',
michael@0 617 'resource gre-resources toolkit/res/\n',
michael@0 618 'resource pdfjs pdfjs/\n',
michael@0 619 'content pdfjs pdfjs/pdfjs\n',
michael@0 620 'locale browser en-US en-US/locale/browser/\n',
michael@0 621 ])
michael@0 622
michael@0 623 self.assertRaises(
michael@0 624 ValueError,
michael@0 625 f.remove,
michael@0 626 ManifestContent('', 'global', 'toolkit/content/global/')
michael@0 627 )
michael@0 628 self.assertRaises(
michael@0 629 ValueError,
michael@0 630 f.remove,
michael@0 631 ManifestOverride('chrome', 'chrome://global/locale/netError.dtd',
michael@0 632 'chrome://browser/locale/netError.dtd')
michael@0 633 )
michael@0 634
michael@0 635 f.remove(ManifestContent('chrome', 'global',
michael@0 636 'toolkit/content/global/'))
michael@0 637 self.assertRaises(
michael@0 638 ValueError,
michael@0 639 f.remove,
michael@0 640 ManifestContent('chrome', 'global', 'toolkit/content/global/')
michael@0 641 )
michael@0 642
michael@0 643 f.copy(self.tmppath('chrome.manifest'))
michael@0 644 content = open(self.tmppath('chrome.manifest')).read()
michael@0 645 self.assertEqual(content[:42], f.open().read(42))
michael@0 646 self.assertEqual(content, f.open().read())
michael@0 647
michael@0 648 # Compiled typelib for the following IDL:
michael@0 649 # interface foo;
michael@0 650 # [uuid(5f70da76-519c-4858-b71e-e3c92333e2d6)]
michael@0 651 # interface bar {
michael@0 652 # void bar(in foo f);
michael@0 653 # };
michael@0 654 bar_xpt = GeneratedFile(
michael@0 655 b'\x58\x50\x43\x4F\x4D\x0A\x54\x79\x70\x65\x4C\x69\x62\x0D\x0A\x1A' +
michael@0 656 b'\x01\x02\x00\x02\x00\x00\x00\x7B\x00\x00\x00\x24\x00\x00\x00\x5C' +
michael@0 657 b'\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' +
michael@0 658 b'\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x5F' +
michael@0 659 b'\x70\xDA\x76\x51\x9C\x48\x58\xB7\x1E\xE3\xC9\x23\x33\xE2\xD6\x00' +
michael@0 660 b'\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x0D\x00\x66\x6F\x6F\x00' +
michael@0 661 b'\x62\x61\x72\x00\x62\x61\x72\x00\x00\x00\x00\x01\x00\x00\x00\x00' +
michael@0 662 b'\x09\x01\x80\x92\x00\x01\x80\x06\x00\x00\x00'
michael@0 663 )
michael@0 664
michael@0 665 # Compiled typelib for the following IDL:
michael@0 666 # [uuid(3271bebc-927e-4bef-935e-44e0aaf3c1e5)]
michael@0 667 # interface foo {
michael@0 668 # void foo();
michael@0 669 # };
michael@0 670 foo_xpt = GeneratedFile(
michael@0 671 b'\x58\x50\x43\x4F\x4D\x0A\x54\x79\x70\x65\x4C\x69\x62\x0D\x0A\x1A' +
michael@0 672 b'\x01\x02\x00\x01\x00\x00\x00\x57\x00\x00\x00\x24\x00\x00\x00\x40' +
michael@0 673 b'\x80\x00\x00\x32\x71\xBE\xBC\x92\x7E\x4B\xEF\x93\x5E\x44\xE0\xAA' +
michael@0 674 b'\xF3\xC1\xE5\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x09\x00' +
michael@0 675 b'\x66\x6F\x6F\x00\x66\x6F\x6F\x00\x00\x00\x00\x01\x00\x00\x00\x00' +
michael@0 676 b'\x05\x00\x80\x06\x00\x00\x00'
michael@0 677 )
michael@0 678
michael@0 679 # Compiled typelib for the following IDL:
michael@0 680 # [uuid(7057f2aa-fdc2-4559-abde-08d939f7e80d)]
michael@0 681 # interface foo {
michael@0 682 # void foo();
michael@0 683 # };
michael@0 684 foo2_xpt = GeneratedFile(
michael@0 685 b'\x58\x50\x43\x4F\x4D\x0A\x54\x79\x70\x65\x4C\x69\x62\x0D\x0A\x1A' +
michael@0 686 b'\x01\x02\x00\x01\x00\x00\x00\x57\x00\x00\x00\x24\x00\x00\x00\x40' +
michael@0 687 b'\x80\x00\x00\x70\x57\xF2\xAA\xFD\xC2\x45\x59\xAB\xDE\x08\xD9\x39' +
michael@0 688 b'\xF7\xE8\x0D\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x09\x00' +
michael@0 689 b'\x66\x6F\x6F\x00\x66\x6F\x6F\x00\x00\x00\x00\x01\x00\x00\x00\x00' +
michael@0 690 b'\x05\x00\x80\x06\x00\x00\x00'
michael@0 691 )
michael@0 692
michael@0 693
michael@0 694 def read_interfaces(file):
michael@0 695 return dict((i.name, i) for i in Typelib.read(file).interfaces)
michael@0 696
michael@0 697
michael@0 698 class TestXPTFile(TestWithTmpDir):
michael@0 699 def test_xpt_file(self):
michael@0 700 x = XPTFile()
michael@0 701 x.add(foo_xpt)
michael@0 702 x.add(bar_xpt)
michael@0 703 x.copy(self.tmppath('interfaces.xpt'))
michael@0 704
michael@0 705 foo = read_interfaces(foo_xpt.open())
michael@0 706 foo2 = read_interfaces(foo2_xpt.open())
michael@0 707 bar = read_interfaces(bar_xpt.open())
michael@0 708 linked = read_interfaces(self.tmppath('interfaces.xpt'))
michael@0 709 self.assertEqual(foo['foo'], linked['foo'])
michael@0 710 self.assertEqual(bar['bar'], linked['bar'])
michael@0 711
michael@0 712 x.remove(foo_xpt)
michael@0 713 x.copy(self.tmppath('interfaces2.xpt'))
michael@0 714 linked = read_interfaces(self.tmppath('interfaces2.xpt'))
michael@0 715 self.assertEqual(bar['foo'], linked['foo'])
michael@0 716 self.assertEqual(bar['bar'], linked['bar'])
michael@0 717
michael@0 718 x.add(foo_xpt)
michael@0 719 x.copy(DestNoWrite(self.tmppath('interfaces.xpt')))
michael@0 720 linked = read_interfaces(self.tmppath('interfaces.xpt'))
michael@0 721 self.assertEqual(foo['foo'], linked['foo'])
michael@0 722 self.assertEqual(bar['bar'], linked['bar'])
michael@0 723
michael@0 724 x = XPTFile()
michael@0 725 x.add(foo2_xpt)
michael@0 726 x.add(bar_xpt)
michael@0 727 x.copy(self.tmppath('interfaces.xpt'))
michael@0 728 linked = read_interfaces(self.tmppath('interfaces.xpt'))
michael@0 729 self.assertEqual(foo2['foo'], linked['foo'])
michael@0 730 self.assertEqual(bar['bar'], linked['bar'])
michael@0 731
michael@0 732 x = XPTFile()
michael@0 733 x.add(foo_xpt)
michael@0 734 x.add(foo2_xpt)
michael@0 735 x.add(bar_xpt)
michael@0 736 from xpt import DataError
michael@0 737 self.assertRaises(DataError, x.copy, self.tmppath('interfaces.xpt'))
michael@0 738
michael@0 739
michael@0 740 class TestMinifiedProperties(TestWithTmpDir):
michael@0 741 def test_minified_properties(self):
michael@0 742 propLines = [
michael@0 743 '# Comments are removed',
michael@0 744 'foo = bar',
michael@0 745 '',
michael@0 746 '# Another comment',
michael@0 747 ]
michael@0 748 prop = GeneratedFile('\n'.join(propLines))
michael@0 749 self.assertEqual(MinifiedProperties(prop).open().readlines(),
michael@0 750 ['foo = bar\n', '\n'])
michael@0 751 open(self.tmppath('prop'), 'wb').write('\n'.join(propLines))
michael@0 752 MinifiedProperties(File(self.tmppath('prop'))) \
michael@0 753 .copy(self.tmppath('prop2'))
michael@0 754 self.assertEqual(open(self.tmppath('prop2')).readlines(),
michael@0 755 ['foo = bar\n', '\n'])
michael@0 756
michael@0 757
michael@0 758 class TestMinifiedJavaScript(TestWithTmpDir):
michael@0 759 orig_lines = [
michael@0 760 '// Comment line',
michael@0 761 'let foo = "bar";',
michael@0 762 'var bar = true;',
michael@0 763 '',
michael@0 764 '// Another comment',
michael@0 765 ]
michael@0 766
michael@0 767 def test_minified_javascript(self):
michael@0 768 orig_f = GeneratedFile('\n'.join(self.orig_lines))
michael@0 769 min_f = MinifiedJavaScript(orig_f)
michael@0 770
michael@0 771 mini_lines = min_f.open().readlines()
michael@0 772 self.assertTrue(mini_lines)
michael@0 773 self.assertTrue(len(mini_lines) < len(self.orig_lines))
michael@0 774
michael@0 775 def _verify_command(self, code):
michael@0 776 our_dir = os.path.abspath(os.path.dirname(__file__))
michael@0 777 return [
michael@0 778 sys.executable,
michael@0 779 os.path.join(our_dir, 'support', 'minify_js_verify.py'),
michael@0 780 code,
michael@0 781 ]
michael@0 782
michael@0 783 def test_minified_verify_success(self):
michael@0 784 orig_f = GeneratedFile('\n'.join(self.orig_lines))
michael@0 785 min_f = MinifiedJavaScript(orig_f,
michael@0 786 verify_command=self._verify_command('0'))
michael@0 787
michael@0 788 mini_lines = min_f.open().readlines()
michael@0 789 self.assertTrue(mini_lines)
michael@0 790 self.assertTrue(len(mini_lines) < len(self.orig_lines))
michael@0 791
michael@0 792 def test_minified_verify_failure(self):
michael@0 793 orig_f = GeneratedFile('\n'.join(self.orig_lines))
michael@0 794 min_f = MinifiedJavaScript(orig_f,
michael@0 795 verify_command=self._verify_command('1'))
michael@0 796
michael@0 797 mini_lines = min_f.open().readlines()
michael@0 798 self.assertEqual(mini_lines, orig_f.open().readlines())
michael@0 799
michael@0 800
michael@0 801 class MatchTestTemplate(object):
michael@0 802 def prepare_match_test(self, with_dotfiles=False):
michael@0 803 self.add('bar')
michael@0 804 self.add('foo/bar')
michael@0 805 self.add('foo/baz')
michael@0 806 self.add('foo/qux/1')
michael@0 807 self.add('foo/qux/bar')
michael@0 808 self.add('foo/qux/2/test')
michael@0 809 self.add('foo/qux/2/test2')
michael@0 810 if with_dotfiles:
michael@0 811 self.add('foo/.foo')
michael@0 812 self.add('foo/.bar/foo')
michael@0 813
michael@0 814 def do_match_test(self):
michael@0 815 self.do_check('', [
michael@0 816 'bar', 'foo/bar', 'foo/baz', 'foo/qux/1', 'foo/qux/bar',
michael@0 817 'foo/qux/2/test', 'foo/qux/2/test2'
michael@0 818 ])
michael@0 819 self.do_check('*', [
michael@0 820 'bar', 'foo/bar', 'foo/baz', 'foo/qux/1', 'foo/qux/bar',
michael@0 821 'foo/qux/2/test', 'foo/qux/2/test2'
michael@0 822 ])
michael@0 823 self.do_check('foo/qux', [
michael@0 824 'foo/qux/1', 'foo/qux/bar', 'foo/qux/2/test', 'foo/qux/2/test2'
michael@0 825 ])
michael@0 826 self.do_check('foo/b*', ['foo/bar', 'foo/baz'])
michael@0 827 self.do_check('baz', [])
michael@0 828 self.do_check('foo/foo', [])
michael@0 829 self.do_check('foo/*ar', ['foo/bar'])
michael@0 830 self.do_check('*ar', ['bar'])
michael@0 831 self.do_check('*/bar', ['foo/bar'])
michael@0 832 self.do_check('foo/*ux', [
michael@0 833 'foo/qux/1', 'foo/qux/bar', 'foo/qux/2/test', 'foo/qux/2/test2'
michael@0 834 ])
michael@0 835 self.do_check('foo/q*ux', [
michael@0 836 'foo/qux/1', 'foo/qux/bar', 'foo/qux/2/test', 'foo/qux/2/test2'
michael@0 837 ])
michael@0 838 self.do_check('foo/*/2/test*', ['foo/qux/2/test', 'foo/qux/2/test2'])
michael@0 839 self.do_check('**/bar', ['bar', 'foo/bar', 'foo/qux/bar'])
michael@0 840 self.do_check('foo/**/test', ['foo/qux/2/test'])
michael@0 841 self.do_check('foo/**', [
michael@0 842 'foo/bar', 'foo/baz', 'foo/qux/1', 'foo/qux/bar',
michael@0 843 'foo/qux/2/test', 'foo/qux/2/test2'
michael@0 844 ])
michael@0 845 self.do_check('**/2/test*', ['foo/qux/2/test', 'foo/qux/2/test2'])
michael@0 846 self.do_check('**/foo', [
michael@0 847 'foo/bar', 'foo/baz', 'foo/qux/1', 'foo/qux/bar',
michael@0 848 'foo/qux/2/test', 'foo/qux/2/test2'
michael@0 849 ])
michael@0 850 self.do_check('**/barbaz', [])
michael@0 851 self.do_check('f**/bar', ['foo/bar'])
michael@0 852
michael@0 853 def do_finder_test(self, finder):
michael@0 854 self.assertTrue(finder.contains('foo/.foo'))
michael@0 855 self.assertTrue(finder.contains('foo/.bar'))
michael@0 856 self.assertTrue('foo/.foo' in [f for f, c in
michael@0 857 finder.find('foo/.foo')])
michael@0 858 self.assertTrue('foo/.bar/foo' in [f for f, c in
michael@0 859 finder.find('foo/.bar')])
michael@0 860 self.assertEqual(sorted([f for f, c in finder.find('foo/.*')]),
michael@0 861 ['foo/.bar/foo', 'foo/.foo'])
michael@0 862 for pattern in ['foo', '**', '**/*', '**/foo', 'foo/*']:
michael@0 863 self.assertFalse('foo/.foo' in [f for f, c in
michael@0 864 finder.find(pattern)])
michael@0 865 self.assertFalse('foo/.bar/foo' in [f for f, c in
michael@0 866 finder.find(pattern)])
michael@0 867 self.assertEqual(sorted([f for f, c in finder.find(pattern)]),
michael@0 868 sorted([f for f, c in finder
michael@0 869 if mozpack.path.match(f, pattern)]))
michael@0 870
michael@0 871
michael@0 872 def do_check(test, finder, pattern, result):
michael@0 873 if result:
michael@0 874 test.assertTrue(finder.contains(pattern))
michael@0 875 else:
michael@0 876 test.assertFalse(finder.contains(pattern))
michael@0 877 test.assertEqual(sorted(list(f for f, c in finder.find(pattern))),
michael@0 878 sorted(result))
michael@0 879
michael@0 880
michael@0 881 class TestFileFinder(MatchTestTemplate, TestWithTmpDir):
michael@0 882 def add(self, path):
michael@0 883 ensureParentDir(self.tmppath(path))
michael@0 884 open(self.tmppath(path), 'wb').write(path)
michael@0 885
michael@0 886 def do_check(self, pattern, result):
michael@0 887 do_check(self, self.finder, pattern, result)
michael@0 888
michael@0 889 def test_file_finder(self):
michael@0 890 self.prepare_match_test(with_dotfiles=True)
michael@0 891 self.finder = FileFinder(self.tmpdir)
michael@0 892 self.do_match_test()
michael@0 893 self.do_finder_test(self.finder)
michael@0 894
michael@0 895 def test_ignored_dirs(self):
michael@0 896 """Ignored directories should not have results returned."""
michael@0 897 self.prepare_match_test()
michael@0 898 self.add('fooz')
michael@0 899
michael@0 900 # Present to ensure prefix matching doesn't exclude.
michael@0 901 self.add('foo/quxz')
michael@0 902
michael@0 903 self.finder = FileFinder(self.tmpdir, ignore=['foo/qux'])
michael@0 904
michael@0 905 self.do_check('**', ['bar', 'foo/bar', 'foo/baz', 'foo/quxz', 'fooz'])
michael@0 906 self.do_check('foo/*', ['foo/bar', 'foo/baz', 'foo/quxz'])
michael@0 907 self.do_check('foo/**', ['foo/bar', 'foo/baz', 'foo/quxz'])
michael@0 908 self.do_check('foo/qux/**', [])
michael@0 909 self.do_check('foo/qux/*', [])
michael@0 910 self.do_check('foo/qux/bar', [])
michael@0 911 self.do_check('foo/quxz', ['foo/quxz'])
michael@0 912 self.do_check('fooz', ['fooz'])
michael@0 913
michael@0 914 def test_ignored_files(self):
michael@0 915 """Ignored files should not have results returned."""
michael@0 916 self.prepare_match_test()
michael@0 917
michael@0 918 # Be sure prefix match doesn't get ignored.
michael@0 919 self.add('barz')
michael@0 920
michael@0 921 self.finder = FileFinder(self.tmpdir, ignore=['foo/bar', 'bar'])
michael@0 922 self.do_check('**', ['barz', 'foo/baz', 'foo/qux/1', 'foo/qux/2/test',
michael@0 923 'foo/qux/2/test2', 'foo/qux/bar'])
michael@0 924 self.do_check('foo/**', ['foo/baz', 'foo/qux/1', 'foo/qux/2/test',
michael@0 925 'foo/qux/2/test2', 'foo/qux/bar'])
michael@0 926
michael@0 927 def test_ignored_patterns(self):
michael@0 928 """Ignore entries with patterns should be honored."""
michael@0 929 self.prepare_match_test()
michael@0 930
michael@0 931 self.add('foo/quxz')
michael@0 932
michael@0 933 self.finder = FileFinder(self.tmpdir, ignore=['foo/qux/*'])
michael@0 934 self.do_check('**', ['foo/bar', 'foo/baz', 'foo/quxz', 'bar'])
michael@0 935 self.do_check('foo/**', ['foo/bar', 'foo/baz', 'foo/quxz'])
michael@0 936
michael@0 937
michael@0 938 class TestJarFinder(MatchTestTemplate, TestWithTmpDir):
michael@0 939 def add(self, path):
michael@0 940 self.jar.add(path, path, compress=True)
michael@0 941
michael@0 942 def do_check(self, pattern, result):
michael@0 943 do_check(self, self.finder, pattern, result)
michael@0 944
michael@0 945 def test_jar_finder(self):
michael@0 946 self.jar = JarWriter(file=self.tmppath('test.jar'))
michael@0 947 self.prepare_match_test()
michael@0 948 self.jar.finish()
michael@0 949 reader = JarReader(file=self.tmppath('test.jar'))
michael@0 950 self.finder = JarFinder(self.tmppath('test.jar'), reader)
michael@0 951 self.do_match_test()
michael@0 952
michael@0 953
michael@0 954 if __name__ == '__main__':
michael@0 955 mozunit.main()

mercurial