super_mox.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. # Copyright (c) 2011 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. """Simplify unit tests based on pymox."""
  5. import os
  6. import random
  7. import shutil
  8. import string
  9. import StringIO
  10. import subprocess
  11. import sys
  12. sys.path.append(os.path.dirname(os.path.dirname(__file__)))
  13. from third_party.pymox import mox
  14. class IsOneOf(mox.Comparator):
  15. def __init__(self, keys):
  16. self._keys = keys
  17. def equals(self, rhs):
  18. return rhs in self._keys
  19. def __repr__(self):
  20. return '<sequence or map containing \'%s\'>' % str(self._keys)
  21. class TestCaseUtils(object):
  22. """Base class with some additional functionalities. People will usually want
  23. to use SuperMoxTestBase instead."""
  24. # Backup the separator in case it gets mocked
  25. _OS_SEP = os.sep
  26. _RANDOM_CHOICE = random.choice
  27. _RANDOM_RANDINT = random.randint
  28. _STRING_LETTERS = string.letters
  29. ## Some utilities for generating arbitrary arguments.
  30. def String(self, max_length):
  31. return ''.join([self._RANDOM_CHOICE(self._STRING_LETTERS)
  32. for _ in xrange(self._RANDOM_RANDINT(1, max_length))])
  33. def Strings(self, max_arg_count, max_arg_length):
  34. return [self.String(max_arg_length) for _ in xrange(max_arg_count)]
  35. def Args(self, max_arg_count=8, max_arg_length=16):
  36. return self.Strings(max_arg_count,
  37. self._RANDOM_RANDINT(1, max_arg_length))
  38. def _DirElts(self, max_elt_count=4, max_elt_length=8):
  39. return self._OS_SEP.join(self.Strings(max_elt_count, max_elt_length))
  40. def Dir(self, max_elt_count=4, max_elt_length=8):
  41. return (self._RANDOM_CHOICE((self._OS_SEP, '')) +
  42. self._DirElts(max_elt_count, max_elt_length))
  43. def RootDir(self, max_elt_count=4, max_elt_length=8):
  44. return self._OS_SEP + self._DirElts(max_elt_count, max_elt_length)
  45. def compareMembers(self, obj, members):
  46. """If you add a member, be sure to add the relevant test!"""
  47. # Skip over members starting with '_' since they are usually not meant to
  48. # be for public use.
  49. actual_members = [x for x in sorted(dir(obj))
  50. if not x.startswith('_')]
  51. expected_members = sorted(members)
  52. if actual_members != expected_members:
  53. diff = ([i for i in actual_members if i not in expected_members] +
  54. [i for i in expected_members if i not in actual_members])
  55. print >> sys.stderr, diff
  56. # pylint: disable=no-member
  57. self.assertEqual(actual_members, expected_members)
  58. def setUp(self):
  59. self.root_dir = self.Dir()
  60. self.args = self.Args()
  61. self.relpath = self.String(200)
  62. def tearDown(self):
  63. pass
  64. class StdoutCheck(object):
  65. def setUp(self):
  66. # Override the mock with a StringIO, it's much less painful to test.
  67. self._old_stdout = sys.stdout
  68. stdout = StringIO.StringIO()
  69. stdout.flush = lambda: None
  70. sys.stdout = stdout
  71. def tearDown(self):
  72. try:
  73. # If sys.stdout was used, self.checkstdout() must be called.
  74. # pylint: disable=no-member
  75. if not sys.stdout.closed:
  76. self.assertEquals('', sys.stdout.getvalue())
  77. except AttributeError:
  78. pass
  79. sys.stdout = self._old_stdout
  80. def checkstdout(self, expected):
  81. value = sys.stdout.getvalue()
  82. sys.stdout.close()
  83. # pylint: disable=no-member
  84. self.assertEquals(expected, value)
  85. class SuperMoxTestBase(TestCaseUtils, StdoutCheck, mox.MoxTestBase):
  86. def setUp(self):
  87. """Patch a few functions with know side-effects."""
  88. TestCaseUtils.setUp(self)
  89. mox.MoxTestBase.setUp(self)
  90. os_to_mock = ('chdir', 'chown', 'close', 'closerange', 'dup', 'dup2',
  91. 'fchdir', 'fchmod', 'fchown', 'fdopen', 'getcwd', 'listdir', 'lseek',
  92. 'makedirs', 'mkdir', 'open', 'popen', 'popen2', 'popen3', 'popen4',
  93. 'read', 'remove', 'removedirs', 'rename', 'renames', 'rmdir', 'symlink',
  94. 'system', 'tmpfile', 'walk', 'write')
  95. self.MockList(os, os_to_mock)
  96. os_path_to_mock = ('abspath', 'exists', 'getsize', 'isdir', 'isfile',
  97. 'islink', 'ismount', 'lexists', 'realpath', 'samefile', 'walk')
  98. self.MockList(os.path, os_path_to_mock)
  99. self.MockList(shutil, ('rmtree'))
  100. self.MockList(subprocess, ('call', 'Popen'))
  101. # Don't mock stderr since it confuses unittests.
  102. self.MockList(sys, ('stdin'))
  103. StdoutCheck.setUp(self)
  104. def tearDown(self):
  105. StdoutCheck.tearDown(self)
  106. TestCaseUtils.tearDown(self)
  107. mox.MoxTestBase.tearDown(self)
  108. def MockList(self, parent, items_to_mock):
  109. for item in items_to_mock:
  110. # Skip over items not present because of OS-specific implementation,
  111. # implemented only in later python version, etc.
  112. if hasattr(parent, item):
  113. try:
  114. self.mox.StubOutWithMock(parent, item)
  115. except TypeError, e:
  116. raise TypeError(
  117. 'Couldn\'t mock %s in %s: %s' % (item, parent.__name__, e))
  118. def UnMock(self, obj, name):
  119. """Restore an object inside a test."""
  120. for (parent, old_child, child_name) in self.mox.stubs.cache:
  121. if parent == obj and child_name == name:
  122. setattr(parent, child_name, old_child)
  123. break