In the ctypes-python bindings, add more tests for the 'Array' class and make
some unsuccessful attempts to fix a self-referencing bug that hese find.

### Some of these tests fail and are named 'xfail_test_*' so that the normal
### test runner does not run them.  (The ability to mark a test as Xfail
### requires either Python 2.7 or the 'unittest2' backport module.)

* subversion/bindings/ctypes-python/csvn/ext/listmixin.py
  (ListMixin.__setitem__): If the new value is a self-reference then make a
    new copy of it.  According to
    <http://code.activestate.com/recipes/440656-list-mixin/#c5>, this is
    needed to handle cases where a list is inserted into itself.
      ### This doesn't seem to make any difference.
  (test_list_mixin): Greatly cut down the set of initial list lengths to
    test, in order to reduce the test run time from a few minutes to a few
    seconds.

* subversion/bindings/ctypes-python/csvn/types.py
  (Array._resize_region): Try a couple of alternative implementations. My
    version of the 'memmove' code is what I would like to think is the
    correct code to open up a space for more elements, where the existing
    code looked plain wrong to me. The plain Python code is useful for
    running the ListMixin self-test code, because it works with Array(c_int)
    (and indeed that test then passes), whereas the code using 'byref'
    throws a TypeError assertion in that case.
  (Array._constructor): New method; the ListMixin documentation says it is
    required.
      ### It doesn't seem to be required in practice, or at least adding or
      removing this implementation seems to make no difference.
  (Array): Add assertions as found in the 'ListMixin' example code.

* subversion/bindings/ctypes-python/test/svntypes.py
  (ArrayTestCase): Add three new tests.
  (__main__): Run just the 'xfail_*' tests.
    ### Temporary.
--This line, and those below, will be ignored--

Index: subversion/bindings/ctypes-python/csvn/ext/listmixin.py
===================================================================
--- subversion/bindings/ctypes-python/csvn/ext/listmixin.py	(revision 1157788)
+++ subversion/bindings/ctypes-python/csvn/ext/listmixin.py	(working copy)
@@ -132,6 +132,8 @@ class ListMixin(object):
       return self._get_element(self._fix_index(i))
 
   def __setitem__(self, i, value):
+    if value is self:
+      value = self._constructor(value)
     if isinstance(i, slice):
       (start, end, step) = self._tuple_from_slice(i)
       if step != None:
@@ -389,8 +391,8 @@ def test_list_mixin(list_class=TestList,
 
   import random
   import sys
-  for i in [1, 3, 8, 16, 18, 29, 59, 111, 213, 501, 1013,
-            2021, 3122, 4039, 5054]:
+  for i in [1, 3, 8, 16, 18, 29]: #, 59, 111, 213, 501, 1013,
+            #2021, 3122, 4039, 5054]:
     x = [rand_elem() for j in range(i)]
     y = list_class(x)
 
Index: subversion/bindings/ctypes-python/csvn/types.py
===================================================================
--- subversion/bindings/ctypes-python/csvn/types.py	(revision 1157788)
+++ subversion/bindings/ctypes-python/csvn/types.py	(working copy)
@@ -22,7 +22,7 @@ from UserDict import DictMixin
 from shutil import copyfileobj
 from tempfile import TemporaryFile
 
-# This class contains Pythonic wrappers for generic Subversion and
+# This module contains Pythonic wrappers for generic Subversion and
 # APR datatypes (e.g. dates, streams, hashes, arrays, etc).
 #
 # These wrappers are used by higher-level APIs in csvn to wrap
@@ -140,16 +140,25 @@ class Array(ListMixin):
     elts = property(fget=lambda self: cast(self.header[0].elts.raw,
                                            POINTER(self.type)))
 
+    def _constructor(self, iterable):
+        return Array(self.type, iterable)
+
     def _get_element(self, i):
+        assert 0 <= i < len(self)
         return self.elts[i]
 
     def _set_element(self, i, value):
+        assert 0 <= i < len(self)
         self.elts[i] = value
 
     def __len__(self):
         return self.header[0].nelts
 
     def _resize_region(self, start, end, new_size):
+        assert 0 <= start <= len(self)
+        assert 0 <= end   <= len(self)
+        assert start <= end
+
         diff = start-end+new_size
 
         # Growing
@@ -161,20 +170,27 @@ class Array(ListMixin):
                 apr_array_push(self)
 
             # Move the old items out of the way, if necessary
-            if end < l:
-                src_idx = max(end-diff,0)
-                memmove(byref(self.elts + end),
-                        byref(self.elts[src_idx]),
-                        (l-src_idx)*self.header[0].elt_size)
+            try:
+              if end < l:
+                memmove(byref(self.elts[end+diff]),
+                        byref(self.elts[end]),
+                        (l-end)*self.header[0].elt_size)
+            except TypeError:
+                for i in range(l - end):
+                    self.elts[l + diff - 1 - i] = self.elts[l - 1 - i]
 
         # Shrinking
         elif diff < 0:
 
             # Overwrite the deleted items with items we still need
-            if end < len(self):
+            try:
+              if end < len(self):
                 memmove(byref(self.elts[end+diff]),
                         byref(self.elts[end]),
                         (len(self)-end)*self.header[0].elt_size)
+            except TypeError:
+                for i in range(len(self) - end):
+                    self.elts[end + diff + i] = self.elts[end + i]
 
             # Shrink the array
             for i in range(-diff):
Index: subversion/bindings/ctypes-python/test/svntypes.py
===================================================================
--- subversion/bindings/ctypes-python/test/svntypes.py	(revision 1157788)
+++ subversion/bindings/ctypes-python/test/svntypes.py	(working copy)
@@ -79,6 +79,13 @@ class HashTestCase(unittest.TestCase):
 
 class ArrayTestCase(unittest.TestCase):
 
+    def xfail_test_append_self(self):
+        a = Array(c_char_p, ['one', 'two'])
+        import pdb
+        pdb.set_trace()
+        a.extend(a)
+        self.assertEqual(a, ['one', 'two', 'one', 'two'])
+
     def test_array(self):
         self.pyarray = ["vini", "vidi", "vici"]
         self.svnarray = _types.Array(c_char_p, self.pyarray)
@@ -86,6 +93,58 @@ class ArrayTestCase(unittest.TestCase):
         self.assertEqual(self.svnarray[2], "vici")
         self.assertEqual(self.svnarray[1], "vidi")
 
+    def test_mixin_int(self):
+        # Run the tests provided by the Array's base class.
+        from csvn.ext.listmixin import test_list_mixin
+        import random
+
+        # The 'list_mixin' test assumes an array class that implements an
+        # array of a specific type, so create such a class based on Array.
+        class ArrayOfInt(Array):
+            def __init__(self, items):
+                Array.__init__(self, c_int, items)
+            def _constructor(self, iterable):
+                return ArrayOfInt(iterable)
+
+        def rand_elem():
+            return random.randrange(100)
+
+        test_list_mixin(ArrayOfInt, rand_elem)
+
+    def xfail_test_mixin_str(self):
+        # Run the tests provided by the Array's base class.
+        from csvn.ext.listmixin import test_list_mixin
+        import random
+
+        # The 'list_mixin' test assumes an array class that implements an
+        # array of a specific type, so create such a class based on Array.
+        class ArrayOfStr(Array):
+            def __init__(self, items):
+                Array.__init__(self, c_char_p, items)
+            def _constructor(self, iterable):
+                return ArrayOfStr(iterable)
+
+        def rand_elem():
+            return 'foo:' + str(random.randrange(100))
+
+        test_list_mixin(ArrayOfStr, rand_elem)
+
+    def xfail_test_extend(self):
+        a = Array(c_char_p)
+        a.extend(['a0', 'a1'])
+        a += ['a2']
+        self.assertEqual(a, ['a0', 'a1', 'a2'])
+        b = Array(c_char_p, a)
+        b[0] = 'b0new'
+        self.assertEqual(b, ['b0new', 'a1', 'a2'])
+        a.extend(b)
+        self.assertEqual(a, ['a0', 'a1', 'a2', 'b0new', 'a1', 'a2'])
+        a[0] = 'a0new'
+        a[4] = 'a4new'
+        b[2] = 'b2new'
+        self.assertEqual(a, ['a0new', 'a1', 'a2', 'b0new', 'a4new', 'a2'])
+        self.assertEqual(b, ['b0new', 'b1', 'b2new'])
+
 def suite():
     suite = unittest.TestSuite()
     suite.addTest(unittest.makeSuite(SvnDateTestCase, 'test'))
@@ -94,5 +153,6 @@ def suite():
     return suite
 
 if __name__ == '__main__':
-    runner = unittest.TextTestRunner()
-    runner.run(suite())
+    runner = unittest.TextTestRunner(verbosity=2)
+    #runner.run(suite())
+    runner.run(unittest.makeSuite(ArrayTestCase, 'xfail_test'))

