changeset 3912:91f440a11b94 beta

fixes issues #849 IMC failed for non-ascii files - added set of regression tests into vcs lib
author Marcin Kuzminski <marcin@python-works.com>
date Wed, 29 May 2013 00:20:27 +0200
parents 7cca0d07c12b
children 5f192af1ba21
files rhodecode/lib/vcs/backends/git/changeset.py rhodecode/lib/vcs/backends/git/inmemory.py rhodecode/lib/vcs/nodes.py rhodecode/model/scm.py rhodecode/tests/vcs/test_changesets.py rhodecode/tests/vcs/test_inmemchangesets.py
diffstat 6 files changed, 74 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/rhodecode/lib/vcs/backends/git/changeset.py	Tue May 28 23:27:44 2013 +0200
+++ b/rhodecode/lib/vcs/backends/git/changeset.py	Wed May 29 00:20:27 2013 +0200
@@ -104,7 +104,7 @@
         return path
 
     def _get_id_for_path(self, path):
-
+        path = safe_str(path)
         # FIXME: Please, spare a couple of minutes and make those codes cleaner;
         if not path in self._paths:
             path = path.strip('/')
@@ -154,7 +154,7 @@
             if not path in self._paths:
                 raise NodeDoesNotExistError("There is no file nor directory "
                     "at the given path '%s' at revision %s"
-                    % (path, self.short_id))
+                    % (path, safe_str(self.short_id)))
         return self._paths[path]
 
     def _get_kind(self, path):
@@ -254,6 +254,7 @@
         Returns stat mode of the file at the given ``path``.
         """
         # ensure path is traversed
+        path = safe_str(path)
         self._get_id_for_path(path)
         return self._stat_modes[path]
 
--- a/rhodecode/lib/vcs/backends/git/inmemory.py	Tue May 28 23:27:44 2013 +0200
+++ b/rhodecode/lib/vcs/backends/git/inmemory.py	Wed May 29 00:20:27 2013 +0200
@@ -46,7 +46,7 @@
         for node in self.added + self.changed:
             # Compute subdirs if needed
             dirpath, nodename = posixpath.split(node.path)
-            dirnames = dirpath and dirpath.split('/') or []
+            dirnames = map(safe_str, dirpath and dirpath.split('/') or [])
             parent = commit_tree
             ancestors = [('', parent)]
 
--- a/rhodecode/lib/vcs/nodes.py	Tue May 28 23:27:44 2013 +0200
+++ b/rhodecode/lib/vcs/nodes.py	Wed May 29 00:20:27 2013 +0200
@@ -16,7 +16,7 @@
 from rhodecode.lib.vcs.backends.base import EmptyChangeset
 from rhodecode.lib.vcs.exceptions import NodeError, RemovedFileNodeError
 from rhodecode.lib.vcs.utils.lazy import LazyProperty
-from rhodecode.lib.vcs.utils import safe_unicode
+from rhodecode.lib.vcs.utils import safe_unicode, safe_str
 
 
 class NodeKind:
@@ -100,8 +100,8 @@
     def __init__(self, path, kind):
         if path.startswith('/'):
             raise NodeError("Cannot initialize Node objects with slash at "
-                "the beginning as only relative paths are supported")
-        self.path = path.rstrip('/')
+                            "the beginning as only relative paths are supported")
+        self.path = safe_str(path.rstrip('/'))  # we store paths as str
         if path == '' and kind != NodeKind.DIR:
             raise NodeError("Only DirNode and its subclasses may be "
                             "initialized with empty path")
--- a/rhodecode/model/scm.py	Tue May 28 23:27:44 2013 +0200
+++ b/rhodecode/model/scm.py	Wed May 29 00:20:27 2013 +0200
@@ -580,8 +580,8 @@
                 raise NonRelativePathError('%s is not an relative path' % f_path)
             if f_path:
                 f_path = os.path.normpath(f_path)
+            content = nodes[f_path]['content']
             f_path = safe_str(f_path)
-            content = nodes[f_path]['content']
             # decoding here will force that we have proper encoded values
             # in any other case this will throw exceptions and deny commit
             if isinstance(content, (basestring,)):
--- a/rhodecode/tests/vcs/test_changesets.py	Tue May 28 23:27:44 2013 +0200
+++ b/rhodecode/tests/vcs/test_changesets.py	Wed May 29 00:20:27 2013 +0200
@@ -1,3 +1,4 @@
+# encoding: utf8
 from __future__ import with_statement
 
 import time
@@ -302,6 +303,7 @@
                 'date': datetime.datetime(2010, 1, 1, 20),
                 'added': [
                     FileNode('foo/bar', content='foo'),
+                    FileNode('foo/bał', content='foo'),
                     FileNode('foobar', content='foo'),
                     FileNode('qwe', content='foo'),
                 ],
@@ -323,6 +325,7 @@
         changeset = self.repo.get_changeset(0)
         self.assertItemsEqual(changeset.added, [
             changeset.get_node('foo/bar'),
+            changeset.get_node('foo/bał'),
             changeset.get_node('foobar'),
             changeset.get_node('qwe'),
         ])
@@ -344,6 +347,14 @@
         self.assertEqual(len(changeset.removed), 1)
         self.assertEqual(list(changeset.removed)[0].path, 'qwe')
 
+    def test_get_filemode(self):
+        changeset = self.repo.get_changeset()
+        self.assertEqual(33188, changeset.get_file_mode('foo/bar'))
+
+    def test_get_filemode_non_ascii(self):
+        changeset = self.repo.get_changeset()
+        self.assertEqual(33188, changeset.get_file_mode('foo/bał'))
+        self.assertEqual(33188, changeset.get_file_mode(u'foo/bał'))
 
 # For each backend create test case class
 for alias in SCM_TESTS:
--- a/rhodecode/tests/vcs/test_inmemchangesets.py	Tue May 28 23:27:44 2013 +0200
+++ b/rhodecode/tests/vcs/test_inmemchangesets.py	Wed May 29 00:20:27 2013 +0200
@@ -1,3 +1,4 @@
+# encoding: utf8
 """
 Tests so called "in memory changesets" commit API of vcs.
 """
@@ -18,6 +19,7 @@
 from rhodecode.lib.vcs.nodes import DirNode
 from rhodecode.lib.vcs.nodes import FileNode
 from rhodecode.lib.vcs.utils.compat import unittest
+from rhodecode.lib.vcs.utils import safe_unicode
 
 
 class InMemoryChangesetTestMixin(object):
@@ -112,6 +114,28 @@
         self.assertEqual(changeset.get_node('foobar/foobaz/file').content, 'foo')
         self.assertEqual(changeset.get_node('foobar/barbaz').content, 'foo')
 
+    def test_add_non_ascii_files(self):
+        rev_count = len(self.repo.revisions)
+        to_add = [
+            FileNode('żółwik/zwierzątko', content='ćććć'),
+            FileNode(u'żółwik/zwierzątko_uni', content=u'ćććć'),
+        ]
+        for node in to_add:
+            self.imc.add(node)
+        message = u'Added: %s' % ', '.join((node.path for node in self.nodes))
+        author = unicode(self.__class__)
+        changeset = self.imc.commit(message=message, author=author)
+
+        newtip = self.repo.get_changeset()
+        self.assertEqual(changeset, newtip)
+        self.assertEqual(rev_count + 1, len(self.repo.revisions))
+        self.assertEqual(newtip.message, message)
+        self.assertEqual(newtip.author, author)
+        self.assertTrue(not any((self.imc.added, self.imc.changed,
+            self.imc.removed)))
+        for node in to_add:
+            self.assertEqual(newtip.get_node(node.path).content, node.content)
+
     def test_add_raise_already_added(self):
         node = FileNode('foobar', content='baz')
         self.imc.add(node)
@@ -140,7 +164,37 @@
         self.assertNotEqual(tip, newtip)
         self.assertNotEqual(tip.id, newtip.id)
         self.assertEqual(newtip.get_node('foo/bar/baz').content,
-            'My **changed** content')
+                        'My **changed** content')
+
+    def test_change_non_ascii(self):
+        to_add = [
+            FileNode('żółwik/zwierzątko', content='ćććć'),
+            FileNode(u'żółwik/zwierzątko_uni', content=u'ćććć'),
+        ]
+        for node in to_add:
+            self.imc.add(node)
+
+        tip = self.imc.commit(u'Initial', u'joe.doe@example.com')
+
+        # Change node's content
+        node = FileNode('żółwik/zwierzątko', content='My **changed** content')
+        self.imc.change(node)
+        self.imc.commit(u'Changed %s' % safe_unicode(node.path),
+                        u'joe.doe@example.com')
+
+        node = FileNode(u'żółwik/zwierzątko_uni', content=u'My **changed** content')
+        self.imc.change(node)
+        self.imc.commit(u'Changed %s' % safe_unicode(node.path),
+                        u'joe.doe@example.com')
+
+        newtip = self.repo.get_changeset()
+        self.assertNotEqual(tip, newtip)
+        self.assertNotEqual(tip.id, newtip.id)
+
+        self.assertEqual(newtip.get_node('żółwik/zwierzątko').content,
+                         'My **changed** content')
+        self.assertEqual(newtip.get_node('żółwik/zwierzątko_uni').content,
+                         'My **changed** content')
 
     def test_change_raise_empty_repository(self):
         node = FileNode('foobar')