859 self.assertTrue(f.cmp(node0, stored0)) |
859 self.assertTrue(f.cmp(node0, stored0)) |
860 |
860 |
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 |
867 stored1 = storageutil.packmeta({ |
971 stored1 = storageutil.packmeta({ |
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. |
888 |
1018 |
889 All file storage backends that support writing should conform to this |
1019 All file storage backends that support writing should conform to this |
1001 self.assertEqual(f.rev(nodes[1]), 1) |
1131 self.assertEqual(f.rev(nodes[1]), 1) |
1002 self.assertEqual(f.rev(nodes[2]), 2) |
1132 self.assertEqual(f.rev(nodes[2]), 2) |
1003 self.assertEqual(f.node(0), nodes[0]) |
1133 self.assertEqual(f.node(0), nodes[0]) |
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]) |
|
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) |
1006 |
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() |
1010 |
1189 |
1119 self.assertEqual(len(f), 1) |
1298 self.assertEqual(len(f), 1) |
1120 |
1299 |
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 |
1128 argument and returns an object implementing the ``ifilestorage`` interface. |
1307 argument and returns an object implementing the ``ifilestorage`` interface. |
1129 |
1308 |
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. |
|
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. |
1132 |
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 |
1136 should find and run it automatically. |
1319 should find and run it automatically. |
1137 """ |
1320 """ |
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) |