[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