##// END OF EJS Templates
testing: add file storage integration for bad hashes and censoring...
Gregory Szorc -
r40087:cdf61ab1 default
parent child Browse files
Show More
@@ -861,6 +861,110 b' class ifiledatatests(basetestcase):'
861 self.assertFalse(f.cmp(node1, fulltext1))
861 self.assertFalse(f.cmp(node1, fulltext1))
862 self.assertTrue(f.cmp(node1, stored0))
862 self.assertTrue(f.cmp(node1, stored0))
863
863
864 def testbadnoderead(self):
865 f = self._makefilefn()
866
867 fulltext0 = b'foo\n' * 30
868 fulltext1 = fulltext0 + b'bar\n'
869
870 with self._maketransactionfn() as tr:
871 node0 = f.add(fulltext0, None, tr, 0, nullid, nullid)
872 node1 = b'\xaa' * 20
873
874 self._addrawrevisionfn(f, tr, node1, node0, nullid, 1,
875 rawtext=fulltext1)
876
877 self.assertEqual(len(f), 2)
878 self.assertEqual(f.parents(node1), (node0, nullid))
879
880 # revision() raises since it performs hash verification.
881 with self.assertRaises(error.StorageError):
882 f.revision(node1)
883
884 # revision(raw=True) still verifies hashes.
885 # TODO this is buggy because of cache interaction.
886 self.assertEqual(f.revision(node1, raw=True), fulltext1)
887
888 # read() behaves like revision().
889 # TODO this is buggy because of cache interaction.
890 f.read(node1)
891
892 # We can't test renamed() here because some backends may not require
893 # reading/validating the fulltext to return rename metadata.
894
895 def testbadnoderevisionraw(self):
896 # Like above except we test revision(raw=True) first to isolate
897 # revision caching behavior.
898 f = self._makefilefn()
899
900 fulltext0 = b'foo\n' * 30
901 fulltext1 = fulltext0 + b'bar\n'
902
903 with self._maketransactionfn() as tr:
904 node0 = f.add(fulltext0, None, tr, 0, nullid, nullid)
905 node1 = b'\xaa' * 20
906
907 self._addrawrevisionfn(f, tr, node1, node0, nullid, 1,
908 rawtext=fulltext1)
909
910 with self.assertRaises(error.StorageError):
911 f.revision(node1, raw=True)
912
913 with self.assertRaises(error.StorageError):
914 f.revision(node1, raw=True)
915
916 def testbadnoderevisionraw(self):
917 # Like above except we test read() first to isolate revision caching
918 # behavior.
919 f = self._makefilefn()
920
921 fulltext0 = b'foo\n' * 30
922 fulltext1 = fulltext0 + b'bar\n'
923
924 with self._maketransactionfn() as tr:
925 node0 = f.add(fulltext0, None, tr, 0, nullid, nullid)
926 node1 = b'\xaa' * 20
927
928 self._addrawrevisionfn(f, tr, node1, node0, nullid, 1,
929 rawtext=fulltext1)
930
931 with self.assertRaises(error.StorageError):
932 f.read(node1)
933
934 # TODO this should raise error.StorageError.
935 f.read(node1)
936
937 def testbadnodedelta(self):
938 f = self._makefilefn()
939
940 fulltext0 = b'foo\n' * 31
941 fulltext1 = fulltext0 + b'bar\n'
942 fulltext2 = fulltext1 + b'baz\n'
943
944 with self._maketransactionfn() as tr:
945 node0 = f.add(fulltext0, None, tr, 0, nullid, nullid)
946 node1 = b'\xaa' * 20
947
948 self._addrawrevisionfn(f, tr, node1, node0, nullid, 1,
949 rawtext=fulltext1)
950
951 with self.assertRaises(error.StorageError):
952 f.read(node1)
953
954 diff = mdiff.textdiff(fulltext1, fulltext2)
955 node2 = storageutil.hashrevisionsha1(fulltext2, node1, nullid)
956 deltas = [(node2, node1, nullid, b'\x01' * 20, node1, diff, 0)]
957
958 # This /might/ fail on some backends.
959 with self._maketransactionfn() as tr:
960 f.addgroup(deltas, lambda x: 0, tr)
961
962 self.assertEqual(len(f), 3)
963
964 # Assuming a delta is stored, we shouldn't need to validate node1 in
965 # order to retrieve node2.
966 self.assertEqual(f.read(node2), fulltext2)
967
864 def testcensored(self):
968 def testcensored(self):
865 f = self._makefilefn()
969 f = self._makefilefn()
866
970
@@ -868,20 +972,46 b' class ifiledatatests(basetestcase):'
868 b'censored': b'tombstone',
972 b'censored': b'tombstone',
869 }, b'')
973 }, b'')
870
974
871 # TODO tests are incomplete because we need the node to be
872 # different due to presence of censor metadata. But we can't
873 # do this with addrevision().
874 with self._maketransactionfn() as tr:
975 with self._maketransactionfn() as tr:
875 node0 = f.add(b'foo', None, tr, 0, nullid, nullid)
976 node0 = f.add(b'foo', None, tr, 0, nullid, nullid)
876 f.addrevision(stored1, tr, 1, node0, nullid,
977
877 flags=repository.REVISION_FLAG_CENSORED)
978 # The node value doesn't matter since we can't verify it.
979 node1 = b'\xbb' * 20
980
981 self._addrawrevisionfn(f, tr, node1, node0, nullid, 1, stored1,
982 censored=True)
878
983
879 self.assertTrue(f.iscensored(1))
984 self.assertTrue(f.iscensored(1))
880
985
881 self.assertEqual(f.revision(1), stored1)
986 with self.assertRaises(error.CensoredNodeError):
987 f.revision(1)
988
882 self.assertEqual(f.revision(1, raw=True), stored1)
989 self.assertEqual(f.revision(1, raw=True), stored1)
883
990
884 self.assertEqual(f.read(1), b'')
991 with self.assertRaises(error.CensoredNodeError):
992 f.read(1)
993
994 def testcensoredrawrevision(self):
995 # Like above, except we do the revision(raw=True) request first to
996 # isolate revision caching behavior.
997
998 f = self._makefilefn()
999
1000 stored1 = storageutil.packmeta({
1001 b'censored': b'tombstone',
1002 }, b'')
1003
1004 with self._maketransactionfn() as tr:
1005 node0 = f.add(b'foo', None, tr, 0, nullid, nullid)
1006
1007 # The node value doesn't matter since we can't verify it.
1008 node1 = b'\xbb' * 20
1009
1010 self._addrawrevisionfn(f, tr, node1, node0, nullid, 1, stored1,
1011 censored=True)
1012
1013 with self.assertRaises(error.CensoredNodeError):
1014 f.revision(1, raw=True)
885
1015
886 class ifilemutationtests(basetestcase):
1016 class ifilemutationtests(basetestcase):
887 """Generic tests for the ifilemutation interface.
1017 """Generic tests for the ifilemutation interface.
@@ -1004,6 +1134,55 b' class ifilemutationtests(basetestcase):'
1004 self.assertEqual(f.node(1), nodes[1])
1134 self.assertEqual(f.node(1), nodes[1])
1005 self.assertEqual(f.node(2), nodes[2])
1135 self.assertEqual(f.node(2), nodes[2])
1006
1136
1137 def testdeltaagainstcensored(self):
1138 # Attempt to apply a delta made against a censored revision.
1139 f = self._makefilefn()
1140
1141 stored1 = storageutil.packmeta({
1142 b'censored': b'tombstone',
1143 }, b'')
1144
1145 with self._maketransactionfn() as tr:
1146 node0 = f.add(b'foo\n' * 30, None, tr, 0, nullid, nullid)
1147
1148 # The node value doesn't matter since we can't verify it.
1149 node1 = b'\xbb' * 20
1150
1151 self._addrawrevisionfn(f, tr, node1, node0, nullid, 1, stored1,
1152 censored=True)
1153
1154 delta = mdiff.textdiff(b'bar\n' * 30, (b'bar\n' * 30) + b'baz\n')
1155 deltas = [(b'\xcc' * 20, node1, nullid, b'\x01' * 20, node1, delta, 0)]
1156
1157 with self._maketransactionfn() as tr:
1158 with self.assertRaises(error.CensoredBaseError):
1159 f.addgroup(deltas, lambda x: 0, tr)
1160
1161 def testcensorrevisionbasic(self):
1162 f = self._makefilefn()
1163
1164 with self._maketransactionfn() as tr:
1165 node0 = f.add(b'foo\n' * 30, None, tr, 0, nullid, nullid)
1166 node1 = f.add(b'foo\n' * 31, None, tr, 1, node0, nullid)
1167 node2 = f.add(b'foo\n' * 32, None, tr, 2, node1, nullid)
1168
1169 with self._maketransactionfn() as tr:
1170 f.censorrevision(tr, node1)
1171
1172 self.assertEqual(len(f), 3)
1173 self.assertEqual(list(f.revs()), [0, 1, 2])
1174
1175 self.assertEqual(f.read(node0), b'foo\n' * 30)
1176
1177 # TODO revlog can't resolve revision after censor. Probably due to a
1178 # cache on the revlog instance.
1179 with self.assertRaises(error.StorageError):
1180 self.assertEqual(f.read(node2), b'foo\n' * 32)
1181
1182 # TODO should raise CensoredNodeError, but fallout from above prevents.
1183 with self.assertRaises(error.StorageError):
1184 f.read(node1)
1185
1007 def testgetstrippointnoparents(self):
1186 def testgetstrippointnoparents(self):
1008 # N revisions where none have parents.
1187 # N revisions where none have parents.
1009 f = self._makefilefn()
1188 f = self._makefilefn()
@@ -1121,7 +1300,7 b' class ifilemutationtests(basetestcase):'
1121 with self.assertRaises(error.LookupError):
1300 with self.assertRaises(error.LookupError):
1122 f.rev(node1)
1301 f.rev(node1)
1123
1302
1124 def makeifileindextests(makefilefn, maketransactionfn):
1303 def makeifileindextests(makefilefn, maketransactionfn, addrawrevisionfn):
1125 """Create a unittest.TestCase class suitable for testing file storage.
1304 """Create a unittest.TestCase class suitable for testing file storage.
1126
1305
1127 ``makefilefn`` is a callable which receives the test case as an
1306 ``makefilefn`` is a callable which receives the test case as an
@@ -1130,6 +1309,10 b' def makeifileindextests(makefilefn, make'
1130 ``maketransactionfn`` is a callable which receives the test case as an
1309 ``maketransactionfn`` is a callable which receives the test case as an
1131 argument and returns a transaction object.
1310 argument and returns a transaction object.
1132
1311
1312 ``addrawrevisionfn`` is a callable which receives arguments describing a
1313 low-level revision to add. This callable allows the insertion of
1314 potentially bad data into the store in order to facilitate testing.
1315
1133 Returns a type that is a ``unittest.TestCase`` that can be used for
1316 Returns a type that is a ``unittest.TestCase`` that can be used for
1134 testing the object implementing the file storage interface. Simply
1317 testing the object implementing the file storage interface. Simply
1135 assign the returned value to a module-level attribute and a test loader
1318 assign the returned value to a module-level attribute and a test loader
@@ -1138,19 +1321,22 b' def makeifileindextests(makefilefn, make'
1138 d = {
1321 d = {
1139 r'_makefilefn': makefilefn,
1322 r'_makefilefn': makefilefn,
1140 r'_maketransactionfn': maketransactionfn,
1323 r'_maketransactionfn': maketransactionfn,
1324 r'_addrawrevisionfn': addrawrevisionfn,
1141 }
1325 }
1142 return type(r'ifileindextests', (ifileindextests,), d)
1326 return type(r'ifileindextests', (ifileindextests,), d)
1143
1327
1144 def makeifiledatatests(makefilefn, maketransactionfn):
1328 def makeifiledatatests(makefilefn, maketransactionfn, addrawrevisionfn):
1145 d = {
1329 d = {
1146 r'_makefilefn': makefilefn,
1330 r'_makefilefn': makefilefn,
1147 r'_maketransactionfn': maketransactionfn,
1331 r'_maketransactionfn': maketransactionfn,
1332 r'_addrawrevisionfn': addrawrevisionfn,
1148 }
1333 }
1149 return type(r'ifiledatatests', (ifiledatatests,), d)
1334 return type(r'ifiledatatests', (ifiledatatests,), d)
1150
1335
1151 def makeifilemutationtests(makefilefn, maketransactionfn):
1336 def makeifilemutationtests(makefilefn, maketransactionfn, addrawrevisionfn):
1152 d = {
1337 d = {
1153 r'_makefilefn': makefilefn,
1338 r'_makefilefn': makefilefn,
1154 r'_maketransactionfn': maketransactionfn,
1339 r'_maketransactionfn': maketransactionfn,
1340 r'_addrawrevisionfn': addrawrevisionfn,
1155 }
1341 }
1156 return type(r'ifilemutationtests', (ifilemutationtests,), d)
1342 return type(r'ifilemutationtests', (ifilemutationtests,), d)
@@ -5,7 +5,9 b' from __future__ import absolute_import'
5 import silenttestrunner
5 import silenttestrunner
6
6
7 from mercurial import (
7 from mercurial import (
8 error,
8 filelog,
9 filelog,
10 revlog,
9 transaction,
11 transaction,
10 ui as uimod,
12 ui as uimod,
11 vfs as vfsmod,
13 vfs as vfsmod,
@@ -33,14 +35,39 b' def maketransaction(self):'
33 return transaction.transaction(STATE['ui'].warn, STATE['vfs'], vfsmap,
35 return transaction.transaction(STATE['ui'].warn, STATE['vfs'], vfsmap,
34 b'journal', b'undo')
36 b'journal', b'undo')
35
37
38 def addrawrevision(self, fl, tr, node, p1, p2, linkrev, rawtext=None,
39 delta=None, censored=False, ellipsis=False, extstored=False):
40 flags = 0
41
42 if censored:
43 flags |= revlog.REVIDX_ISCENSORED
44 if ellipsis:
45 flags |= revlog.REVIDX_ELLIPSIS
46 if extstored:
47 flags |= revlog.REVIDX_EXTSTORED
48
49 if rawtext is not None:
50 fl._revlog.addrawrevision(rawtext, tr, linkrev, p1, p2, node, flags)
51 elif delta is not None:
52 raise error.Abort('support for storing raw deltas not yet supported')
53 else:
54 raise error.Abort('must supply rawtext or delta arguments')
55
56 # We may insert bad data. Clear caches to prevent e.g. cache hits to
57 # bypass hash verification.
58 fl._revlog.clearcaches()
59
36 # Assigning module-level attributes that inherit from unittest.TestCase
60 # Assigning module-level attributes that inherit from unittest.TestCase
37 # is all that is needed to register tests.
61 # is all that is needed to register tests.
38 filelogindextests = storagetesting.makeifileindextests(makefilefn,
62 filelogindextests = storagetesting.makeifileindextests(makefilefn,
39 maketransaction)
63 maketransaction,
64 addrawrevision)
40 filelogdatatests = storagetesting.makeifiledatatests(makefilefn,
65 filelogdatatests = storagetesting.makeifiledatatests(makefilefn,
41 maketransaction)
66 maketransaction,
67 addrawrevision)
42 filelogmutationtests = storagetesting.makeifilemutationtests(makefilefn,
68 filelogmutationtests = storagetesting.makeifilemutationtests(makefilefn,
43 maketransaction)
69 maketransaction,
70 addrawrevision)
44
71
45 if __name__ == '__main__':
72 if __name__ == '__main__':
46 silenttestrunner.main(__name__)
73 silenttestrunner.main(__name__)
General Comments 0
You need to be logged in to leave comments. Login now