class MercurialSubversionClientTests(MercurialTestBase): """Unit tests for hgsubversion.""" TESTSERVER = "http://127.0.0.1:8080" def __init__(self, *args, **kwargs): self._tmpbase = '' self.clone_dir = '' self.svn_repo = '' self.svn_checkout = '' self.client = None self._svnserve_pid = 0 self._max_svnserve_pid_tries = 12 self._svnserve_port = os.environ.get('SVNSERVE_PORT') self._required_exes = ('svnadmin', 'svnserve', 'svn') MercurialTestBase.__init__(self, *args, **kwargs) def setUp(self): super(MercurialSubversionClientTests, self).setUp() self._hg_env = {'FOO': 'BAR'} # Make sure hgsubversion is enabled. # # This will modify the .hgrc in the temp home directory created # for these tests. # # The "hgsubversion =" tells Mercurial to check for hgsubversion # in the default PYTHONPATH. fp = open('%s/.hgrc' % os.environ['HOME'], 'w') fp.write('[extensions]\n') fp.write('hgsubversion =\n') fp.close() for exe in self._required_exes: if not is_exe_in_path(exe): raise SkipTest('missing svn stuff! giving up!') if not self._has_hgsubversion(): raise SkipTest('unable to use `hgsubversion` extension! ' 'giving up!') if not self._tmpbase: self._tmpbase = self.create_tmp_dir() self._create_svn_repo() self._fire_up_svnserve() self._fill_in_svn_repo() try: self._get_testing_clone() except (OSError, IOError): msg = 'could not clone from svn repo! skipping...' raise SkipTest(msg).with_traceback(sys.exc_info()[2]) self._spin_up_client() self._stub_in_config_and_options() def _has_hgsubversion(self): try: output = self._run_hg(['svn', '--help'], ignore_errors=True, extra_ignore_errors=(255)) except OSError: return False return not re.search("unknown command ['\"]svn['\"]", output, re.I) def tearDown(self): super(MercurialSubversionClientTests, self).tearDown() os.kill(self._svnserve_pid, 9) def _svn_add_file_commit(self, filename, data, msg, add_file=True): outfile = open(filename, 'w') outfile.write(data) outfile.close() if add_file: execute(['svn', 'add', filename], ignore_errors=True) execute(['svn', 'commit', '-m', msg]) def _create_svn_repo(self): self.svn_repo = os.path.join(self._tmpbase, 'svnrepo') execute(['svnadmin', 'create', self.svn_repo]) def _fire_up_svnserve(self): if not self._svnserve_port: self._svnserve_port = str(randint(30000, 40000)) pid_file = os.path.join(self._tmpbase, 'svnserve.pid') execute([ 'svnserve', '--pid-file', pid_file, '-d', '--listen-port', self._svnserve_port, '-r', self._tmpbase ]) for i in range(0, self._max_svnserve_pid_tries): try: self._svnserve_pid = int(open(pid_file).read().strip()) return except (IOError, OSError): time.sleep(0.25) # This will re-raise the last exception, which will be either # IOError or OSError if the above fails and this branch is reached raise def _fill_in_svn_repo(self): self.svn_checkout = os.path.join(self._tmpbase, 'checkout.svn') execute([ 'svn', 'checkout', 'file://%s' % self.svn_repo, self.svn_checkout ]) os.chdir(self.svn_checkout) for subtree in ('trunk', 'branches', 'tags'): execute(['svn', 'mkdir', subtree]) execute(['svn', 'commit', '-m', 'filling in T/b/t']) os.chdir(os.path.join(self.svn_checkout, 'trunk')) for i, data in enumerate([FOO, FOO1, FOO2]): self._svn_add_file_commit('foo.txt', data, 'foo commit %s' % i, add_file=(i == 0)) def _get_testing_clone(self): self.clone_dir = os.path.join(self._tmpbase, 'checkout.hg') self._run_hg([ 'clone', 'svn://127.0.0.1:%s/svnrepo' % self._svnserve_port, self.clone_dir, ]) def _spin_up_client(self): os.chdir(self.clone_dir) self.client = MercurialClient(options=self.options) def _stub_in_config_and_options(self): self.options.parent_branch = None def testGetRepositoryInfoSimple(self): """Testing MercurialClient (+svn) get_repository_info, simple case""" ri = self.client.get_repository_info() self.assertEqual('svn', self.client._type) self.assertEqual('/trunk', ri.base_path) self.assertEqual('svn://127.0.0.1:%s/svnrepo' % self._svnserve_port, ri.path) def testCalculateRepositoryInfo(self): """Testing MercurialClient (+svn) _calculate_hgsubversion_repository_info properly determines repository and base paths""" info = ( "URL: svn+ssh://[email protected]/repo/trunk\n" "Repository Root: svn+ssh://[email protected]/repo\n" "Repository UUID: bfddb570-5023-0410-9bc8-bc1659bf7c01\n" "Revision: 9999\n" "Node Kind: directory\n" "Last Changed Author: user\n" "Last Changed Rev: 9999\n" "Last Changed Date: 2012-09-05 18:04:28 +0000 (Wed, 05 Sep 2012)") repo_info = self.client._calculate_hgsubversion_repository_info(info) self.assertEqual(repo_info.path, "svn+ssh://svn.example.net/repo") self.assertEqual(repo_info.base_path, "/trunk") def testScanForServerSimple(self): """Testing MercurialClient (+svn) scan_for_server, simple case""" ri = self.client.get_repository_info() server = self.client.scan_for_server(ri) self.assertTrue(server is None) def testScanForServerReviewboardrc(self): """Testing MercurialClient (+svn) scan_for_server in .reviewboardrc""" rc_filename = os.path.join(self.clone_dir, '.reviewboardrc') rc = open(rc_filename, 'w') rc.write('REVIEWBOARD_URL = "%s"' % self.TESTSERVER) rc.close() self.client.config = load_config() ri = self.client.get_repository_info() server = self.client.scan_for_server(ri) self.assertEqual(self.TESTSERVER, server) def testScanForServerProperty(self): """Testing MercurialClient (+svn) scan_for_server in svn property""" os.chdir(self.svn_checkout) execute(['svn', 'update']) execute([ 'svn', 'propset', 'reviewboard:url', self.TESTSERVER, self.svn_checkout ]) execute(['svn', 'commit', '-m', 'adding reviewboard:url property']) os.chdir(self.clone_dir) self._run_hg(['pull']) self._run_hg(['update', '-C']) ri = self.client.get_repository_info() self.assertEqual(self.TESTSERVER, self.client.scan_for_server(ri)) def testDiffSimple(self): """Testing MercurialClient (+svn) diff, simple case""" self.client.get_repository_info() self._hg_add_file_commit('foo.txt', FOO4, 'edit 4') revisions = self.client.parse_revision_spec([]) result = self.client.diff(revisions) self.assertTrue(isinstance(result, dict)) self.assertTrue('diff' in result) self.assertEqual( md5(result['diff']).hexdigest(), '2eb0a5f2149232c43a1745d90949fcd5') self.assertEqual(result['parent_diff'], None) def testDiffSimpleMultiple(self): """Testing MercurialClient (+svn) diff with multiple commits""" self.client.get_repository_info() self._hg_add_file_commit('foo.txt', FOO4, 'edit 4') self._hg_add_file_commit('foo.txt', FOO5, 'edit 5') self._hg_add_file_commit('foo.txt', FOO6, 'edit 6') revisions = self.client.parse_revision_spec([]) result = self.client.diff(revisions) self.assertTrue(isinstance(result, dict)) self.assertTrue('diff' in result) self.assertEqual( md5(result['diff']).hexdigest(), '3d007394de3831d61e477cbcfe60ece8') self.assertEqual(result['parent_diff'], None) def testDiffOfRevision(self): """Testing MercurialClient (+svn) diff specifying a revision""" self.client.get_repository_info() self._hg_add_file_commit('foo.txt', FOO4, 'edit 4', branch='b') self._hg_add_file_commit('foo.txt', FOO5, 'edit 5', branch='b') self._hg_add_file_commit('foo.txt', FOO6, 'edit 6', branch='b') self._hg_add_file_commit('foo.txt', FOO4, 'edit 7', branch='b') revisions = self.client.parse_revision_spec(['3']) result = self.client.diff(revisions) self.assertTrue(isinstance(result, dict)) self.assertTrue('diff' in result) self.assertEqual( md5(result['diff']).hexdigest(), '2eb0a5f2149232c43a1745d90949fcd5') self.assertEqual(result['parent_diff'], None)
class MercurialSubversionClientTests(MercurialTestBase): """Unit tests for hgsubversion.""" TESTSERVER = 'http://127.0.0.1:8080' SVNSERVE_MAX_RETRIES = 12 _svnserve_pid = None _svn_temp_base_path = None _skip_reason = None @classmethod def setUpClass(cls): for exe in ('svnadmin', 'svnserve', 'svn'): if not is_exe_in_path(exe): cls._skip_reason = '%s is not available on the system.' % exe break else: has_hgsubversion = False try: output = execute([ 'hg', '--config', 'extensions.hgsubversion=', 'svn', '--help' ], ignore_errors=True, extra_ignore_errors=(255, )) has_hgsubversion = \ not re.search('unknown command [\'"]svn[\'"]', output, re.I) except OSError: has_hgsubversion = False if not has_hgsubversion: cls._skip_reason = \ 'hgsubversion is not available or cannot be used.' super(MercurialSubversionClientTests, cls).setUpClass() # Don't do any of the following expensive stuff if we know we're just # going to skip all the tests. if cls._skip_reason: return # Create the repository that we'll be populating and later cloning. temp_base_path = tempfile.mkdtemp(prefix='rbtools.') cls._svn_temp_base_path = temp_base_path svn_repo_path = os.path.join(temp_base_path, 'svnrepo') execute(['svnadmin', 'create', svn_repo_path]) # Fill it with content. First, though, we have to clone it. svn_checkout_path = os.path.join(temp_base_path, 'checkout.svn') execute([ 'svn', 'checkout', 'file://%s' % svn_repo_path, svn_checkout_path ]) os.chdir(svn_checkout_path) execute([ 'svn', 'propset', 'reviewboard:url', cls.TESTSERVER, svn_checkout_path ]) execute(['svn', 'mkdir', 'trunk', 'branches', 'tags']) execute(['svn', 'commit', '-m', 'Initial commit.']) os.chdir(os.path.join(svn_checkout_path, 'trunk')) for i, data in enumerate([FOO, FOO1, FOO2]): cls.svn_add_file_commit(filename='foo.txt', data=data, msg='Test commit %s' % i, add_file=(i == 0)) # Launch svnserve so Mercurial can pull from it. svnserve_port = (os.environ.get('SVNSERVE_PORT') or str(randint(30000, 40000))) pid_file = os.path.join(temp_base_path, 'svnserve.pid') execute([ 'svnserve', '--single-thread', '--pid-file', pid_file, '-d', '--listen-port', svnserve_port, '-r', temp_base_path ]) for i in range(0, cls.SVNSERVE_MAX_RETRIES): try: cls._svnserve_pid = int(open(pid_file).read().strip()) except (IOError, OSError): # Wait to see if svnserve has launched yet. time.sleep(0.25) if not cls._svnserve_pid: raise cls.failureException('Unable to launch svnserve on port %s' % svnserve_port) cls.svn_checkout_url = 'svn://127.0.0.1:%s/svnrepo' % svnserve_port @classmethod def tearDownClass(cls): if cls._svnserve_pid: os.kill(cls._svnserve_pid, 9) if cls._svn_temp_base_path: shutil.rmtree(cls._svn_temp_base_path, ignore_errors=True) super(MercurialSubversionClientTests, cls).tearDownClass() def setUp(self): super(MercurialSubversionClientTests, self).setUp() if self._skip_reason: raise SkipTest(self._skip_reason) home_dir = self.get_user_home() hgrc_path = os.path.join(home_dir, '.hgrc') # Make sure hgsubversion is enabled. # # This will modify the .hgrc in the temp home directory created for # these tests. # # The "hgsubversion =" tells Mercurial to check for hgsubversion in # the default PYTHONPATH with open(hgrc_path, 'w') as fp: fp.write('[extensions]\n') fp.write('hgsubversion =\n') try: self.clone_dir = os.path.join(home_dir, 'checkout.hg') self.run_hg( ['clone', '--stream', self.svn_checkout_url, self.clone_dir]) except (OSError, IOError) as e: self.fail('Unable to clone Subversion repository: %s' % e) os.chdir(self.clone_dir) self.options.parent_branch = None self.client = MercurialClient(options=self.options) @classmethod def svn_add_file_commit(self, filename, data, msg, add_file=True): with open(filename, 'wb') as fp: fp.write(data) if add_file: execute(['svn', 'add', filename], ignore_errors=True) execute(['svn', 'commit', '-m', msg]) def test_get_repository_info(self): """Testing MercurialClient.get_repository_info with SVN""" ri = self.client.get_repository_info() self.assertEqual(self.client._type, 'svn') self.assertEqual(ri.base_path, '/trunk') self.assertEqual(ri.path, self.svn_checkout_url) def test_calculate_repository_info(self): """Testing MercurialClient._calculate_hgsubversion_repository_info with SVN determines repository and base paths """ repo_info = self.client._calculate_hgsubversion_repository_info( 'URL: svn+ssh://[email protected]/repo/trunk\n' 'Repository Root: svn+ssh://[email protected]/repo\n' 'Repository UUID: bfddb570-5023-0410-9bc8-bc1659bf7c01\n' 'Revision: 9999\n' 'Node Kind: directory\n' 'Last Changed Author: user\n' 'Last Changed Rev: 9999\n' 'Last Changed Date: 2012-09-05 18:04:28 +0000 (Wed, 05 Sep 2012)') self.assertEqual(repo_info.path, 'svn+ssh://svn.example.net/repo') self.assertEqual(repo_info.base_path, '/trunk') def test_scan_for_server_with_reviewboardrc(self): """Testing MercurialClient.scan_for_server with SVN and configured .reviewboardrc """ with self.reviewboardrc({'REVIEWBOARD_URL': 'https://example.com/'}): self.client.config = load_config() ri = self.client.get_repository_info() self.assertEqual(self.client.scan_for_server(ri), 'https://example.com/') def test_scan_for_server_with_property(self): """Testing MercurialClient.scan_for_server with SVN and reviewboard:url property """ ri = self.client.get_repository_info() self.assertEqual(self.client.scan_for_server(ri), self.TESTSERVER) def test_diff(self): """Testing MercurialClient.diff with SVN""" self.client.get_repository_info() self.hg_add_file_commit(filename='foo.txt', data=FOO4, msg='edit 4') revisions = self.client.parse_revision_spec([]) result = self.client.diff(revisions) self.assertIsInstance(result, dict) self.assertIn('diff', result) self.assertEqual( md5(result['diff']).hexdigest(), '2eb0a5f2149232c43a1745d90949fcd5') self.assertIsNone(result['parent_diff']) def test_diff_with_multiple_commits(self): """Testing MercurialClient.diff with SVN and multiple commits""" self.client.get_repository_info() self.hg_add_file_commit(filename='foo.txt', data=FOO4, msg='edit 4') self.hg_add_file_commit(filename='foo.txt', data=FOO5, msg='edit 5') self.hg_add_file_commit(filename='foo.txt', data=FOO6, msg='edit 6') revisions = self.client.parse_revision_spec([]) result = self.client.diff(revisions) self.assertIsInstance(result, dict) self.assertIn('diff', result) self.assertEqual( md5(result['diff']).hexdigest(), '3d007394de3831d61e477cbcfe60ece8') self.assertIsNone(result['parent_diff']) def test_diff_with_revision(self): """Testing MercurialClient.diff with SVN and specific revision""" self.client.get_repository_info() self.hg_add_file_commit(filename='foo.txt', data=FOO4, msg='edit 4', branch='b') self.hg_add_file_commit(filename='foo.txt', data=FOO5, msg='edit 5', branch='b') self.hg_add_file_commit(filename='foo.txt', data=FOO6, msg='edit 6', branch='b') self.hg_add_file_commit(filename='foo.txt', data=FOO4, msg='edit 7', branch='b') revisions = self.client.parse_revision_spec(['3']) result = self.client.diff(revisions) self.assertIsInstance(result, dict) self.assertIn('diff', result) self.assertEqual( md5(result['diff']).hexdigest(), '2eb0a5f2149232c43a1745d90949fcd5') self.assertIsNone(result['parent_diff'])