Show More
@@ -861,6 +861,110 b' class ifiledatatests(basetestcase):' | |||
|
861 | 861 | self.assertFalse(f.cmp(node1, fulltext1)) |
|
862 | 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 | 968 | def testcensored(self): |
|
865 | 969 | f = self._makefilefn() |
|
866 | 970 | |
@@ -868,20 +972,46 b' class ifiledatatests(basetestcase):' | |||
|
868 | 972 | b'censored': b'tombstone', |
|
869 | 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 | 975 | with self._maketransactionfn() as tr: |
|
875 | 976 | node0 = f.add(b'foo', None, tr, 0, nullid, nullid) |
|
876 | f.addrevision(stored1, tr, 1, node0, nullid, | |
|
877 | flags=repository.REVISION_FLAG_CENSORED) | |
|
977 | ||
|
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 | 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 | 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 | 1016 | class ifilemutationtests(basetestcase): |
|
887 | 1017 | """Generic tests for the ifilemutation interface. |
@@ -1004,6 +1134,55 b' class ifilemutationtests(basetestcase):' | |||
|
1004 | 1134 | self.assertEqual(f.node(1), nodes[1]) |
|
1005 | 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 | 1186 | def testgetstrippointnoparents(self): |
|
1008 | 1187 | # N revisions where none have parents. |
|
1009 | 1188 | f = self._makefilefn() |
@@ -1121,7 +1300,7 b' class ifilemutationtests(basetestcase):' | |||
|
1121 | 1300 | with self.assertRaises(error.LookupError): |
|
1122 | 1301 | f.rev(node1) |
|
1123 | 1302 | |
|
1124 | def makeifileindextests(makefilefn, maketransactionfn): | |
|
1303 | def makeifileindextests(makefilefn, maketransactionfn, addrawrevisionfn): | |
|
1125 | 1304 | """Create a unittest.TestCase class suitable for testing file storage. |
|
1126 | 1305 | |
|
1127 | 1306 | ``makefilefn`` is a callable which receives the test case as an |
@@ -1130,6 +1309,10 b' def makeifileindextests(makefilefn, make' | |||
|
1130 | 1309 | ``maketransactionfn`` is a callable which receives the test case as an |
|
1131 | 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 | 1316 | Returns a type that is a ``unittest.TestCase`` that can be used for |
|
1134 | 1317 | testing the object implementing the file storage interface. Simply |
|
1135 | 1318 | assign the returned value to a module-level attribute and a test loader |
@@ -1138,19 +1321,22 b' def makeifileindextests(makefilefn, make' | |||
|
1138 | 1321 | d = { |
|
1139 | 1322 | r'_makefilefn': makefilefn, |
|
1140 | 1323 | r'_maketransactionfn': maketransactionfn, |
|
1324 | r'_addrawrevisionfn': addrawrevisionfn, | |
|
1141 | 1325 | } |
|
1142 | 1326 | return type(r'ifileindextests', (ifileindextests,), d) |
|
1143 | 1327 | |
|
1144 | def makeifiledatatests(makefilefn, maketransactionfn): | |
|
1328 | def makeifiledatatests(makefilefn, maketransactionfn, addrawrevisionfn): | |
|
1145 | 1329 | d = { |
|
1146 | 1330 | r'_makefilefn': makefilefn, |
|
1147 | 1331 | r'_maketransactionfn': maketransactionfn, |
|
1332 | r'_addrawrevisionfn': addrawrevisionfn, | |
|
1148 | 1333 | } |
|
1149 | 1334 | return type(r'ifiledatatests', (ifiledatatests,), d) |
|
1150 | 1335 | |
|
1151 | def makeifilemutationtests(makefilefn, maketransactionfn): | |
|
1336 | def makeifilemutationtests(makefilefn, maketransactionfn, addrawrevisionfn): | |
|
1152 | 1337 | d = { |
|
1153 | 1338 | r'_makefilefn': makefilefn, |
|
1154 | 1339 | r'_maketransactionfn': maketransactionfn, |
|
1340 | r'_addrawrevisionfn': addrawrevisionfn, | |
|
1155 | 1341 | } |
|
1156 | 1342 | return type(r'ifilemutationtests', (ifilemutationtests,), d) |
@@ -5,7 +5,9 b' from __future__ import absolute_import' | |||
|
5 | 5 | import silenttestrunner |
|
6 | 6 | |
|
7 | 7 | from mercurial import ( |
|
8 | error, | |
|
8 | 9 | filelog, |
|
10 | revlog, | |
|
9 | 11 | transaction, |
|
10 | 12 | ui as uimod, |
|
11 | 13 | vfs as vfsmod, |
@@ -33,14 +35,39 b' def maketransaction(self):' | |||
|
33 | 35 | return transaction.transaction(STATE['ui'].warn, STATE['vfs'], vfsmap, |
|
34 | 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 | 60 | # Assigning module-level attributes that inherit from unittest.TestCase |
|
37 | 61 | # is all that is needed to register tests. |
|
38 | 62 | filelogindextests = storagetesting.makeifileindextests(makefilefn, |
|
39 |
maketransaction |
|
|
63 | maketransaction, | |
|
64 | addrawrevision) | |
|
40 | 65 | filelogdatatests = storagetesting.makeifiledatatests(makefilefn, |
|
41 |
maketransaction |
|
|
66 | maketransaction, | |
|
67 | addrawrevision) | |
|
42 | 68 | filelogmutationtests = storagetesting.makeifilemutationtests(makefilefn, |
|
43 |
maketransaction |
|
|
69 | maketransaction, | |
|
70 | addrawrevision) | |
|
44 | 71 | |
|
45 | 72 | if __name__ == '__main__': |
|
46 | 73 | silenttestrunner.main(__name__) |
General Comments 0
You need to be logged in to leave comments.
Login now