[PATCH 4 of 4] hgmerge: add tests
Steve Borho
steve at borho.org
Mon Jan 7 15:20:37 CST 2008
# HG changeset patch
# User Steve Borho <steve at borho.org>
# Date 1199739440 21600
# Node ID 9fcea1f4e87bdc809729829e61b599af448015bf
# Parent a08b4e537ea09530e1fdcff09cd34bcbc351c1f5
hgmerge: add tests
These tests currently rely on srllib. They will be ported to the standard
test suite before they are merged.
diff --git a/tests/hgmerge/Interactive/base.txt b/tests/hgmerge/Interactive/base.txt
new file mode 100644
--- /dev/null
+++ b/tests/hgmerge/Interactive/base.txt
@@ -0,0 +1,2 @@
+Line 1
+Line 2
diff --git a/tests/hgmerge/Interactive/local.txt b/tests/hgmerge/Interactive/local.txt
new file mode 100644
--- /dev/null
+++ b/tests/hgmerge/Interactive/local.txt
@@ -0,0 +1,2 @@
+Line 1
+My line 2
diff --git a/tests/hgmerge/Interactive/other.txt b/tests/hgmerge/Interactive/other.txt
new file mode 100644
--- /dev/null
+++ b/tests/hgmerge/Interactive/other.txt
@@ -0,0 +1,2 @@
+Line 1
+Your line 2
diff --git a/tests/hgmerge/Interactive/tst.py b/tests/hgmerge/Interactive/tst.py
new file mode 100644
--- /dev/null
+++ b/tests/hgmerge/Interactive/tst.py
@@ -0,0 +1,13 @@
+import sys
+import os.path
+sys.path.insert(0, os.path.realpath(os.path.join('..', '..', '..')))
+
+from mercurial import hgmerge
+
+noninteractive, interactive, extfilter = hgmerge.get_plugins()
+print "%s, %s" % (noninteractive.name, interactive.name)
+print noninteractive.executable, noninteractive.arguments
+
+rslt = hgmerge.merge('base.txt', 'local.txt', 'other.txt', noninteractive,
+ interactive, extfilter)
+print "Got result: %r" % rslt
diff --git a/tests/hgmerge/TODO.txt b/tests/hgmerge/TODO.txt
new file mode 100644
--- /dev/null
+++ b/tests/hgmerge/TODO.txt
@@ -0,0 +1,2 @@
+* Test HGMERGE_TOOL
+* Test 'ext' option
diff --git a/tests/hgmerge/_common.py b/tests/hgmerge/_common.py
new file mode 100644
--- /dev/null
+++ b/tests/hgmerge/_common.py
@@ -0,0 +1,112 @@
+import sys
+import os
+import srllib.testing as srltesting
+import tempfile
+import warnings
+
+sys.path.insert(0, os.path.realpath(os.path.join(os.path.dirname(__file__),
+ '..', '..')))
+from mercurial import hgmerge
+
+class MthdMock:
+ def __init__(self, returnval=None, func=None):
+ self.mock_args = self.mock_kwds = None
+ self.__retval = returnval
+ self.__func = func
+
+ def __call__(self, *args, **kwds):
+ self.mock_args = args
+ self.mock_kwds = kwds
+ if self.__func is not None:
+ return self.__func(*args, **kwds)
+ return self.__retval
+
+ @property
+ def mock_is_called(self):
+ return self.mock_args is not None
+
+ def mock_set_returnvalue(self, retval):
+ self.__retval = retval
+
+class FakeUi(object):
+ def __init__(self, conf=None, globalconf=None):
+ if conf is None: conf = {}
+ if globalconf is None: globalconf = {}
+ self.__conf = conf
+ self.__globalconf = globalconf
+ self.warn = MthdMock()
+ self.info = MthdMock()
+ self.write = MthdMock()
+ self.prompt = MthdMock()
+ self.debug = MthdMock()
+
+ def configitems(self, key):
+ if key == 'hgmerge-plugins':
+ return self.__conf.items()
+ elif key == 'hgmerge':
+ return self.__globalconf.items()
+ return {}.items()
+
+ def config(self, sect, field, default=None):
+ if sect == 'hgmerge-plugins':
+ return self.__conf.get(field, default)
+ elif sect == 'hgmerge':
+ return self.__globalconf.get(field, default)
+ return default
+
+ def configbool(self, sect, field, default=None):
+ val = self.config(sect, field, default=default)
+ if val is default:
+ return val
+ if val == 'True':
+ return True
+ if val == 'False':
+ return False
+ print 'Not bool: %s' % val
+ raise ValueError(val)
+
+ def configlist(self, sect, field, default=None):
+ val = self.config(sect, field, default=default)
+ if val is default:
+ return val
+ return val.replace(',', ' ').split()
+
+class FakeRepo(object):
+ def __init__(self, conf=None, globalconf=None):
+ self.ui = FakeUi(conf, globalconf)
+
+class TestCase(srltesting.TestCase):
+ def setUp(self):
+ srltesting.TestCase.setUp(self)
+ self.__origattrs = {}
+ self.__tempfpaths = []
+ self.__scriptdir = os.path.abspath(os.path.join("Data", "bin"))
+ self.__outputdir = os.path.join(self.__scriptdir, "Output")
+ if not os.path.exists(self.__outputdir):
+ os.makedirs(self.__outputdir)
+
+ def tearDown(self):
+ srltesting.TestCase.tearDown(self)
+ for (obj, attr), val in self.__origattrs.items():
+ setattr(obj, attr, val)
+ for fpath in self.__tempfpaths:
+ try: os.remove(fpath)
+ except EnvironmentError: pass
+
+ def _create_repo(self, conf={}):
+ return FakeRepo(conf)
+
+ def _set_objattr(self, obj, attr, val):
+ if (obj, attr) not in self.__origattrs:
+ self.__origattrs[(obj, attr)] = getattr(obj, attr)
+ setattr(obj, attr, val)
+
+ def _get_tempfname(self):
+ """ Get a temporary filename.
+
+ The file is automatically removed upon teardown if still there.
+ """
+ fd, fpath = tempfile.mkstemp()
+ os.close(fd)
+ self.__tempfpaths.append(fpath)
+ return fpath
\ No newline at end of file
diff --git a/tests/hgmerge/runtests.py b/tests/hgmerge/runtests.py
new file mode 100755
--- /dev/null
+++ b/tests/hgmerge/runtests.py
@@ -0,0 +1,4 @@
+#!/usr/bin/env python
+import srllib.testing as srltesting
+
+srltesting.run_tests("mercurial.hgmerge")
\ No newline at end of file
diff --git a/tests/hgmerge/test_plugins.py b/tests/hgmerge/test_plugins.py
new file mode 100644
--- /dev/null
+++ b/tests/hgmerge/test_plugins.py
@@ -0,0 +1,138 @@
+''' Test the _plugins module.
+'''
+import os.path
+
+from _common import *
+
+import mercurial.hgmerge._plugins as mergeplugs
+import mercurial.hgmerge._pluginapi as mergepluginapi
+import mercurial.util as hgutil
+
+class toolpluginTest(TestCase):
+ ''' Test toolplugin class.
+ '''
+ def setUp(self):
+ TestCase.setUp(self)
+ self.__ui = FakeUi()
+
+ def test_construct_exe_none(self):
+ ''' Test constructing with None for executable.
+ '''
+ plug = self.__create_plug()
+ self.assertEqual(plug.executable, plug.name.lower())
+
+ def test__detect_in_path(self):
+ plug = self.__create_plug()
+ self._set_objattr(hgutil, 'find_exe', MthdMock(returnval='/test'))
+ self.assertEqual(plug._detect_in_path(), '/test')
+
+ def test__detect_in_reg_instldir(self):
+ ''' Test _detect_in_reg when looking for installation directory. '''
+ plug = self.__create_plug(win_regpath_installdir='SOFTWARE\\test')
+ lookupmock = MthdMock(returnval='/test')
+ self._set_objattr(mergepluginapi, 'lookup_reg', lookupmock)
+ self._set_objattr(os, 'access', MthdMock(returnval=True))
+ self.assertEqual(os.path.dirname(plug._detect_in_reg()), '/test')
+ self.assertEqual(lookupmock.mock_args, ('SOFTWARE\\test',))
+
+ def test__detect_in_reg_instlpath(self):
+ ''' Test _detect_in_reg when looking for installation path. '''
+ plug = self.__create_plug(win_regpath_installpath='SOFTWARE\\test')
+ lookupmock = MthdMock(returnval='/test/test.exe')
+ self._set_objattr(mergepluginapi, 'lookup_reg', lookupmock)
+ self._set_objattr(os, 'access', MthdMock(returnval=True))
+ self.assertEqual(plug._detect_in_reg(), '/test/test.exe')
+ self.assertEqual(lookupmock.mock_args, ('SOFTWARE\\test',))
+
+ def test_detect(self):
+ plug = self.__create_plug()
+ self.__detect(plug, path='/test')
+
+ def test_detect_false(self):
+ plug = self.__create_plug()
+ self.__detect(plug)
+
+ def __detect(self, plug, path=None, reg=None):
+ pathmock = plug._detect_in_path = MthdMock(returnval=path)
+ regmock = plug._detect_in_reg = MthdMock(returnval=reg)
+ r = plug.detect(self.__ui)
+ self.assert_(pathmock.mock_is_called)
+ if path is None:
+ self.assert_(regmock.mock_is_called)
+ if path is not None or reg is not None:
+ self.assert_(r)
+ else:
+ self.assertNot(r)
+
+ def test_merge(self):
+ plug = self.__create_plug()
+ plug.executable = 'test'
+ self.assertEqual(self.__merge(plug), 0)
+ self.assertEqual(self.__runmock.mock_args, ([plug.executable] +
+ self.__mergeargs, self.__ui))
+
+ def test_merge_stdout(self):
+ ''' Test merging to stdout.
+ '''
+ plug = self.__create_plug(stdout=True)
+ self.__merge(plug)
+ ofile = self.__runmock.mock_kwds['stdout']
+ self.assertEqual(ofile.name, self.__mergeargs[-1])
+
+ def test_merge_check_conflicts(self):
+ ''' Test checking for conflicts after merge.
+ '''
+ plug = self.__create_plug(check_conflicts=True)
+ self.__merge(plug)
+ self.assertEqual(self.__check_conflictsmock.mock_args[0],
+ self.__mergeargs[-1])
+
+ def test_postmerge(self):
+ plug = self.__create_plug()
+ cleanupmock = MthdMock()
+ self._set_objattr(mergepluginapi, 'cleanupeol', cleanupmock)
+ plug.postmerge('base', 'local', 'other', 'output', self.__ui)
+ self.assert_(cleanupmock.mock_is_called)
+
+ def __merge(self, plug, retval=0, conflict=False):
+ self.__runmock = MthdMock(returnval=retval)
+ self._set_objattr(mergepluginapi, 'runcommand', self.__runmock)
+ self.__check_conflictsmock = MthdMock(returnval=conflict)
+ self._set_objattr(mergepluginapi, 'checkconflicts',
+ self.__check_conflictsmock)
+ args = self.__mergeargs = ['base', 'local', 'other', 'output']
+ return plug.merge(*(args + [self.__ui]))
+
+ def __create_plug(self, executable=None, arguments=None, **kwds):
+ plug = mergeplugs.toolplugin('Test', executable, arguments, **kwds)
+ return plug
+
+class _FileMock(list):
+ def close(self):
+ pass
+
+class MiscTest(TestCase):
+ ''' Test miscellaneous.
+ '''
+ def test__checkconflicts(self):
+ txt = '''Testing
+<<<<<<<
+=======
+>>>>>>>
+'''
+ self.__check_conflicts(txt, True)
+
+ def test__checkconflicts_false(self):
+ ''' Test _checkconflicts with no conflicts. '''
+ txt = '''No
+conflicts
+here
+'''
+ self.__check_conflicts(txt, False)
+
+ def __check_conflicts(self, txt, conflicts):
+ filemock = _FileMock(txt.splitlines())
+ filemock.close = MthdMock()
+ import __builtin__
+ self._set_objattr(__builtin__, 'open', MthdMock(returnval=filemock))
+ self.assertEqual(mergepluginapi.checkconflicts('test'), conflicts)
diff --git a/tests/hgmerge/testhgmerge.py b/tests/hgmerge/testhgmerge.py
new file mode 100644
--- /dev/null
+++ b/tests/hgmerge/testhgmerge.py
@@ -0,0 +1,556 @@
+#!/usr/bin/env python
+import os.path, tempfile, sys
+import re
+import stat
+from glob import glob
+
+from _common import *
+
+from mercurial import hgmerge
+import mercurial.hgmerge._plugins as mergeplugs
+import mercurial.hgmerge._simplemerge as hgsimplemerge
+
+class _FakePlugin(object):
+ def __init__(self, name='Fake', type_='tool', result=0, detect=True,
+ priority=0):
+ self.type = type_
+ self.name = name
+ self.priority = priority
+ self.__result, self.__detect = result, detect
+ self.fake_mergeargs = None
+ self.fake_opts = {}
+ self.fake_mergeexc = None
+
+ @property
+ def fake_is_called(self):
+ return self.fake_mergeargs is not None
+
+ def set_options(self, options, ui):
+ for attr, val in options.attrs.items():
+ self.fake_opts[attr] = val
+ try: self.priority = options.attrs['priority']
+ except KeyError: pass
+
+ def detect(self, ui):
+ return self.__detect
+
+ def premerge(self, base, local, other, output, ui):
+ return True
+
+ def merge(self, base, local, other, output, ui):
+ self.fake_mergeargs = (base, local, other, output, ui)
+ if self.fake_mergeexc is not None:
+ raise self.fake_mergeexc
+ return self.__result
+
+ def postmerge(self, base, local, other, output, ui):
+ pass
+
+def _read_file(fname):
+ f = file(fname, 'rb')
+ try: data = f.read()
+ finally: f.close()
+ return data
+
+def _create_file(fname, content):
+ f = file(fname, 'wb')
+ try: f.write(content)
+ finally: f.close()
+
+class merge_test(TestCase):
+ ''' Test merge function.
+ '''
+ def setUp(self):
+ TestCase.setUp(self)
+ self.__repo = FakeRepo()
+ self.__ui = self.__repo.ui
+ self.__simplemerge = MthdMock(returnval=0)
+ self._set_objattr(hgsimplemerge, 'simplemerge', self.__simplemerge)
+
+ def tearDown(self):
+ TestCase.tearDown(self)
+ assert hgsimplemerge.simplemerge is not self.__simplemerge
+
+ def test_merge(self):
+ ''' Test simple merge, with no conflicts. '''
+ def merge(base, local, other, output, ui, **kwds):
+ self.__mergeargs = (base, local, other, output, ui)
+ f = file(output, 'w')
+ f.write('Success')
+ f.close()
+ return 0
+
+ base, local, other = self.__get_versions()
+ interactive, extfilter = self.__create_plug()
+ self._set_objattr(hgsimplemerge, 'simplemerge', merge)
+
+ r = hgmerge.merge(self.__repo, local, base, other, pluginspec=
+ (interactive, extfilter))
+ self.assertEqual(r, 0)
+ self.__verify_mergeargs(self.__mergeargs, base, local, other)
+ self.assertNot(interactive.fake_is_called)
+ # Now verify merge output
+ f = file(local.name, 'r')
+ txt = f.read()
+ f.close()
+ self.assertEqual(txt, 'Success')
+
+ def test_merge_permissions(self):
+ ''' Verify that the permissions are by default the same as for the local
+ copy.
+ '''
+ base, local, other = self.__get_versions()
+ os.chmod(local.name, 0666)
+ interactive, extfilter = self.__create_plug()
+ hgmerge.merge(self.__repo, local, base, other, pluginspec=
+ (interactive, extfilter))
+ st = os.stat(local.name)
+ self.assertEqual(stat.S_IMODE(st.st_mode), 0666)
+
+ def test_merge_failure(self):
+ ''' Verify behaviour upon merge failure.
+
+ The idea here is to verify that a botched merge operation does not
+ result in permanent damage.
+ '''
+ def merge(base, local, other, output, ui):
+ self.__mergelocal = local
+ f = file(output, 'w')
+ f.write('Botched')
+ f.close()
+ raise Exception()
+
+ base, local, other = self.__get_versions()
+ f = file(local.name, 'w')
+ f.write('My text')
+ f.close()
+ interactive, extfilter = self.__create_plug()
+ self.__simplemerge.mock_set_returnvalue(1)
+ self._set_objattr(interactive, 'merge', merge)
+
+ r = hgmerge.merge(self.__repo, local, base, other, pluginspec=
+ (interactive, extfilter))
+ self.assertEqual(r, 2)
+ # Verify merge output and backup
+ f = file(local.name)
+ txt = f.read()
+ f.close()
+ self.assertEqual(txt, 'Botched')
+ f = file(self.__mergelocal)
+ txt = f.read()
+ f.close()
+ self.assertEqual(txt, 'My text')
+ # Verify that the user is warned
+ self.assert_(self.__ui.warn.mock_args[0].startswith(
+ 'Merging failed'))
+
+ def test_merge_conflicts(self):
+ ''' Test merge in which conflicts must be resolved. '''
+ base, local, other = self.__get_versions()
+ interactive, extfilter = self.__create_plug()
+ self.__simplemerge.mock_set_returnvalue(1)
+ self.__ui.prompt.mock_set_returnvalue('y')
+
+ r = hgmerge.merge(self.__repo, local, base, other, pluginspec=
+ (interactive, extfilter))
+ self.assertEqual(r, 0)
+ self.__verify_mergeargs(interactive.fake_mergeargs, base, local, other)
+
+ def test_merge_conflicts_no_plugin(self):
+ ''' Test merge where there are conflicts, but no fallback plug-in. '''
+ def merge(base, local, other, output, ui, **kwds):
+ f = file(output, 'w')
+ try: f.write('Conflicts')
+ finally: f.close()
+ return 1
+
+ base, local, other = self.__get_versions()
+ self._set_objattr(hgsimplemerge, 'simplemerge', merge)
+
+ r = hgmerge.merge(self.__repo, local, base, other, pluginspec=
+ (None, False))
+ self.assertEqual(r, 1)
+ self.assertEqual(_read_file(local.name), 'Conflicts')
+ # Make sure no backup is kept
+ self.assertNot(glob('%s.orig.*' % local.name))
+
+ def __verify_mergeargs(self, mergeargs, base, local, other):
+ mbase, mlocal, mother, moutput, mui = mergeargs
+ self.assertEqual(mbase, base.name)
+ self.assert_(re.match(r'^%s\.orig\.\d+' % local.name, mlocal))
+ self.assertEqual(mother, other.name)
+ self.assertEqual(moutput, local.name)
+ self.assertIs(mui, self.__ui)
+
+ def test_merge_plugins_unspecified(self):
+ ''' Test merge without specifying plugins. '''
+ base, local, other = self.__get_versions()
+ interactive, extfilter = self.__create_plug()
+ get_pluginmock = MthdMock(returnval=(interactive,
+ extfilter))
+ self._set_objattr(hgmerge, 'get_plugin', get_pluginmock)
+ repo = self.__repo
+ # Assert that get_plugin is called when no plug-ins are specified
+ hgmerge.merge(repo, local, base, other, pluginspec=None)
+ self.assertEqual(get_pluginmock.mock_args, (repo, local.name))
+
+ def test_merge_plugin_none(self):
+ ''' Test having get_plugin return no plug-in while one was requested
+ for this filetype.
+ '''
+ base, local, other = self.__get_versions()
+ get_pluginmock = MthdMock(returnval=(None, True))
+ self._set_objattr(hgmerge, 'get_plugin', get_pluginmock)
+ self.assertEqual(hgmerge.merge(self.__repo, local, base, other),
+ 2)
+
+ def test_merge_unmergeable_success(self):
+ ''' Test merging where file doesn't appear mergeable, but user chooses
+ one version.
+ '''
+ base, local, other = self.__get_versions()
+ interactive, extfilter = self.__create_plug()
+ ask_unmergeable = MthdMock(returnval=True)
+ self._set_objattr(hgmerge, '_ask_unmergeable', ask_unmergeable)
+ self._set_objattr(hgmerge, '_is_mergeable', MthdMock(returnval=False))
+
+ r = hgmerge.merge(self.__repo, local, base, other, pluginspec=
+ (interactive, extfilter))
+ self.assertEqual(r, 0)
+ self.assert_(ask_unmergeable.mock_is_called)
+
+ def test_merge_unmergeable_failure(self):
+ ''' Test merging where file doesn't appear mergeable, and user doesn't
+ choose one version.
+ '''
+ base, local, other = self.__get_versions()
+ interactive, extfilter = self.__create_plug()
+ ask_unmergeable = MthdMock(returnval=False)
+ self._set_objattr(hgmerge, '_ask_unmergeable', ask_unmergeable)
+ self._set_objattr(hgmerge, '_is_mergeable', MthdMock(returnval=False))
+
+ r = hgmerge.merge(self.__repo, local, base, other, pluginspec=
+ (interactive, extfilter))
+ self.assertEqual(r, 2)
+
+ def test__is_mergeable(self):
+ base, local, other = self.__get_versions()
+ for fdesc in (base, local, other):
+ self._set_objattr(fdesc, '_eoltype', MthdMock(returnval='unix'))
+ self.assert_(hgmerge._is_mergeable(base, local, other))
+
+ def test__is_mergeable_false(self):
+ base, local, other = self.__get_versions()
+ for mergeable, unmergeable in ((local, other), (other, local)):
+ self._set_objattr(mergeable, '_eoltp', 'unix')
+ self._set_objattr(unmergeable, '_eoltp', 'binary')
+ self.assertFalse(hgmerge._is_mergeable(base, local, other))
+
+ def test__ask_unmergeable_local(self):
+ ''' Test _ask_unmergeable_method, where user picks local version. '''
+ base, local, other = self.__get_versions()
+ ui = self.__ui
+ ui.prompt.mock_set_returnvalue('k')
+ r = hgmerge._ask_unmergeable(self.__ui, local, other, local)
+ self.assert_(r)
+
+ def test__ask_unmergeable_other(self):
+ ''' Test _ask_unmergeable_method, where user picks other version. '''
+ base, local, other = self.__get_versions()
+ ui = self.__ui
+ ui.prompt.mock_set_returnvalue('o')
+ r = hgmerge._ask_unmergeable(self.__ui, local, other, local)
+ self.assert_(r)
+
+ def test__ask_unmergeable_fail(self):
+ ''' Test _ask_unmergeable_method, where user fails to pick a version.
+ '''
+ # TODO: Find out how user cancellation is communicated
+# raise NotImplementedError()
+
+ def test__ask_unmergeable_binary(self):
+ ''' Test _ask_unmergeable_method, where one version is binary.
+
+ For coverage.
+ '''
+ base, local, other = self.__get_versions()
+ ui = self.__ui
+ ui.prompt.mock_set_returnvalue('o')
+ self._set_objattr(local, '_eoltp', 'binary')
+ hgmerge._ask_unmergeable(self.__ui, local, other, local)
+
+ def test__ask_unmergeable_other_symlink(self):
+ ''' Test _ask_unmergeable_method, where the other version is a symlink.
+ '''
+ ui = self.__ui
+ ui.prompt.mock_set_returnvalue('o')
+ local = self._get_tempfname()
+ linkdest = self._get_tempfname()
+ other = self._get_tempfname()
+ os.remove(other)
+ os.symlink(linkdest, other)
+
+ local = hgmerge.filedesc(local, False)
+ other = hgmerge.filedesc(other, True)
+ r = hgmerge._ask_unmergeable(self.__ui, local, other, local)
+ self.assert_(r)
+ self.assertEqual(os.readlink(local.name), linkdest)
+
+ def test__ask_unmergeable_unknown_eol(self):
+ ''' Test _ask_unmergeable_method, where one version has an unknown EOL
+ type.
+
+ For coverage.
+ '''
+ base, local, other = self.__get_versions()
+ ui = self.__ui
+ ui.prompt.mock_set_returnvalue('o')
+ self._set_objattr(local, '_eoltp', 'unknown')
+ hgmerge._ask_unmergeable(self.__ui, local, other, local)
+
+ def test_simplemerge(self):
+ ''' Test the simplemerge plug-in. '''
+ import mercurial.hgmerge._simplemerge
+ simplemergemock = MthdMock()
+ self._set_objattr(mercurial.hgmerge._simplemerge, 'simplemerge',
+ simplemergemock)
+ hgmerge.simplemerge().merge('base', 'local', 'other', 'output',
+ self.__ui)
+ # Simply ensure that the correct arguments are passed
+ self.assertEqual(simplemergemock.mock_args, ('base', 'local', 'other',
+ 'output', self.__ui))
+
+ def __create_plug(self, result=0):
+ """ Create mock plug.
+ """
+ plug = _FakePlugin(result=result)
+ return plug, False
+
+ def __get_versions(self, basecontent=None, localcontent=None,
+ othercontent=None, base_islink=False, local_islink=False,
+ other_islink=False):
+ """ Create three different file versions for merging. """
+ if basecontent is None:
+ basecontent = "First line\n"
+ if localcontent is None:
+ localcontent = "%sMy change\n" % (basecontent,)
+ if othercontent is None:
+ othercontent = "My change\n%s" % (basecontent,)
+
+ base = self._get_tempfname()
+ f = file(base, "w")
+ try: f.write(basecontent)
+ finally: f.close()
+ local, other = self._get_tempfname(), self._get_tempfname()
+ f = file(local, "w")
+ try: f.write(localcontent)
+ finally: f.close()
+ f = file(other, "w")
+ try: f.write(othercontent)
+ finally: f.close()
+
+ return (hgmerge.filedesc(base, base_islink), hgmerge.filedesc(local,
+ local_islink), hgmerge.filedesc(other, other_islink))
+
+class filedesc_test(TestCase):
+ def test_linkdest_reallink(self):
+ ''' Test method linkdest with a real link. '''
+ tempf = self._get_tempfname()
+ templnk = self._get_tempfname()
+ os.remove(templnk)
+ os.symlink(tempf, templnk)
+ fdesc = self.__create_filedesc(templnk, islink=True)
+ self.assertEqual(fdesc.linkdest, tempf)
+
+ def test_linkdest_fakelink(self):
+ ''' Test method linkdest with an emulated link. '''
+ tempf = self._get_tempfname()
+ templnk = self._get_tempfile()
+ templnk.write(tempf)
+ templnk.close()
+ fdesc = self.__create_filedesc(templnk.name, islink=True)
+ self.assertEqual(fdesc.linkdest, tempf)
+
+ def test_eoltype_link(self):
+ fdesc = self.__create_filedesc(islink=True)
+ self.assertEqual(fdesc.eoltype, 'symlink')
+
+ def __create_filedesc(self, fname=None, islink=False):
+ if fname is None:
+ fname = self._get_tempfname()
+ return hgmerge.filedesc(fname, islink)
+
+class get_plugin_test(TestCase):
+ ''' Test get_plugin function.
+ '''
+ def setUp(self):
+ TestCase.setUp(self)
+
+ def test_get_plugin(self):
+ ''' Test the get_plugin method. '''
+ self._set_objattr(hgmerge, 'plugins', [_FakePlugin()])
+ interactive, extfilter = self.__get_plugin()
+ self.assertIs(interactive, hgmerge.plugins[0])
+ self.assertNot(extfilter)
+
+ def test_get_plugin_none(self):
+ ''' Test get_plugin when no interactive plug-in is available. '''
+ self._set_objattr(hgmerge, 'plugins', [_FakePlugin(detect=
+ False)])
+ self.assertEqual(self.__get_plugin(), (None, False))
+
+ def test_get_plugin_initial(self):
+ ''' Ensure that get_plugin calls _setup_plugs initially. '''
+ def setup_plugs(ui):
+ self._set_objattr(hgmerge, 'plugins', [])
+
+ # Make sure this is not defined
+ self._set_objattr(hgmerge, 'plugins', None)
+ setupmock = MthdMock(func=setup_plugs)
+ self._set_objattr(hgmerge, '_setup_plugs', setupmock)
+ repo = FakeRepo()
+ self.assertIs(self.__get_plugin(repo)[0], None)
+ self.assertEqual(setupmock.mock_args, (repo.ui,))
+
+ def test_get_plugin_hgrc_hgmerge_plugin(self):
+ ''' Make sure hgrc parameter 'default' is respected. '''
+ self._set_objattr(hgmerge, 'plugins', [_FakePlugin(),
+ _FakePlugin(name='test2')])
+ repo = FakeRepo(globalconf={'default': 'test2'})
+ plug, extfilter = self.__get_plugin(repo)
+ self.assertEqual(plug.name, 'test2')
+ self.assertNot(extfilter)
+
+ def test_get_plugin_hgrc_ext(self):
+ ''' Make sure hgrc parameter ext<file extension> is respected. '''
+ self._set_objattr(hgmerge, 'plugins', [_FakePlugin(),
+ _FakePlugin(name='test2')])
+ inter, extfilter = self.__get_plugin(FakeRepo(globalconf=
+ {'ext.png': 'test2'}), fname='test.png')
+ self.assertEqual(inter.name, 'test2')
+ self.assert_(extfilter)
+
+ def test_get_plugin_hgrc_ext_missing(self):
+ ''' Test when specified plug-in for a file extension is missing. '''
+ self._set_objattr(hgmerge, 'plugins', [_FakePlugin(),
+ _FakePlugin(name='test2', detect=False)])
+ ret = self.__get_plugin(FakeRepo(globalconf=
+ {'ext.png': 'test2'}), fname='test.png')
+ self.assertEqual(ret, (None, True))
+
+ def __get_plugin(self, repo=None, fname=None):
+ if repo is None:
+ repo = self._create_repo()
+ return hgmerge.get_plugin(repo, fname=fname)
+
+ def test__setup_plugs_modify(self):
+ ''' Test modifying plug via _setup_plugs. '''
+ args = '$local $base $other'
+ self.__setup_plugs({'test.type': 'tool', 'test.arguments': args})
+ self.assertEqual(self.__fakeplug.fake_opts, {'arguments': args.split(),
+ 'priority': 0})
+
+ def test__setup_plugs_define(self):
+ ''' Test defining tool via _setup_plugs. '''
+ # Note that the default type for a plug-in should be tool
+ self.__setup_plugs({'newplug': ''})
+ plug = hgmerge.plugins[-1]
+ self.assertEqual(plug.name, 'newplug')
+ self.assertEqual(plug.type, 'tool')
+
+ def test__setup_plugs_define_dflt_type(self):
+ ''' Test defining plug-in with default type. '''
+ self.__setup_plugs({'newplug': ''})
+ plug = hgmerge.plugins[-1]
+ self.assertEqual(plug.name, 'newplug')
+ self.assertEqual(plug.type, 'tool')
+
+ def test__setup_plugs_define_invalid(self):
+ ''' Test providing invalid definition for tool. '''
+ def check_plugs():
+ ''' Verify that the plug-ins aren't changed. '''
+ self.assertEqual(hgmerge.plugins,
+ mergeplugs.plugins)
+
+ conf = {'newplug.type': 'invalid'}
+ self.__setup_plugs(conf)
+ check_plugs()
+ conf = {'newplug.type': 'tool', 'newplug.stdout': 'invalid'}
+ self.__setup_plugs(conf)
+ check_plugs()
+
+ def test__setup_plugs_priority(self):
+ ''' Test defining plug-in with priority. '''
+ self.__setup_plugs({'newplug.type': 'tool', 'newplug.priority': '1'})
+ self.assertEqual(hgmerge.plugins[0].name, 'newplug')
+
+ def test__setup_plugs_priority_modify(self):
+ ''' Test modifying plug-in's priority. '''
+ self.__setup_plugs({'test.priority': '10'})
+ self.assertEqual(hgmerge.plugins[0].priority, 10)
+
+ def test_query_plugins(self):
+ def setup_plugs(ui):
+ self._set_objattr(hgmerge, 'plugins', [
+ _FakePlugin(name='test1', detect=False), _FakePlugin(
+ name='test2')])
+ self._set_objattr(hgmerge, 'plugins', None)
+ self._set_objattr(hgmerge, '_setup_plugs', setup_plugs)
+ self.assertEqual(hgmerge.query_plugins(FakeUi())[0].name, 'test2')
+
+ def test_query_plugins_rescan(self):
+ setupmock = MthdMock()
+ self._set_objattr(hgmerge, 'plugins', [])
+ self._set_objattr(hgmerge, '_setup_plugs', setupmock)
+ hgmerge.query_plugins(FakeUi(), rescan=True)
+ self.assert_(setupmock.mock_is_called)
+
+ def __setup_plugs(self, conf):
+ plug = self.__fakeplug = _FakePlugin('test')
+ self._set_objattr(mergeplugs, 'plugins', [plug])
+ ui = self.__ui = FakeUi(conf)
+ hgmerge._setup_plugs(ui)
+
+class hgmerge_misc_test(TestCase):
+ ''' Test miscellaneous in the hgmerge package.
+ '''
+ def test_stockplugins(self):
+ ''' Test the stockplugins variable.
+ '''
+ self.assertIs(hgmerge.stockplugins, mergeplugs.plugins)
+ self.assert_(isinstance(hgmerge.stockplugins, (list, tuple)))
+
+class simplemerge_test(TestCase):
+ ''' Test _simplemerge module. '''
+ def test_simplemerge(self):
+ base = self._create_file('Base\n\n')
+ local = self._create_file('Base\n\nLocal\n')
+ other = self._create_file('Other\n\n')
+ output = self._get_tempfname()
+ r = hgsimplemerge.simplemerge(base, local, other, output, FakeUi())
+ self.assertEqual(r, 0)
+ self.assertEqual(_read_file(output), 'Other\n\nLocal\n')
+
+ def test_simplemerge_conflicts(self):
+ ''' Test simplemerge on conflicting changes. '''
+ base = self._create_file('\n')
+ local = self._create_file('Local\n')
+ other = self._create_file('Other\n')
+ output = self._get_tempfname()
+ r = hgsimplemerge.simplemerge(base, local, other, output, FakeUi())
+ self.assertEqual(r, 1)
+
+ self.assertEqual(_read_file(output), '''<<<<<<< %s
+Local
+=======
+Other
+>>>>>>> %s
+''' % (local, other))
+
+ def _create_file(self, contents='', fname=None):
+ if fname is None:
+ fname = self._get_tempfname()
+ f = file(fname, 'w')
+ try: f.write(contents)
+ finally: f.close()
+ return fname
\ No newline at end of file
More information about the Mercurial-devel
mailing list