diff --git a/gclient.py b/gclient.py
index f48f13b33223db9cc5d786c6148610c72a8d528d..71cb00804b00d93604f5c2b327ccf7caea413d65 100755
--- a/gclient.py
+++ b/gclient.py
@@ -64,7 +64,7 @@ Hooks
 """
 
 __author__ = "darinf@gmail.com (Darin Fisher)"
-__version__ = "0.3.1"
+__version__ = "0.3.2"
 
 import errno
 import optparse
@@ -397,7 +397,7 @@ def RemoveDirectory(*path):
     os.rmdir(file_path)
 
 
-def SubprocessCall(command, in_directory, out, fail_status=None):
+def SubprocessCall(command, in_directory, fail_status=None):
   """Runs command, a list, in directory in_directory.
 
   This function wraps SubprocessCallAndCapture, but does not perform the
@@ -405,10 +405,10 @@ def SubprocessCall(command, in_directory, out, fail_status=None):
   description.
   """
   # Call subprocess and capture nothing:
-  SubprocessCallAndCapture(command, in_directory, out, fail_status)
+  SubprocessCallAndCapture(command, in_directory, fail_status)
 
 
-def SubprocessCallAndCapture(command, in_directory, out, fail_status=None,
+def SubprocessCallAndCapture(command, in_directory, fail_status=None,
                              pattern=None, capture_list=None):
   """Runs command, a list, in directory in_directory.
 
@@ -423,8 +423,8 @@ def SubprocessCallAndCapture(command, in_directory, out, fail_status=None,
   default), gclient will raise an Error exception.
   """
 
-  print >> out, ("\n________ running \'%s\' in \'%s\'"
-      % (' '.join(command), in_directory))
+  print("\n________ running \'%s\' in \'%s\'"
+        % (' '.join(command), in_directory))
 
   # *Sigh*:  Windows needs shell=True, or else it won't search %PATH% for the
   # executable, but shell=True makes subprocess on Linux fail when it's called
@@ -475,7 +475,7 @@ def IsUsingGit(root, paths):
 # SVN utils:
 
 
-def RunSVN(options, args, in_directory):
+def RunSVN(args, in_directory):
   """Runs svn, sending output to stdout.
 
   Args:
@@ -488,10 +488,10 @@ def RunSVN(options, args, in_directory):
   c = [SVN_COMMAND]
   c.extend(args)
 
-  SubprocessCall(c, in_directory, options.stdout)
+  SubprocessCall(c, in_directory)
 
 
-def CaptureSVN(options, args, in_directory):
+def CaptureSVN(args, in_directory):
   """Runs svn, capturing output sent to stdout as a string.
 
   Args:
@@ -508,11 +508,13 @@ def CaptureSVN(options, args, in_directory):
   # the svn.exe executable, but shell=True makes subprocess on Linux fail
   # when it's called with a list because it only tries to execute the
   # first string ("svn").
-  return subprocess.Popen(c, cwd=in_directory, shell=(sys.platform == 'win32'),
+  return subprocess.Popen(c,
+                          cwd=in_directory,
+                          shell=(sys.platform == 'win32'),
                           stdout=subprocess.PIPE).communicate()[0]
 
 
-def RunSVNAndGetFileList(options, args, in_directory, file_list):
+def RunSVNAndGetFileList(args, in_directory, file_list):
   """Runs svn checkout, update, or status, output to stdout.
 
   The first item in args must be either "checkout", "update", or "status".
@@ -552,11 +554,13 @@ def RunSVNAndGetFileList(options, args, in_directory, file_list):
         'update':   update_pattern,
       }[args[0]]
 
-  SubprocessCallAndCapture(command, in_directory, options.stdout,
-                           pattern=pattern, capture_list=file_list)
+  SubprocessCallAndCapture(command,
+                           in_directory,
+                           pattern=pattern,
+                           capture_list=file_list)
 
 
-def CaptureSVNInfo(options, relpath, in_directory):
+def CaptureSVNInfo(relpath, in_directory=None):
   """Returns a dictionary from the svn info output for the given file.
 
   Args:
@@ -564,7 +568,8 @@ def CaptureSVNInfo(options, relpath, in_directory):
       the directory given by in_directory.
     in_directory: The directory where svn is to be run.
   """
-  dom = ParseXML(CaptureSVN(options, ["info", "--xml", relpath], in_directory))
+  output = CaptureSVN(["info", "--xml", relpath], in_directory)
+  dom = ParseXML(output)
   result = {}
   if dom:
     def C(item, f):
@@ -592,13 +597,13 @@ def CaptureSVNInfo(options, relpath, in_directory):
   return result
 
 
-def CaptureSVNHeadRevision(options, url):
+def CaptureSVNHeadRevision(url):
   """Get the head revision of a SVN repository.
 
   Returns:
     Int head revision
   """
-  info = CaptureSVN(options, ["info", "--xml", url], os.getcwd())
+  info = CaptureSVN(["info", "--xml", url], os.getcwd())
   dom = xml.dom.minidom.parseString(info)
   return int(dom.getElementsByTagName('entry')[0].getAttribute('revision'))
 
@@ -617,7 +622,7 @@ class FileStatus:
             self.path)
 
 
-def CaptureSVNStatus(options, path):
+def CaptureSVNStatus(path):
   """Runs 'svn status' on an existing path.
 
   Args:
@@ -626,7 +631,7 @@ def CaptureSVNStatus(options, path):
   Returns:
     An array of FileStatus corresponding to the emulated output of 'svn status'
     version 1.5."""
-  dom = ParseXML(CaptureSVN(options, ["status", "--xml"], path))
+  dom = ParseXML(CaptureSVN(["status", "--xml"], path))
   results = []
   if dom:
     # /status/target/entry/(wc-status|commit|author|date)
@@ -728,13 +733,13 @@ class SCMWrapper(object):
     """Cleanup working copy."""
     command = ['cleanup']
     command.extend(args)
-    RunSVN(options, command, os.path.join(self._root_dir, self.relpath))
+    RunSVN(command, os.path.join(self._root_dir, self.relpath))
 
   def diff(self, options, args, file_list):
     # NOTE: This function does not currently modify file_list.
     command = ['diff']
     command.extend(args)
-    RunSVN(options, command, os.path.join(self._root_dir, self.relpath))
+    RunSVN(command, os.path.join(self._root_dir, self.relpath))
 
   def update(self, options, args, file_list):
     """Runs SCM to update or transparently checkout the working copy.
@@ -747,8 +752,7 @@ class SCMWrapper(object):
     # Only update if git is not controlling the directory.
     git_path = os.path.join(self._root_dir, self.relpath, '.git')
     if options.path_exists(git_path):
-      print >> options.stdout, (
-          "________ found .git directory; skipping %s" % self.relpath)
+      print("________ found .git directory; skipping %s" % self.relpath)
       return
 
     if args:
@@ -776,23 +780,22 @@ class SCMWrapper(object):
       command = ['checkout', url, os.path.join(self._root_dir, self.relpath)]
       if revision:
         command.extend(['--revision', str(revision)])
-      RunSVNAndGetFileList(options, command, self._root_dir, file_list)
+      RunSVNAndGetFileList(command, self._root_dir, file_list)
       return
 
     # Get the existing scm url and the revision number of the current checkout.
-    from_info = CaptureSVNInfo(options,
-                               os.path.join(self._root_dir, self.relpath, '.'),
+    from_info = CaptureSVNInfo(os.path.join(self._root_dir, self.relpath, '.'),
                                '.')
 
     if options.manually_grab_svn_rev:
       # Retrieve the current HEAD version because svn is slow at null updates.
       if not revision:
-        from_info_live = CaptureSVNInfo(options, from_info['URL'], '.')
+        from_info_live = CaptureSVNInfo(from_info['URL'], '.')
         revision = int(from_info_live['Revision'])
         rev_str = ' at %d' % revision
 
     if from_info['URL'] != components[0]:
-      to_info = CaptureSVNInfo(options, url, '.')
+      to_info = CaptureSVNInfo(url, '.')
       if from_info['Repository Root'] != to_info['Repository Root']:
         # We have different roots, so check if we can switch --relocate.
         # Subversion only permits this if the repository UUIDs match.
@@ -814,7 +817,7 @@ class SCMWrapper(object):
                    from_info['Repository Root'],
                    to_info['Repository Root'],
                    self.relpath]
-        RunSVN(options, command, self._root_dir)
+        RunSVN(command, self._root_dir)
         from_info['URL'] = from_info['URL'].replace(
             from_info['Repository Root'],
             to_info['Repository Root'])
@@ -823,14 +826,13 @@ class SCMWrapper(object):
     # number of the existing directory, then we don't need to bother updating.
     if not options.force and from_info['Revision'] == revision:
       if options.verbose or not forced_revision:
-        print >>options.stdout, ("\n_____ %s%s" % (
-            self.relpath, rev_str))
+        print("\n_____ %s%s" % (self.relpath, rev_str))
       return
 
     command = ["update", os.path.join(self._root_dir, self.relpath)]
     if revision:
       command.extend(['--revision', str(revision)])
-    RunSVNAndGetFileList(options, command, self._root_dir, file_list)
+    RunSVNAndGetFileList(command, self._root_dir, file_list)
 
   def revert(self, options, args, file_list):
     """Reverts local modifications. Subversion specific.
@@ -842,17 +844,16 @@ class SCMWrapper(object):
     if not os.path.isdir(path):
       # svn revert won't work if the directory doesn't exist. It needs to
       # checkout instead.
-      print >>options.stdout, ("\n_____ %s is missing, synching instead" %
-                               self.relpath)
+      print("\n_____ %s is missing, synching instead" % self.relpath)
       # Don't reuse the args.
       return self.update(options, [], file_list)
 
-    files = CaptureSVNStatus(options, path)
+    files = CaptureSVNStatus(path)
     # Batch the command.
     files_to_revert = []
     for file in files:
       file_path = os.path.join(path, file.path)
-      print >>options.stdout, file_path
+      print(file_path)
       # Unversioned file or unexpected unversioned file.
       if file.text_status in ('?', '~'):
         # Remove extraneous file. Also remove unexpected unversioned
@@ -876,7 +877,7 @@ class SCMWrapper(object):
       for p in files_to_revert:
         # Some shell have issues with command lines too long.
         if accumulated_length and accumulated_length + len(p) > 3072:
-          RunSVN(options, command + accumulated_paths,
+          RunSVN(command + accumulated_paths,
                  os.path.join(self._root_dir, self.relpath))
           accumulated_paths = []
           accumulated_length = 0
@@ -884,7 +885,7 @@ class SCMWrapper(object):
           accumulated_paths.append(p)
           accumulated_length += len(p)
       if accumulated_paths:
-        RunSVN(options, command + accumulated_paths,
+        RunSVN(command + accumulated_paths,
                os.path.join(self._root_dir, self.relpath))
 
   def status(self, options, args, file_list):
@@ -894,13 +895,12 @@ class SCMWrapper(object):
     command.extend(args)
     if not os.path.isdir(path):
       # svn status won't work if the directory doesn't exist.
-      print >> options.stdout, (
-          "\n________ couldn't run \'%s\' in \'%s\':\nThe directory "
-          "does not exist."
-          % (' '.join(command), path))
+      print("\n________ couldn't run \'%s\' in \'%s\':\nThe directory "
+            "does not exist."
+            % (' '.join(command), path))
       # There's no file list to retrieve.
     else:
-      RunSVNAndGetFileList(options, command, path, file_list)
+      RunSVNAndGetFileList(command, path, file_list)
 
 
 ## GClient implementation.
@@ -1181,8 +1181,7 @@ class GClient(object):
     # Use a discrete exit status code of 2 to indicate that a hook action
     # failed.  Users of this script may wish to treat hook action failures
     # differently from VC failures.
-    SubprocessCall(command, self._root_dir, self._options.stdout,
-                   fail_status=2)
+    SubprocessCall(command, self._root_dir, fail_status=2)
 
   def _RunHooks(self, command, file_list, is_using_git):
     """Evaluates all hooks, running actions as needed.
@@ -1325,16 +1324,15 @@ class GClient(object):
       for entry in prev_entries:
         e_dir = os.path.join(self._root_dir, entry)
         if entry not in entries and self._options.path_exists(e_dir):
-          if CaptureSVNStatus(self._options, e_dir):
+          if CaptureSVNStatus(e_dir):
             # There are modified files in this entry
             entries[entry] = None  # Keep warning until removed.
-            print >> self._options.stdout, (
-                "\nWARNING: \"%s\" is no longer part of this client.  "
-                "It is recommended that you manually remove it.\n") % entry
+            print("\nWARNING: \"%s\" is no longer part of this client.  "
+                  "It is recommended that you manually remove it.\n") % entry
           else:
             # Delete the entry
-            print >> self._options.stdout, ("\n________ deleting \'%s\' " +
-                "in \'%s\'") % (entry, self._root_dir)
+            print("\n________ deleting \'%s\' " +
+                  "in \'%s\'") % (entry, self._root_dir)
             RemoveDirectory(e_dir)
       # record the current list of entries for next time
       self._SaveEntries(entries)
@@ -1383,8 +1381,7 @@ class GClient(object):
           return (original_url, int(revision_overrides[name]))
         else:
           # TODO(aharper): SVN/SCMWrapper cleanup (non-local commandset)
-          return (original_url, CaptureSVNHeadRevision(self._options,
-                                                       original_url))
+          return (original_url, CaptureSVNHeadRevision(original_url))
       else:
         url_components = original_url.split("@")
         if revision_overrides.has_key(name):
@@ -1401,7 +1398,6 @@ class GClient(object):
       entries[name] = "%s@%d" % (url, rev)
       # TODO(aharper): SVN/SCMWrapper cleanup (non-local commandset)
       entries_deps_content[name] = CaptureSVN(
-                                     self._options,
                                      ["cat",
                                       "%s/%s@%d" % (url,
                                                     self._options.deps_file,
@@ -1429,7 +1425,6 @@ class GClient(object):
         deps_parent_url_components = deps_parent_url.split("@")
         # TODO(aharper): SVN/SCMWrapper cleanup (non-local commandset)
         deps_parent_content = CaptureSVN(
-                                self._options,
                                 ["cat",
                                  "%s/%s@%s" % (deps_parent_url_components[0],
                                                self._options.deps_file,
@@ -1444,7 +1439,7 @@ class GClient(object):
         (url, rev) = GetURLAndRev(d, sub_deps[d])
         entries[d] = "%s@%d" % (url, rev)
 
-    print ";".join(["%s,%s" % (x, entries[x]) for x in sorted(entries.keys())])
+    print(";".join(["%s,%s" % (x, entries[x]) for x in sorted(entries.keys())]))
 
 
 ## gclient commands.
@@ -1462,7 +1457,7 @@ def DoCleanup(options, args):
   if options.verbose:
     # Print out the .gclient file.  This is longer than if we just printed the
     # client dict, but more legible, and it might contain helpful comments.
-    print >>options.stdout, client.ConfigContent()
+    print(client.ConfigContent())
   options.verbose = True
   return client.RunOnDeps('cleanup', args)
 
@@ -1505,7 +1500,7 @@ def DoHelp(options, args):
     Error: if the command is unknown.
   """
   if len(args) == 1 and args[0] in COMMAND_USAGE_TEXT:
-    print >>options.stdout, COMMAND_USAGE_TEXT[args[0]]
+    print(COMMAND_USAGE_TEXT[args[0]])
   else:
     raise Error("unknown subcommand '%s'; see 'gclient help'" % args[0])
 
@@ -1522,7 +1517,7 @@ def DoStatus(options, args):
   if options.verbose:
     # Print out the .gclient file.  This is longer than if we just printed the
     # client dict, but more legible, and it might contain helpful comments.
-    print >>options.stdout, client.ConfigContent()
+    print(client.ConfigContent())
   options.verbose = True
   return client.RunOnDeps('status', args)
 
@@ -1561,7 +1556,7 @@ def DoUpdate(options, args):
   if options.verbose:
     # Print out the .gclient file.  This is longer than if we just printed the
     # client dict, but more legible, and it might contain helpful comments.
-    print >>options.stdout, client.ConfigContent()
+    print(client.ConfigContent())
   return client.RunOnDeps('update', args)
 
 
@@ -1577,7 +1572,7 @@ def DoDiff(options, args):
   if options.verbose:
     # Print out the .gclient file.  This is longer than if we just printed the
     # client dict, but more legible, and it might contain helpful comments.
-    print >>options.stdout, client.ConfigContent()
+    print(client.ConfigContent())
   options.verbose = True
   return client.RunOnDeps('diff', args)
 
@@ -1606,7 +1601,7 @@ def DoRunHooks(options, args):
   if options.verbose:
     # Print out the .gclient file.  This is longer than if we just printed the
     # client dict, but more legible, and it might contain helpful comments.
-    print >>options.stdout, client.ConfigContent()
+    print(client.ConfigContent())
   return client.RunOnDeps('runhooks', args)
 
 
@@ -1705,8 +1700,6 @@ def Main(argv):
   options.entries_filename = ".gclient_entries"
   options.deps_file = "DEPS"
 
-  # These are overridded when testing. They are not externally visible.
-  options.stdout = sys.stdout
   options.path_exists = os.path.exists
   options.gclient = GClient
   options.scm_wrapper = SCMWrapper
@@ -1718,7 +1711,7 @@ if "__main__" == __name__:
   try:
     result = Main(sys.argv)
   except Error, e:
-    print "Error: %s" % str(e)
+    print >> sys.stderr, "Error: %s" % str(e)
     result = 1
   sys.exit(result)
 
diff --git a/tests/gclient_test.py b/tests/gclient_test.py
index 9d3719dd9b3e929fc47eba2707e5ddf019427fb4..2a1d0d33a457bfe7e6d53cad21e32bb3957cdc5d 100644
--- a/tests/gclient_test.py
+++ b/tests/gclient_test.py
@@ -77,6 +77,21 @@ class BaseTestCase(unittest.TestCase):
     else:
       self.fail('%s not raised' % msg)
 
+  def compareMembers(self, object, members):
+    """If you add a member, be sure to add the relevant test!"""
+    # Skip over members starting with '_' since they are usually not meant to
+    # be for public use.
+    actual_members = [x for x in sorted(dir(object))
+                      if not x.startswith('_')]
+    expected_members = sorted(members)
+    if actual_members != expected_members:
+      diff = ([i for i in actual_members if i not in expected_members] +
+              [i for i in expected_members if i not in actual_members])
+      print diff
+    self.assertEqual(actual_members, expected_members)
+
+
+class GClientBaseTestCase(BaseTestCase):
   def Options(self, *args, **kwargs):
     return self.OptionsObject(self, *args, **kwargs)
 
@@ -99,11 +114,8 @@ class BaseTestCase(unittest.TestCase):
     gclient.RunSVN = self.mox.CreateMockAnything()
     self._RunSVNAndGetFileList = gclient.RunSVNAndGetFileList
     gclient.RunSVNAndGetFileList = self.mox.CreateMockAnything()
-    # Doesn't seem to work very well:
-    self._os = gclient.os
-    gclient.os = self.mox.CreateMock(os)
-    self._sys = gclient.sys
-    gclient.sys = self.mox.CreateMock(sys)
+    self._sys_stdout = gclient.sys.stdout
+    gclient.sys.stdout = self.mox.CreateMock(self._sys_stdout)
     self._subprocess = gclient.subprocess
     gclient.subprocess = self.mox.CreateMock(subprocess)
 
@@ -116,13 +128,11 @@ class BaseTestCase(unittest.TestCase):
     gclient.RemoveDirectory = self._RemoveDirectory
     gclient.RunSVN = self._RunSVN
     gclient.RunSVNAndGetFileList = self._RunSVNAndGetFileList
-    # Doesn't seem to work very well:
-    gclient.os = self._os
-    gclient.sys = self._sys
+    gclient.sys.stdout = self._sys_stdout
     gclient.subprocess = self._subprocess
 
 
-class GclientTestCase(BaseTestCase):
+class GclientTestCase(GClientBaseTestCase):
   class OptionsObject(object):
     def __init__(self, test_case, verbose=False, spec=None,
                  config_filename='a_file_name',
@@ -140,19 +150,16 @@ class GclientTestCase(BaseTestCase):
       self.head = False
 
       # Mox
-      self.stdout = test_case.stdout
       self.path_exists = test_case.path_exists
       self.platform = test_case.platform
       self.gclient = test_case.gclient
       self.scm_wrapper = test_case.scm_wrapper
 
   def setUp(self):
-    BaseTestCase.setUp(self)
-    self.stdout = self.mox.CreateMock(sys.stdout)
+    GClientBaseTestCase.setUp(self)
     #self.subprocess = self.mox.CreateMock(subprocess)
     # Stub os.path.exists.
     self.path_exists = self.mox.CreateMockAnything()
-    self.sys = self.mox.CreateMock(sys)
     self.platform = 'darwin'
 
     self.gclient = self.mox.CreateMock(gclient.GClient)
@@ -163,7 +170,7 @@ class GclientTestCase(BaseTestCase):
     self.url = Url()
 
 
-class GClientCommandsTestCase(BaseTestCase):
+class GClientCommandsTestCase(GClientBaseTestCase):
   def testCommands(self):
     known_commands = [gclient.DoCleanup, gclient.DoConfig, gclient.DoDiff,
                       gclient.DoHelp, gclient.DoStatus, gclient.DoUpdate,
@@ -224,24 +231,23 @@ class TestDoConfig(GclientTestCase):
 
 class TestDoHelp(GclientTestCase):
   def testGetUsage(self):
-    options = self.Options()
-    print >> options.stdout, gclient.COMMAND_USAGE_TEXT['config']
-
+    print(gclient.COMMAND_USAGE_TEXT['config'])
     self.mox.ReplayAll()
+    options = self.Options()
     gclient.DoHelp(options, ('config',))
     self.mox.VerifyAll()
 
   def testTooManyArgs(self):
-    options = self.Options()
     self.mox.ReplayAll()
+    options = self.Options()
     self.assertRaisesError("unknown subcommand 'config'; see 'gclient help'",
                            gclient.DoHelp, options, ('config',
                                                      'another argument'))
     self.mox.VerifyAll()
 
   def testUnknownSubcommand(self):
-    options = self.Options()
     self.mox.ReplayAll()
+    options = self.Options()
     self.assertRaisesError("unknown subcommand 'xyzzy'; see 'gclient help'",
                            gclient.DoHelp, options, ('xyzzy',))
     self.mox.VerifyAll()
@@ -273,7 +279,7 @@ class GenericCommandTestCase(GclientTestCase):
     self.gclient.LoadCurrentConfig(options).AndReturn(self.gclient)
     text = "# Dummy content\nclient = 'my client'"
     self.gclient.ConfigContent().AndReturn(text)
-    print >>self.stdout, text
+    print(text)
     self.gclient.RunOnDeps(command, self.args).AndReturn(0)
 
     self.mox.ReplayAll()
@@ -331,7 +337,7 @@ class TestDoUpdate(GenericCommandTestCase):
     self.gclient.GetVar("solutions")
     text = "# Dummy content\nclient = 'my client'"
     self.gclient.ConfigContent().AndReturn(text)
-    print >>self.stdout, text
+    print(text)
     self.gclient.RunOnDeps(command, self.args).AndReturn(0)
 
     self.mox.ReplayAll()
@@ -377,18 +383,14 @@ class TestDoRevert(GenericCommandTestCase):
 
 class GClientClassTestCase(GclientTestCase):
   def testDir(self):
-    members = ['ConfigContent', 'FromImpl', '_VarImpl', '_ParseAllDeps',
-      '_ParseSolutionDeps', 'GetVar', '_LoadConfig', 'LoadCurrentConfig',
-      '_ReadEntries', '_RunHookAction', '_RunHooks', 'RunOnDeps', 'SaveConfig',
-      '_SaveEntries', 'SetConfig', 'SetDefaultConfig', 'supported_commands',
-      'PrintRevInfo']
+    members = [
+      'ConfigContent', 'FromImpl', 'GetVar', 'LoadCurrentConfig',
+      'RunOnDeps', 'SaveConfig', 'SetConfig', 'SetDefaultConfig',
+      'supported_commands', 'PrintRevInfo',
+    ]
 
     # If you add a member, be sure to add the relevant test!
-    actual_members = [x for x in sorted(dir(gclient.GClient))
-                      if not x.startswith('__')]
-    self.assertEqual(actual_members, sorted(members))
-    self.mox.ReplayAll()
-    self.mox.VerifyAll()
+    self.compareMembers(gclient.GClient('root_dir', 'options'), members)
 
   def testSetConfig_ConfigContent_GetVar_SaveConfig_SetDefaultConfig(self):
     options = self.Options()
@@ -731,11 +733,11 @@ class GClientClassTestCase(GclientTestCase):
   def testRunOnDepsRevisions(self):
     def OptIsRev(options, rev):
       if not options.revision == str(rev):
-        print "options.revision = %s" % options.revision
+        print("options.revision = %s" % options.revision)
       return options.revision == str(rev)
     def OptIsRevNone(options):
       if options.revision:
-        print "options.revision = %s" % options.revision
+        print("options.revision = %s" % options.revision)
       return options.revision == None
     def OptIsRev42(options):
       return OptIsRev(options, 42)
@@ -1058,7 +1060,7 @@ deps = {
     pass
 
 
-class SCMWrapperTestCase(BaseTestCase):
+class SCMWrapperTestCase(GClientBaseTestCase):
   class OptionsObject(object):
      def __init__(self, test_case, verbose=False, revision=None):
       self.verbose = verbose
@@ -1068,29 +1070,25 @@ class SCMWrapperTestCase(BaseTestCase):
       self.force = False
 
       # Mox
-      self.stdout = test_case.stdout
       self.path_exists = test_case.path_exists
 
   def setUp(self):
-    BaseTestCase.setUp(self)
+    GClientBaseTestCase.setUp(self)
     self.root_dir = Dir()
     self.args = Args()
     self.url = Url()
     self.relpath = 'asf'
-    self.stdout = self.mox.CreateMock(sys.stdout)
     # Stub os.path.exists.
     self.path_exists = self.mox.CreateMockAnything()
 
   def testDir(self):
-    members = ['FullUrlForRelativeUrl', 'RunCommand',
-      'cleanup', 'diff', 'revert', 'status', 'update']
+    members = [
+      'FullUrlForRelativeUrl', 'RunCommand', 'cleanup', 'diff', 'relpath',
+      'revert', 'scm_name', 'status', 'update', 'url',
+    ]
 
     # If you add a member, be sure to add the relevant test!
-    actual_members = [x for x in sorted(dir(gclient.SCMWrapper))
-                      if not x.startswith('__')]
-    self.assertEqual(actual_members, sorted(members))
-    self.mox.ReplayAll()
-    self.mox.VerifyAll()
+    self.compareMembers(gclient.SCMWrapper(), members)
 
   def testFullUrlForRelativeUrl(self):
     self.url = 'svn://a/b/c/d'
@@ -1125,12 +1123,11 @@ class SCMWrapperTestCase(BaseTestCase):
     gclient.os.path.isdir(base_path).AndReturn(False)
     # It'll to a checkout instead.
     options.path_exists(os.path.join(base_path, '.git')).AndReturn(False)
-    print >>options.stdout, ("\n_____ %s is missing, synching instead" %
-                             self.relpath)
+    print("\n_____ %s is missing, synching instead" % self.relpath)
     # Checkout.
     options.path_exists(base_path).AndReturn(False)
     files_list = self.mox.CreateMockAnything()
-    gclient.RunSVNAndGetFileList(options, ['checkout', self.url, base_path],
+    gclient.RunSVNAndGetFileList(['checkout', self.url, base_path],
                                  self.root_dir, files_list)
 
     self.mox.ReplayAll()
@@ -1145,7 +1142,7 @@ class SCMWrapperTestCase(BaseTestCase):
     base_path = os.path.join(self.root_dir, self.relpath)
     gclient.os.path.isdir = self.mox.CreateMockAnything()
     gclient.os.path.isdir(base_path).AndReturn(True)
-    gclient.CaptureSVNStatus(options, base_path).AndReturn([])
+    gclient.CaptureSVNStatus(base_path).AndReturn([])
 
     self.mox.ReplayAll()
     scm = gclient.SCMWrapper(url=self.url, root_dir=self.root_dir,
@@ -1164,11 +1161,11 @@ class SCMWrapperTestCase(BaseTestCase):
       gclient.FileStatus('a', 'M', ' ', ' ', ' '), 
       gclient.FileStatus('b', 'A', ' ', ' ', ' '),
     ]
-    gclient.CaptureSVNStatus(options, base_path).AndReturn(items)
+    gclient.CaptureSVNStatus(base_path).AndReturn(items)
 
-    print >>options.stdout, os.path.join(base_path, 'a')
-    print >>options.stdout, os.path.join(base_path, 'b')
-    gclient.RunSVN(options, ['revert', 'a', 'b'], base_path)
+    print(os.path.join(base_path, 'a'))
+    print(os.path.join(base_path, 'b'))
+    gclient.RunSVN(['revert', 'a', 'b'], base_path)
 
     self.mox.ReplayAll()
     scm = gclient.SCMWrapper(url=self.url, root_dir=self.root_dir,
@@ -1183,7 +1180,7 @@ class SCMWrapperTestCase(BaseTestCase):
     base_path = os.path.join(self.root_dir, self.relpath)
     gclient.os.path.isdir = self.mox.CreateMockAnything()
     gclient.os.path.isdir(base_path).AndReturn(True)
-    gclient.RunSVNAndGetFileList(options, ['status'] + self.args, base_path,
+    gclient.RunSVNAndGetFileList(['status'] + self.args, base_path,
                                  []).AndReturn(None)
 
     self.mox.ReplayAll()
@@ -1208,7 +1205,7 @@ class SCMWrapperTestCase(BaseTestCase):
     # Checkout.
     options.path_exists(base_path).AndReturn(False)
     files_list = self.mox.CreateMockAnything()
-    gclient.RunSVNAndGetFileList(options, ['checkout', self.url, base_path],
+    gclient.RunSVNAndGetFileList(['checkout', self.url, base_path],
                                  self.root_dir, files_list)
     self.mox.ReplayAll()
     scm = gclient.SCMWrapper(url=self.url, root_dir=self.root_dir,
@@ -1229,15 +1226,15 @@ class SCMWrapperTestCase(BaseTestCase):
     options.path_exists(os.path.join(base_path, '.git')).AndReturn(False)
     # Checkout or update.
     options.path_exists(base_path).AndReturn(True)
-    gclient.CaptureSVNInfo(options, os.path.join(base_path, "."), '.'
+    gclient.CaptureSVNInfo(os.path.join(base_path, "."), '.'
         ).AndReturn(file_info)
     # Cheat a bit here.
-    gclient.CaptureSVNInfo(options, file_info['URL'], '.').AndReturn(file_info)
+    gclient.CaptureSVNInfo(file_info['URL'], '.').AndReturn(file_info)
     additional_args = []
     if options.manually_grab_svn_rev:
       additional_args = ['--revision', str(file_info['Revision'])]
     files_list = []
-    gclient.RunSVNAndGetFileList(options, ['update', base_path] + additional_args,
+    gclient.RunSVNAndGetFileList(['update', base_path] + additional_args,
                                  self.root_dir, files_list)
 
     self.mox.ReplayAll()
@@ -1250,8 +1247,7 @@ class SCMWrapperTestCase(BaseTestCase):
     options = self.Options(verbose=True)
     options.path_exists(os.path.join(self.root_dir, self.relpath, '.git')
         ).AndReturn(True)
-    print >> options.stdout, (
-        "________ found .git directory; skipping %s" % self.relpath)
+    print("________ found .git directory; skipping %s" % self.relpath)
 
     self.mox.ReplayAll()
     scm = gclient.SCMWrapper(url=self.url, root_dir=self.root_dir,
@@ -1276,9 +1272,7 @@ class SCMWrapperTestCase(BaseTestCase):
 </entry>
 </info>
 """ % self.url
-    options = self.Options(verbose=True)
-    gclient.CaptureSVN(options, ['info', '--xml', self.url],
-                       '.').AndReturn(xml_text)
+    gclient.CaptureSVN(['info', '--xml', self.url], '.').AndReturn(xml_text)
     expected = {
       'URL': 'http://src.chromium.org/svn/trunk/src/chrome/app/d',
       'UUID': None,
@@ -1291,7 +1285,7 @@ class SCMWrapperTestCase(BaseTestCase):
       'Node Kind': 'file',
     }
     self.mox.ReplayAll()
-    file_info = self._CaptureSVNInfo(options, self.url, '.')
+    file_info = self._CaptureSVNInfo(self.url, '.')
     self.assertEquals(sorted(file_info.items()), sorted(expected.items()))
     self.mox.VerifyAll()
 
@@ -1319,11 +1313,9 @@ class SCMWrapperTestCase(BaseTestCase):
 </entry>
 </info>
 """ % (self.url, self.root_dir)
-    options = self.Options(verbose=True)
-    gclient.CaptureSVN(options, ['info', '--xml', self.url],
-                       '.').AndReturn(xml_text)
+    gclient.CaptureSVN(['info', '--xml', self.url], '.').AndReturn(xml_text)
     self.mox.ReplayAll()
-    file_info = self._CaptureSVNInfo(options, self.url, '.')
+    file_info = self._CaptureSVNInfo(self.url, '.')
     expected = {
       'URL': self.url,
       'UUID': '7b9385f5-0452-0410-af26-ad4892b7a1fb',
@@ -1339,6 +1331,23 @@ class SCMWrapperTestCase(BaseTestCase):
     self.mox.VerifyAll()
 
 
+class RunSVNTestCase(BaseTestCase):
+  def setUp(self):
+    self.mox = mox.Mox()
+    self._OldSubprocessCall = gclient.SubprocessCall
+    gclient.SubprocessCall = self.mox.CreateMockAnything()
+
+  def tearDown(self):
+    gclient.SubprocessCall = self._OldSubprocessCall
+
+  def testRunSVN(self):
+    param2 = 'bleh'
+    gclient.SubprocessCall(['svn', 'foo', 'bar'], param2).AndReturn(None)
+    self.mox.ReplayAll()
+    gclient.RunSVN(['foo', 'bar'], param2)
+    self.mox.VerifyAll()
+
+
 if __name__ == '__main__':
   unittest.main()