##// END OF EJS Templates
posix: always seek to EOF when opening a file in append mode...
posix: always seek to EOF when opening a file in append mode Python 3 already does this, so skip it there. Consider the program: #include <stdio.h> int main() { FILE *f = fopen("narf", "w"); fprintf(f, "narf\n"); fclose(f); f = fopen("narf", "a"); printf("%ld\n", ftell(f)); fprintf(f, "troz\n"); printf("%ld\n", ftell(f)); return 0; } on macOS, FreeBSD, and Linux with glibc, this program prints 5 10 but on musl libc (Alpine Linux and probably others) this prints 0 10 By my reading of https://pubs.opengroup.org/onlinepubs/009695399/functions/fopen.html this is technically correct, specifically: > Opening a file with append mode (a as the first character in the > mode argument) shall cause all subsequent writes to the file to be > forced to the then current end-of-file, regardless of intervening > calls to fseek(). in other words, the file position doesn't really matter in append-mode files, and we can't depend on it being at all meaningful unless we perform a seek() before tell() after open(..., 'a'). Experimentally after a .write() we can do a .tell() and it'll always be reasonable, but I'm unclear from reading the specification if that's a smart thing to rely on. This matches what we do on Windows and what Python 3 does for free, so let's just be consistent. Thanks to Yuya for the idea.

File last commit:

r41843:0c7b353c default
r42778:97ada9b8 5.0.2 stable
Show More
test-rust-ancestor.py
175 lines | 6.6 KiB | text/x-python | PythonLexer
/ tests / test-rust-ancestor.py
Georges Racinet
rust-cpython: testing the bindings from Python...
r41004 from __future__ import absolute_import
Georges Racinet
rust-cpython: binding for AncestorsIterator...
r41083 import sys
Georges Racinet
rust-cpython: testing the bindings from Python...
r41004 import unittest
Georges Racinet
rust-cpython: raising error.WdirUnsupported...
r41386 from mercurial import (
error,
node,
)
Georges Racinet
rust-cpython: testing the bindings from Python...
r41004 try:
from mercurial import rustext
Georges Racinet
rust-cpython: binding for AncestorsIterator...
r41083 rustext.__name__ # trigger immediate actual import
Georges Racinet
rust-cpython: testing the bindings from Python...
r41004 except ImportError:
rustext = None
Georges Racinet
rust-cpython: binding for AncestorsIterator...
r41083 else:
# this would fail already without appropriate ancestor.__package__
Georges Racinet
rust-cpython: binding for LazyAncestors...
r41149 from mercurial.rustext.ancestor import (
AncestorsIterator,
Georges Racinet
rust-cpython: bindings for MissingAncestors...
r41224 LazyAncestors,
MissingAncestors,
Georges Racinet
rust-cpython: binding for LazyAncestors...
r41149 )
Georges Racinet
rust-cpython: binding for headrevs()...
r41843 from mercurial.rustext import dagop
Georges Racinet
rust-cpython: testing the bindings from Python...
r41004
try:
from mercurial.cext import parsers as cparsers
except ImportError:
cparsers = None
Georges Racinet
rust-cpython: binding for AncestorsIterator...
r41083 # picked from test-parse-index2, copied rather than imported
# so that it stays stable even if test-parse-index2 changes or disappears.
data_non_inlined = (
b'\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01D\x19'
b'\x00\x07e\x12\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff'
b'\xff\xff\xff\xff\xd1\xf4\xbb\xb0\xbe\xfc\x13\xbd\x8c\xd3\x9d'
b'\x0f\xcd\xd9;\x8c\x07\x8cJ/\x00\x00\x00\x00\x00\x00\x00\x00\x00'
b'\x00\x00\x00\x00\x00\x00\x01D\x19\x00\x00\x00\x00\x00\xdf\x00'
b'\x00\x01q\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\xff'
b'\xff\xff\xff\xc1\x12\xb9\x04\x96\xa4Z1t\x91\xdfsJ\x90\xf0\x9bh'
b'\x07l&\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
b'\x00\x01D\xf8\x00\x00\x00\x00\x01\x1b\x00\x00\x01\xb8\x00\x00'
b'\x00\x01\x00\x00\x00\x02\x00\x00\x00\x01\xff\xff\xff\xff\x02\n'
b'\x0e\xc6&\xa1\x92\xae6\x0b\x02i\xfe-\xe5\xbao\x05\xd1\xe7\x00'
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01F'
b'\x13\x00\x00\x00\x00\x01\xec\x00\x00\x03\x06\x00\x00\x00\x01'
b'\x00\x00\x00\x03\x00\x00\x00\x02\xff\xff\xff\xff\x12\xcb\xeby1'
b'\xb6\r\x98B\xcb\x07\xbd`\x8f\x92\xd9\xc4\x84\xbdK\x00\x00\x00'
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00'
)
Georges Racinet
rust-cpython: testing the bindings from Python...
r41004 @unittest.skipIf(rustext is None or cparsers is None,
Georges Racinet
rust-cpython: binding for AncestorsIterator...
r41083 "rustext or the C Extension parsers module "
"ancestor relies on is not available")
Georges Racinet
rust-cpython: testing the bindings from Python...
r41004 class rustancestorstest(unittest.TestCase):
"""Test the correctness of binding to Rust code.
This test is merely for the binding to Rust itself: extraction of
Python variable, giving back the results etc.
It is not meant to test the algorithmic correctness of the operations
on ancestors it provides. Hence the very simple embedded index data is
good enough.
Algorithmic correctness is asserted by the Rust unit tests.
"""
Georges Racinet
rust-cpython: binding for AncestorsIterator...
r41083 def parseindex(self):
return cparsers.parse_index2(data_non_inlined, False)[0]
def testiteratorrevlist(self):
idx = self.parseindex()
# checking test assumption about the index binary data:
self.assertEqual({i: (r[5], r[6]) for i, r in enumerate(idx)},
{0: (-1, -1),
1: (0, -1),
2: (1, -1),
3: (2, -1)})
ait = AncestorsIterator(idx, [3], 0, True)
self.assertEqual([r for r in ait], [3, 2, 1, 0])
ait = AncestorsIterator(idx, [3], 0, False)
self.assertEqual([r for r in ait], [2, 1, 0])
Georges Racinet
rust-cpython: binding for LazyAncestors...
r41149 def testlazyancestors(self):
idx = self.parseindex()
start_count = sys.getrefcount(idx) # should be 2 (see Python doc)
self.assertEqual({i: (r[5], r[6]) for i, r in enumerate(idx)},
{0: (-1, -1),
1: (0, -1),
2: (1, -1),
3: (2, -1)})
lazy = LazyAncestors(idx, [3], 0, True)
# we have two more references to the index:
# - in its inner iterator for __contains__ and __bool__
# - in the LazyAncestors instance itself (to spawn new iterators)
self.assertEqual(sys.getrefcount(idx), start_count + 2)
self.assertTrue(2 in lazy)
self.assertTrue(bool(lazy))
self.assertEqual(list(lazy), [3, 2, 1, 0])
# a second time to validate that we spawn new iterators
self.assertEqual(list(lazy), [3, 2, 1, 0])
# now let's watch the refcounts closer
ait = iter(lazy)
self.assertEqual(sys.getrefcount(idx), start_count + 3)
del ait
self.assertEqual(sys.getrefcount(idx), start_count + 2)
del lazy
self.assertEqual(sys.getrefcount(idx), start_count)
# let's check bool for an empty one
self.assertFalse(LazyAncestors(idx, [0], 0, False))
Georges Racinet
rust-cpython: bindings for MissingAncestors...
r41224 def testmissingancestors(self):
idx = self.parseindex()
missanc = MissingAncestors(idx, [1])
self.assertTrue(missanc.hasbases())
self.assertEqual(missanc.missingancestors([3]), [2, 3])
missanc.addbases({2})
Georges Racinet
rust-cpython: set conversion for MissingAncestors.bases()...
r41279 self.assertEqual(missanc.bases(), {1, 2})
Georges Racinet
rust-cpython: bindings for MissingAncestors...
r41224 self.assertEqual(missanc.missingancestors([3]), [3])
Georges Racinet
rust: MissingAncestors.basesheads()...
r41282 self.assertEqual(missanc.basesheads(), {2})
Georges Racinet
rust-cpython: bindings for MissingAncestors...
r41224
def testmissingancestorsremove(self):
idx = self.parseindex()
missanc = MissingAncestors(idx, [1])
revs = {0, 1, 2, 3}
missanc.removeancestorsfrom(revs)
self.assertEqual(revs, {2, 3})
Georges Racinet
rust-cpython: binding for AncestorsIterator...
r41083 def testrefcount(self):
idx = self.parseindex()
start_count = sys.getrefcount(idx)
# refcount increases upon iterator init...
ait = AncestorsIterator(idx, [3], 0, True)
self.assertEqual(sys.getrefcount(idx), start_count + 1)
self.assertEqual(next(ait), 3)
# and decreases once the iterator is removed
del ait
self.assertEqual(sys.getrefcount(idx), start_count)
# and removing ref to the index after iterator init is no issue
ait = AncestorsIterator(idx, [3], 0, True)
del idx
Georges Racinet
rust-cpython: binding for LazyAncestors...
r41149 self.assertEqual(list(ait), [3, 2, 1, 0])
Georges Racinet
rust-cpython: testing the bindings from Python...
r41004
def testgrapherror(self):
Georges Racinet
rust-cpython: binding for AncestorsIterator...
r41083 data = (data_non_inlined[:64 + 27] +
b'\xf2' +
data_non_inlined[64 + 28:])
idx = cparsers.parse_index2(data, False)[0]
with self.assertRaises(rustext.GraphError) as arc:
AncestorsIterator(idx, [1], -1, False)
exc = arc.exception
self.assertIsInstance(exc, ValueError)
# rust-cpython issues appropriate str instances for Python 2 and 3
self.assertEqual(exc.args, ('ParentOutOfRange', 1))
Georges Racinet
rust-cpython: testing the bindings from Python...
r41004
Georges Racinet
rust-cpython: raising error.WdirUnsupported...
r41386 def testwdirunsupported(self):
# trying to access ancestors of the working directory raises
# WdirUnsupported directly
idx = self.parseindex()
with self.assertRaises(error.WdirUnsupported):
list(AncestorsIterator(idx, [node.wdirrev], -1, False))
Georges Racinet
rust-cpython: testing the bindings from Python...
r41004
Georges Racinet
rust-cpython: binding for headrevs()...
r41843 def testheadrevs(self):
idx = self.parseindex()
self.assertEqual(dagop.headrevs(idx, [1, 2, 3]), {3})
Georges Racinet
rust-cpython: testing the bindings from Python...
r41004 if __name__ == '__main__':
import silenttestrunner
silenttestrunner.main(__name__)