1# Copyright (C) 2009, 2010, 2011 Canonical Ltd 2# 3# This program is free software; you can redistribute it and/or modify 4# it under the terms of the GNU General Public License as published by 5# the Free Software Foundation; either version 2 of the License, or 6# (at your option) any later version. 7# 8# This program is distributed in the hope that it will be useful, 9# but WITHOUT ANY WARRANTY; without even the implied warranty of 10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11# GNU General Public License for more details. 12# 13# You should have received a copy of the GNU General Public License 14# along with this program; if not, write to the Free Software 15# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 16 17"""Tests for _chk_map_*.""" 18 19from ... import ( 20 tests, 21 ) 22from .. import ( 23 chk_map, 24 ) 25from ...static_tuple import StaticTuple 26stuple = StaticTuple 27 28 29def load_tests(loader, standard_tests, pattern): 30 suite, _ = tests.permute_tests_for_extension(standard_tests, loader, 31 'breezy.bzr._chk_map_py', 'breezy.bzr._chk_map_pyx') 32 return suite 33 34 35class TestSearchKeys(tests.TestCase): 36 37 module = None # Filled in by test parameterization 38 39 def assertSearchKey16(self, expected, key): 40 self.assertEqual(expected, self.module._search_key_16(key)) 41 42 def assertSearchKey255(self, expected, key): 43 actual = self.module._search_key_255(key) 44 self.assertEqual(expected, actual, 'actual: %r' % (actual,)) 45 46 def test_simple_16(self): 47 self.assertSearchKey16(b'8C736521', stuple(b'foo',)) 48 self.assertSearchKey16(b'8C736521\x008C736521', stuple(b'foo', b'foo')) 49 self.assertSearchKey16(b'8C736521\x0076FF8CAA', stuple(b'foo', b'bar')) 50 self.assertSearchKey16(b'ED82CD11', stuple(b'abcd',)) 51 52 def test_simple_255(self): 53 self.assertSearchKey255(b'\x8cse!', stuple(b'foo',)) 54 self.assertSearchKey255(b'\x8cse!\x00\x8cse!', stuple(b'foo', b'foo')) 55 self.assertSearchKey255( 56 b'\x8cse!\x00v\xff\x8c\xaa', stuple(b'foo', b'bar')) 57 # The standard mapping for these would include '\n', so it should be 58 # mapped to '_' 59 self.assertSearchKey255(b'\xfdm\x93_\x00P_\x1bL', stuple(b'<', b'V')) 60 61 def test_255_does_not_include_newline(self): 62 # When mapping via _search_key_255, we should never have the '\n' 63 # character, but all other 255 values should be present 64 chars_used = set() 65 for char_in in range(256): 66 search_key = self.module._search_key_255( 67 stuple(bytes([char_in]),)) 68 chars_used.update([bytes([x]) for x in search_key]) 69 all_chars = {bytes([x]) for x in range(256)} 70 unused_chars = all_chars.symmetric_difference(chars_used) 71 self.assertEqual({b'\n'}, unused_chars) 72 73 74class TestDeserialiseLeafNode(tests.TestCase): 75 76 module = None 77 78 def assertDeserialiseErrors(self, text): 79 self.assertRaises((ValueError, IndexError), 80 self.module._deserialise_leaf_node, text, b'not-a-real-sha') 81 82 def test_raises_on_non_leaf(self): 83 self.assertDeserialiseErrors(b'') 84 self.assertDeserialiseErrors(b'short\n') 85 self.assertDeserialiseErrors(b'chknotleaf:\n') 86 self.assertDeserialiseErrors(b'chkleaf:x\n') 87 self.assertDeserialiseErrors(b'chkleaf:\n') 88 self.assertDeserialiseErrors(b'chkleaf:\nnotint\n') 89 self.assertDeserialiseErrors(b'chkleaf:\n10\n') 90 self.assertDeserialiseErrors(b'chkleaf:\n10\n256\n') 91 self.assertDeserialiseErrors(b'chkleaf:\n10\n256\n10\n') 92 93 def test_deserialise_empty(self): 94 node = self.module._deserialise_leaf_node( 95 b"chkleaf:\n10\n1\n0\n\n", stuple(b"sha1:1234",)) 96 self.assertEqual(0, len(node)) 97 self.assertEqual(10, node.maximum_size) 98 self.assertEqual((b"sha1:1234",), node.key()) 99 self.assertIsInstance(node.key(), StaticTuple) 100 self.assertIs(None, node._search_prefix) 101 self.assertIs(None, node._common_serialised_prefix) 102 103 def test_deserialise_items(self): 104 node = self.module._deserialise_leaf_node( 105 b"chkleaf:\n0\n1\n2\n\nfoo bar\x001\nbaz\nquux\x001\nblarh\n", 106 (b"sha1:1234",)) 107 self.assertEqual(2, len(node)) 108 self.assertEqual([((b"foo bar",), b"baz"), ((b"quux",), b"blarh")], 109 sorted(node.iteritems(None))) 110 111 def test_deserialise_item_with_null_width_1(self): 112 node = self.module._deserialise_leaf_node( 113 b"chkleaf:\n0\n1\n2\n\nfoo\x001\nbar\x00baz\nquux\x001\nblarh\n", 114 (b"sha1:1234",)) 115 self.assertEqual(2, len(node)) 116 self.assertEqual([((b"foo",), b"bar\x00baz"), ((b"quux",), b"blarh")], 117 sorted(node.iteritems(None))) 118 119 def test_deserialise_item_with_null_width_2(self): 120 node = self.module._deserialise_leaf_node( 121 b"chkleaf:\n0\n2\n2\n\nfoo\x001\x001\nbar\x00baz\n" 122 b"quux\x00\x001\nblarh\n", 123 (b"sha1:1234",)) 124 self.assertEqual(2, len(node)) 125 self.assertEqual([((b"foo", b"1"), b"bar\x00baz"), ((b"quux", b""), b"blarh")], 126 sorted(node.iteritems(None))) 127 128 def test_iteritems_selected_one_of_two_items(self): 129 node = self.module._deserialise_leaf_node( 130 b"chkleaf:\n0\n1\n2\n\nfoo bar\x001\nbaz\nquux\x001\nblarh\n", 131 (b"sha1:1234",)) 132 self.assertEqual(2, len(node)) 133 self.assertEqual([((b"quux",), b"blarh")], 134 sorted(node.iteritems(None, [(b"quux",), (b"qaz",)]))) 135 136 def test_deserialise_item_with_common_prefix(self): 137 node = self.module._deserialise_leaf_node( 138 b"chkleaf:\n0\n2\n2\nfoo\x00\n1\x001\nbar\x00baz\n2\x001\nblarh\n", 139 (b"sha1:1234",)) 140 self.assertEqual(2, len(node)) 141 self.assertEqual([((b"foo", b"1"), b"bar\x00baz"), ((b"foo", b"2"), b"blarh")], 142 sorted(node.iteritems(None))) 143 self.assertIs(chk_map._unknown, node._search_prefix) 144 self.assertEqual(b'foo\x00', node._common_serialised_prefix) 145 146 def test_deserialise_multi_line(self): 147 node = self.module._deserialise_leaf_node( 148 b"chkleaf:\n0\n2\n2\nfoo\x00\n1\x002\nbar\nbaz\n2\x002\nblarh\n\n", 149 (b"sha1:1234",)) 150 self.assertEqual(2, len(node)) 151 self.assertEqual([((b"foo", b"1"), b"bar\nbaz"), 152 ((b"foo", b"2"), b"blarh\n"), 153 ], sorted(node.iteritems(None))) 154 self.assertIs(chk_map._unknown, node._search_prefix) 155 self.assertEqual(b'foo\x00', node._common_serialised_prefix) 156 157 def test_key_after_map(self): 158 node = self.module._deserialise_leaf_node( 159 b"chkleaf:\n10\n1\n0\n\n", (b"sha1:1234",)) 160 node.map(None, (b"foo bar",), b"baz quux") 161 self.assertEqual(None, node.key()) 162 163 def test_key_after_unmap(self): 164 node = self.module._deserialise_leaf_node( 165 b"chkleaf:\n0\n1\n2\n\nfoo bar\x001\nbaz\nquux\x001\nblarh\n", 166 (b"sha1:1234",)) 167 node.unmap(None, (b"foo bar",)) 168 self.assertEqual(None, node.key()) 169 170 171class TestDeserialiseInternalNode(tests.TestCase): 172 173 module = None 174 175 def assertDeserialiseErrors(self, text): 176 self.assertRaises((ValueError, IndexError), 177 self.module._deserialise_internal_node, text, 178 stuple(b'not-a-real-sha',)) 179 180 def test_raises_on_non_internal(self): 181 self.assertDeserialiseErrors(b'') 182 self.assertDeserialiseErrors(b'short\n') 183 self.assertDeserialiseErrors(b'chknotnode:\n') 184 self.assertDeserialiseErrors(b'chknode:x\n') 185 self.assertDeserialiseErrors(b'chknode:\n') 186 self.assertDeserialiseErrors(b'chknode:\nnotint\n') 187 self.assertDeserialiseErrors(b'chknode:\n10\n') 188 self.assertDeserialiseErrors(b'chknode:\n10\n256\n') 189 self.assertDeserialiseErrors(b'chknode:\n10\n256\n10\n') 190 # no trailing newline 191 self.assertDeserialiseErrors(b'chknode:\n10\n256\n0\n1\nfo') 192 193 def test_deserialise_one(self): 194 node = self.module._deserialise_internal_node( 195 b"chknode:\n10\n1\n1\n\na\x00sha1:abcd\n", stuple(b'sha1:1234',)) 196 self.assertIsInstance(node, chk_map.InternalNode) 197 self.assertEqual(1, len(node)) 198 self.assertEqual(10, node.maximum_size) 199 self.assertEqual((b"sha1:1234",), node.key()) 200 self.assertEqual(b'', node._search_prefix) 201 self.assertEqual({b'a': (b'sha1:abcd',)}, node._items) 202 203 def test_deserialise_with_prefix(self): 204 node = self.module._deserialise_internal_node( 205 b"chknode:\n10\n1\n1\npref\na\x00sha1:abcd\n", 206 stuple(b'sha1:1234',)) 207 self.assertIsInstance(node, chk_map.InternalNode) 208 self.assertEqual(1, len(node)) 209 self.assertEqual(10, node.maximum_size) 210 self.assertEqual((b"sha1:1234",), node.key()) 211 self.assertEqual(b'pref', node._search_prefix) 212 self.assertEqual({b'prefa': (b'sha1:abcd',)}, node._items) 213 214 node = self.module._deserialise_internal_node( 215 b"chknode:\n10\n1\n1\npref\n\x00sha1:abcd\n", 216 stuple(b'sha1:1234',)) 217 self.assertIsInstance(node, chk_map.InternalNode) 218 self.assertEqual(1, len(node)) 219 self.assertEqual(10, node.maximum_size) 220 self.assertEqual((b"sha1:1234",), node.key()) 221 self.assertEqual(b'pref', node._search_prefix) 222 self.assertEqual({b'pref': (b'sha1:abcd',)}, node._items) 223 224 def test_deserialise_pref_with_null(self): 225 node = self.module._deserialise_internal_node( 226 b"chknode:\n10\n1\n1\npref\x00fo\n\x00sha1:abcd\n", 227 stuple(b'sha1:1234',)) 228 self.assertIsInstance(node, chk_map.InternalNode) 229 self.assertEqual(1, len(node)) 230 self.assertEqual(10, node.maximum_size) 231 self.assertEqual((b"sha1:1234",), node.key()) 232 self.assertEqual(b'pref\x00fo', node._search_prefix) 233 self.assertEqual({b'pref\x00fo': (b'sha1:abcd',)}, node._items) 234 235 def test_deserialise_with_null_pref(self): 236 node = self.module._deserialise_internal_node( 237 b"chknode:\n10\n1\n1\npref\x00fo\n\x00\x00sha1:abcd\n", 238 stuple(b'sha1:1234',)) 239 self.assertIsInstance(node, chk_map.InternalNode) 240 self.assertEqual(1, len(node)) 241 self.assertEqual(10, node.maximum_size) 242 self.assertEqual((b"sha1:1234",), node.key()) 243 self.assertEqual(b'pref\x00fo', node._search_prefix) 244 self.assertEqual({b'pref\x00fo\x00': (b'sha1:abcd',)}, node._items) 245 246 247class Test_BytesToTextKey(tests.TestCase): 248 249 def assertBytesToTextKey(self, key, bytes): 250 self.assertEqual(key, 251 self.module._bytes_to_text_key(bytes)) 252 253 def assertBytesToTextKeyRaises(self, bytes): 254 # These are invalid bytes, and we want to make sure the code under test 255 # raises an exception rather than segfaults, etc. We don't particularly 256 # care what exception. 257 self.assertRaises(Exception, self.module._bytes_to_text_key, bytes) 258 259 def test_file(self): 260 self.assertBytesToTextKey((b'file-id', b'revision-id'), 261 b'file: file-id\nparent-id\nname\nrevision-id\n' 262 b'da39a3ee5e6b4b0d3255bfef95601890afd80709\n100\nN') 263 264 def test_invalid_no_kind(self): 265 self.assertBytesToTextKeyRaises( 266 b'file file-id\nparent-id\nname\nrevision-id\n' 267 b'da39a3ee5e6b4b0d3255bfef95601890afd80709\n100\nN') 268 269 def test_invalid_no_space(self): 270 self.assertBytesToTextKeyRaises( 271 b'file:file-id\nparent-id\nname\nrevision-id\n' 272 b'da39a3ee5e6b4b0d3255bfef95601890afd80709\n100\nN') 273 274 def test_invalid_too_short_file_id(self): 275 self.assertBytesToTextKeyRaises(b'file:file-id') 276 277 def test_invalid_too_short_parent_id(self): 278 self.assertBytesToTextKeyRaises(b'file:file-id\nparent-id') 279 280 def test_invalid_too_short_name(self): 281 self.assertBytesToTextKeyRaises(b'file:file-id\nparent-id\nname') 282 283 def test_dir(self): 284 self.assertBytesToTextKey((b'dir-id', b'revision-id'), 285 b'dir: dir-id\nparent-id\nname\nrevision-id') 286