Rev 2512: Add validate method to ContainerReader and BytesRecordReader. in http://bazaar.launchpad.net/~bzr/bzr/container-format
Andrew Bennetts
andrew.bennetts at canonical.com
Thu Jun 14 06:54:10 BST 2007
At http://bazaar.launchpad.net/~bzr/bzr/container-format
------------------------------------------------------------
revno: 2512
revision-id: andrew.bennetts at canonical.com-20070614055245-rtwk0vgz74fyyimo
parent: andrew.bennetts at canonical.com-20070614022816-ne4h4qk0j50x6n26
committer: Andrew Bennetts <andrew.bennetts at canonical.com>
branch nick: container-format
timestamp: Thu 2007-06-14 15:52:45 +1000
message:
Add validate method to ContainerReader and BytesRecordReader.
modified:
bzrlib/errors.py errors.py-20050309040759-20512168c4e14fbd
bzrlib/pack.py container.py-20070607160755-tr8zc26q18rn0jnb-1
bzrlib/tests/test_errors.py test_errors.py-20060210110251-41aba2deddf936a8
bzrlib/tests/test_pack.py test_container.py-20070607160755-tr8zc26q18rn0jnb-2
=== modified file 'bzrlib/errors.py'
--- a/bzrlib/errors.py 2007-06-08 06:33:59 +0000
+++ b/bzrlib/errors.py 2007-06-14 05:52:45 +0000
@@ -2170,3 +2170,11 @@
def __init__(self, reason):
self.reason = reason
+
+class ContainerHasExcessDataError(ContainerError):
+
+ _fmt = "Container has data after end marker: %(excess)r"
+
+ def __init__(self, excess):
+ self.excess = excess
+
=== modified file 'bzrlib/pack.py'
--- a/bzrlib/pack.py 2007-06-13 10:54:46 +0000
+++ b/bzrlib/pack.py 2007-06-14 05:52:45 +0000
@@ -148,10 +148,31 @@
# Unknown record type.
raise errors.UnknownRecordTypeError(record_kind)
+ def validate(self):
+ """Validate this container and its records.
+
+ You can either validate or iter_records, you can't do both.
+
+ :raises ContainerError: if something is invalid.
+ """
+ for names, bytes in self.iter_records():
+ # XXX: bytes is str, should be callable to get bytes.
+ # callable()
+ pass
+ excess_bytes = self.reader_func(1)
+ if excess_bytes != '':
+ raise errors.ContainerHasExcessDataError(excess_bytes)
+
class BytesRecordReader(BaseReader):
def read(self):
+ """Read this record.
+
+ You can either validate or read, you can't do both.
+
+ :returns: (names, bytes)
+ """
# Read the content length.
length_line = self._read_line()
try:
@@ -173,3 +194,11 @@
raise errors.UnexpectedEndOfContainerError()
return names, bytes
+ def validate(self):
+ """Validate this record.
+
+ You can either validate or read, you can't do both.
+
+ :raises ContainerError: if this record is invalid.
+ """
+ self.read()
=== modified file 'bzrlib/tests/test_errors.py'
--- a/bzrlib/tests/test_errors.py 2007-06-08 06:33:59 +0000
+++ b/bzrlib/tests/test_errors.py 2007-06-14 05:52:45 +0000
@@ -293,6 +293,13 @@
"Invalid record: xxx",
str(e))
+ def test_container_has_excess_data(self):
+ """Test the formatting of ContainerHasExcessDataError."""
+ e = errors.ContainerHasExcessDataError("excess bytes")
+ self.assertEqual(
+ "Container has data after end marker: 'excess bytes'",
+ str(e))
+
class PassThroughError(errors.BzrError):
=== modified file 'bzrlib/tests/test_pack.py'
--- a/bzrlib/tests/test_pack.py 2007-06-13 10:54:46 +0000
+++ b/bzrlib/tests/test_pack.py 2007-06-14 05:52:45 +0000
@@ -87,6 +87,11 @@
class TestContainerReader(tests.TestCase):
+ def get_reader_for(self, bytes):
+ stream = StringIO(bytes)
+ reader = pack.ContainerReader(stream.read)
+ return reader
+
def test_construct(self):
"""Test constructing a ContainerReader.
@@ -97,14 +102,12 @@
def test_empty_container(self):
"""Read an empty container."""
- input = StringIO("Bazaar pack format 1\nE")
- reader = pack.ContainerReader(input.read)
+ reader = self.get_reader_for("Bazaar pack format 1\nE")
self.assertEqual([], list(reader.iter_records()))
def test_unknown_format(self):
"""Unrecognised container formats raise UnknownContainerFormatError."""
- input = StringIO("unknown format\n")
- reader = pack.ContainerReader(input.read)
+ reader = self.get_reader_for("unknown format\n")
self.assertRaises(
errors.UnknownContainerFormatError, reader.iter_records)
@@ -112,16 +115,14 @@
"""Containers that don't end with an End Marker record should cause
UnexpectedEndOfContainerError to be raised.
"""
- input = StringIO("Bazaar pack format 1\n")
- reader = pack.ContainerReader(input.read)
+ reader = self.get_reader_for("Bazaar pack format 1\n")
iterator = reader.iter_records()
self.assertRaises(
errors.UnexpectedEndOfContainerError, iterator.next)
def test_unknown_record_type(self):
"""Unknown record types cause UnknownRecordTypeError to be raised."""
- input = StringIO("Bazaar pack format 1\nX")
- reader = pack.ContainerReader(input.read)
+ reader = self.get_reader_for("Bazaar pack format 1\nX")
iterator = reader.iter_records()
self.assertRaises(
errors.UnknownRecordTypeError, iterator.next)
@@ -133,21 +134,57 @@
TestBytesRecordReader. This test is here to ensure that
ContainerReader's integration with BytesRecordReader is working.
"""
- input = StringIO("Bazaar pack format 1\nB5\n\naaaaaE")
- reader = pack.ContainerReader(input.read)
+ reader = self.get_reader_for("Bazaar pack format 1\nB5\n\naaaaaE")
expected_records = [([], 'aaaaa')]
self.assertEqual(expected_records, list(reader.iter_records()))
+ def test_validate_empty_container(self):
+ reader = self.get_reader_for("Bazaar pack format 1\nE")
+ # No exception raised
+ reader.validate()
+
+ def test_validate_non_empty_valid_container(self):
+ reader = self.get_reader_for("Bazaar pack format 1\nB3\nname\n\nabcE")
+ # No exception raised
+ reader.validate()
+
+ def test_validate_bad_format(self):
+ inputs = ["", "x", "Bazaar pack format 1", "bad\n"]
+ for input in inputs:
+ reader = self.get_reader_for(input)
+ self.assertRaises(
+ (errors.UnexpectedEndOfContainerError,
+ errors.UnknownContainerFormatError),
+ reader.validate)
+
+ def test_validate_bad_record_marker(self):
+ reader = self.get_reader_for("Bazaar pack format 1\nX")
+ self.assertRaises(errors.UnknownRecordTypeError, reader.validate)
+
+ def test_validate_data_after_end_marker(self):
+ reader = self.get_reader_for("Bazaar pack format 1\nEcrud")
+ self.assertRaises(
+ errors.ContainerHasExcessDataError, reader.validate)
+
+ def test_validate_no_end_marker(self):
+ reader = self.get_reader_for("Bazaar pack format 1\n")
+ self.assertRaises(
+ errors.UnexpectedEndOfContainerError, reader.validate)
+
class TestBytesRecordReader(tests.TestCase):
"""Tests for parsing Bytes records with BytesRecordReader."""
+ def get_reader_for(self, bytes):
+ stream = StringIO(bytes)
+ reader = pack.BytesRecordReader(stream.read)
+ return reader
+
def test_record_with_no_name(self):
"""Reading a Bytes record with no name returns an empty list of
names.
"""
- input = StringIO("5\n\naaaaa")
- reader = pack.BytesRecordReader(input.read)
+ reader = self.get_reader_for("5\n\naaaaa")
names, bytes = reader.read()
self.assertEqual([], names)
self.assertEqual('aaaaa', bytes)
@@ -156,8 +193,7 @@
"""Reading a Bytes record with one name returns a list of just that
name.
"""
- input = StringIO("5\nname1\n\naaaaa")
- reader = pack.BytesRecordReader(input.read)
+ reader = self.get_reader_for("5\nname1\n\naaaaa")
names, bytes = reader.read()
self.assertEqual(['name1'], names)
self.assertEqual('aaaaa', bytes)
@@ -165,8 +201,7 @@
def test_record_with_two_names(self):
"""Reading a Bytes record with two names returns a list of both names.
"""
- input = StringIO("5\nname1\nname2\n\naaaaa")
- reader = pack.BytesRecordReader(input.read)
+ reader = self.get_reader_for("5\nname1\nname2\n\naaaaa")
names, bytes = reader.read()
self.assertEqual(['name1', 'name2'], names)
self.assertEqual('aaaaa', bytes)
@@ -175,8 +210,7 @@
"""If the length-prefix is not a number, parsing raises
InvalidRecordError.
"""
- input = StringIO("not a number\n")
- reader = pack.BytesRecordReader(input.read)
+ reader = self.get_reader_for("not a number\n")
self.assertRaises(errors.InvalidRecordError, reader.read)
def test_early_eof(self):
@@ -191,8 +225,7 @@
"""
complete_record = "6\nname\n\nabcdef"
for count in range(0, len(complete_record)):
- input = StringIO(complete_record[:count])
- reader = pack.BytesRecordReader(input.read)
+ reader = self.get_reader_for(complete_record[:count])
# We don't use assertRaises to make diagnosing failures easier.
try:
reader.read()
@@ -205,37 +238,49 @@
def test_initial_eof(self):
"""EOF before any bytes read at all."""
- input = StringIO("")
- reader = pack.BytesRecordReader(input.read)
+ reader = self.get_reader_for("")
self.assertRaises(errors.UnexpectedEndOfContainerError, reader.read)
def test_eof_after_length(self):
"""EOF after reading the length and before reading name(s)."""
- input = StringIO("123\n")
- reader = pack.BytesRecordReader(input.read)
+ reader = self.get_reader_for("123\n")
self.assertRaises(errors.UnexpectedEndOfContainerError, reader.read)
def test_eof_during_name(self):
"""EOF during reading a name."""
- input = StringIO("123\nname")
- reader = pack.BytesRecordReader(input.read)
+ reader = self.get_reader_for("123\nname")
self.assertRaises(errors.UnexpectedEndOfContainerError, reader.read)
def test_invalid_name_whitespace(self):
"""Names must have no whitespace."""
# A name with a space.
- input = StringIO("0\nbad name\n\n")
- reader = pack.BytesRecordReader(input.read)
+ reader = self.get_reader_for("0\nbad name\n\n")
self.assertRaises(errors.InvalidRecordError, reader.read)
# A name with a tab.
- input = StringIO("0\nbad\tname\n\n")
- reader = pack.BytesRecordReader(input.read)
+ reader = self.get_reader_for("0\nbad\tname\n\n")
self.assertRaises(errors.InvalidRecordError, reader.read)
# A name with a vertical tab.
- input = StringIO("0\nbad\vname\n\n")
- reader = pack.BytesRecordReader(input.read)
+ reader = self.get_reader_for("0\nbad\vname\n\n")
self.assertRaises(errors.InvalidRecordError, reader.read)
-
+ def test_validate_whitespace_in_name(self):
+ reader = self.get_reader_for("0\nbad name\n\nE")
+ self.assertRaises(errors.InvalidRecordError, reader.validate)
+
+ def test_validate_interrupted_prelude(self):
+ reader = self.get_reader_for("")
+ self.assertRaises(
+ errors.UnexpectedEndOfContainerError, reader.validate)
+
+ def test_validate_interrupted_body(self):
+ reader = self.get_reader_for("1\n\n")
+ self.assertRaises(
+ errors.UnexpectedEndOfContainerError, reader.validate)
+
+ def test_validate_unparseable_length(self):
+ reader = self.get_reader_for("\n\n")
+ self.assertRaises(
+ errors.InvalidRecordError, reader.validate)
+
More information about the bazaar-commits
mailing list