| 1 | """ |
|---|
| 2 | Ported to Python 3. |
|---|
| 3 | """ |
|---|
| 4 | |
|---|
| 5 | import os.path, json |
|---|
| 6 | from twisted.trial import unittest |
|---|
| 7 | from twisted.python import usage |
|---|
| 8 | from twisted.internet import defer |
|---|
| 9 | |
|---|
| 10 | from allmydata.scripts import cli |
|---|
| 11 | from allmydata.util import fileutil |
|---|
| 12 | from allmydata.util.encodingutil import (quote_output, unicode_to_output, to_bytes) |
|---|
| 13 | from allmydata.util.assertutil import _assert |
|---|
| 14 | from ..no_network import GridTestMixin |
|---|
| 15 | from .common import CLITestMixin |
|---|
| 16 | from ..common_util import skip_if_cannot_represent_filename |
|---|
| 17 | |
|---|
| 18 | class Cp(GridTestMixin, CLITestMixin, unittest.TestCase): |
|---|
| 19 | |
|---|
| 20 | def test_not_enough_args(self): |
|---|
| 21 | o = cli.CpOptions() |
|---|
| 22 | self.failUnlessRaises(usage.UsageError, |
|---|
| 23 | o.parseOptions, ["onearg"]) |
|---|
| 24 | |
|---|
| 25 | def test_unicode_filename(self): |
|---|
| 26 | self.basedir = "cli/Cp/unicode_filename" |
|---|
| 27 | |
|---|
| 28 | fn1 = os.path.join(self.basedir, u"\u00C4rtonwall") |
|---|
| 29 | artonwall_arg = u"\u00C4rtonwall" |
|---|
| 30 | |
|---|
| 31 | skip_if_cannot_represent_filename(fn1) |
|---|
| 32 | |
|---|
| 33 | self.set_up_grid(oneshare=True) |
|---|
| 34 | |
|---|
| 35 | DATA1 = "unicode file content" |
|---|
| 36 | fileutil.write(fn1, DATA1) |
|---|
| 37 | |
|---|
| 38 | fn2 = os.path.join(self.basedir, "Metallica") |
|---|
| 39 | DATA2 = "non-unicode file content" |
|---|
| 40 | fileutil.write(fn2, DATA2) |
|---|
| 41 | |
|---|
| 42 | d = self.do_cli("create-alias", "tahoe") |
|---|
| 43 | |
|---|
| 44 | d.addCallback(lambda res: self.do_cli("cp", fn1, "tahoe:")) |
|---|
| 45 | |
|---|
| 46 | d.addCallback(lambda res: self.do_cli("get", "tahoe:" + artonwall_arg)) |
|---|
| 47 | d.addCallback(lambda rc_out_err: self.assertEqual(rc_out_err[1], DATA1)) |
|---|
| 48 | |
|---|
| 49 | # Version where destination filename is explicitly Unicode too. |
|---|
| 50 | d.addCallback(lambda res: self.do_cli("cp", fn1, "tahoe:" + artonwall_arg + "-2")) |
|---|
| 51 | d.addCallback(lambda res: self.do_cli("get", "tahoe:" + artonwall_arg + "-2")) |
|---|
| 52 | d.addCallback(lambda rc_out_err: self.assertEqual(rc_out_err[1], DATA1)) |
|---|
| 53 | |
|---|
| 54 | d.addCallback(lambda res: self.do_cli("cp", fn2, "tahoe:")) |
|---|
| 55 | |
|---|
| 56 | d.addCallback(lambda res: self.do_cli("get", "tahoe:Metallica")) |
|---|
| 57 | d.addCallback(lambda rc_out_err: self.assertEqual(rc_out_err[1], DATA2)) |
|---|
| 58 | |
|---|
| 59 | d.addCallback(lambda res: self.do_cli("ls", "tahoe:")) |
|---|
| 60 | def _check(args): |
|---|
| 61 | (rc, out, err) = args |
|---|
| 62 | try: |
|---|
| 63 | unicode_to_output(u"\u00C4rtonwall") |
|---|
| 64 | except UnicodeEncodeError: |
|---|
| 65 | self.failUnlessReallyEqual(rc, 1) |
|---|
| 66 | self.failUnlessReallyEqual(out, "Metallica\n") |
|---|
| 67 | self.failUnlessIn(quote_output(u"\u00C4rtonwall"), err) |
|---|
| 68 | self.failUnlessIn("files whose names could not be converted", err) |
|---|
| 69 | else: |
|---|
| 70 | self.failUnlessReallyEqual(rc, 0) |
|---|
| 71 | self.failUnlessReallyEqual(out, u"Metallica\n\u00C4rtonwall\n\u00C4rtonwall-2\n") |
|---|
| 72 | self.assertEqual(len(err), 0, err) |
|---|
| 73 | d.addCallback(_check) |
|---|
| 74 | |
|---|
| 75 | return d |
|---|
| 76 | |
|---|
| 77 | def test_dangling_symlink_vs_recursion(self): |
|---|
| 78 | if not hasattr(os, 'symlink'): |
|---|
| 79 | raise unittest.SkipTest("Symlinks are not supported by Python on this platform.") |
|---|
| 80 | |
|---|
| 81 | # cp -r on a directory containing a dangling symlink shouldn't assert |
|---|
| 82 | self.basedir = "cli/Cp/dangling_symlink_vs_recursion" |
|---|
| 83 | self.set_up_grid(oneshare=True) |
|---|
| 84 | dn = os.path.join(self.basedir, "dir") |
|---|
| 85 | os.mkdir(dn) |
|---|
| 86 | fn = os.path.join(dn, "Fakebandica") |
|---|
| 87 | ln = os.path.join(dn, "link") |
|---|
| 88 | os.symlink(fn, ln) |
|---|
| 89 | |
|---|
| 90 | d = self.do_cli("create-alias", "tahoe") |
|---|
| 91 | d.addCallback(lambda res: self.do_cli("cp", "--recursive", |
|---|
| 92 | dn, "tahoe:")) |
|---|
| 93 | return d |
|---|
| 94 | |
|---|
| 95 | def test_copy_using_filecap(self): |
|---|
| 96 | self.basedir = "cli/Cp/test_copy_using_filecap" |
|---|
| 97 | self.set_up_grid(oneshare=True) |
|---|
| 98 | outdir = os.path.join(self.basedir, "outdir") |
|---|
| 99 | os.mkdir(outdir) |
|---|
| 100 | fn1 = os.path.join(self.basedir, "Metallica") |
|---|
| 101 | fn2 = os.path.join(outdir, "Not Metallica") |
|---|
| 102 | fn3 = os.path.join(outdir, "test2") |
|---|
| 103 | DATA1 = b"puppies" * 10000 |
|---|
| 104 | fileutil.write(fn1, DATA1) |
|---|
| 105 | |
|---|
| 106 | d = self.do_cli("create-alias", "tahoe") |
|---|
| 107 | d.addCallback(lambda ign: self.do_cli("put", fn1)) |
|---|
| 108 | def _put_file(args): |
|---|
| 109 | (rc, out, err) = args |
|---|
| 110 | self.failUnlessReallyEqual(rc, 0) |
|---|
| 111 | self.failUnlessIn("200 OK", err) |
|---|
| 112 | # keep track of the filecap |
|---|
| 113 | self.filecap = out.strip() |
|---|
| 114 | d.addCallback(_put_file) |
|---|
| 115 | |
|---|
| 116 | # Let's try copying this to the disk using the filecap. |
|---|
| 117 | d.addCallback(lambda ign: self.do_cli("cp", self.filecap, fn2)) |
|---|
| 118 | def _copy_file(args): |
|---|
| 119 | (rc, out, err) = args |
|---|
| 120 | self.failUnlessReallyEqual(rc, 0) |
|---|
| 121 | results = fileutil.read(fn2) |
|---|
| 122 | self.failUnlessReallyEqual(results, DATA1) |
|---|
| 123 | d.addCallback(_copy_file) |
|---|
| 124 | |
|---|
| 125 | # Test copying a filecap to local dir, which should fail without a |
|---|
| 126 | # destination filename (#761). |
|---|
| 127 | d.addCallback(lambda ign: self.do_cli("cp", self.filecap, outdir)) |
|---|
| 128 | def _resp(args): |
|---|
| 129 | (rc, out, err) = args |
|---|
| 130 | self.failUnlessReallyEqual(rc, 1) |
|---|
| 131 | self.failUnlessIn("when copying into a directory, all source files must have names, but", |
|---|
| 132 | err) |
|---|
| 133 | self.assertEqual(len(out), 0, out) |
|---|
| 134 | d.addCallback(_resp) |
|---|
| 135 | |
|---|
| 136 | # Create a directory, linked at tahoe:test . |
|---|
| 137 | d.addCallback(lambda ign: self.do_cli("mkdir", "tahoe:test")) |
|---|
| 138 | def _get_dir(args): |
|---|
| 139 | (rc, out, err) = args |
|---|
| 140 | self.failUnlessReallyEqual(rc, 0) |
|---|
| 141 | self.dircap = out.strip() |
|---|
| 142 | d.addCallback(_get_dir) |
|---|
| 143 | |
|---|
| 144 | # Upload a file to the directory. |
|---|
| 145 | d.addCallback(lambda ign: |
|---|
| 146 | self.do_cli("put", fn1, "tahoe:test/test_file")) |
|---|
| 147 | d.addCallback(lambda rc_out_err: self.failUnlessReallyEqual(rc_out_err[0], 0)) |
|---|
| 148 | |
|---|
| 149 | # Copying DIRCAP/filename to a local dir should work, because the |
|---|
| 150 | # destination filename can be inferred. |
|---|
| 151 | d.addCallback(lambda ign: |
|---|
| 152 | self.do_cli("cp", self.dircap + "/test_file", outdir)) |
|---|
| 153 | def _get_resp(args): |
|---|
| 154 | (rc, out, err) = args |
|---|
| 155 | self.failUnlessReallyEqual(rc, 0) |
|---|
| 156 | results = fileutil.read(os.path.join(outdir, "test_file")) |
|---|
| 157 | self.failUnlessReallyEqual(results, DATA1) |
|---|
| 158 | d.addCallback(_get_resp) |
|---|
| 159 | |
|---|
| 160 | # ... and to an explicit filename different from the source filename. |
|---|
| 161 | d.addCallback(lambda ign: |
|---|
| 162 | self.do_cli("cp", self.dircap + "/test_file", fn3)) |
|---|
| 163 | def _get_resp2(args): |
|---|
| 164 | (rc, out, err) = args |
|---|
| 165 | self.failUnlessReallyEqual(rc, 0) |
|---|
| 166 | results = fileutil.read(fn3) |
|---|
| 167 | self.failUnlessReallyEqual(results, DATA1) |
|---|
| 168 | d.addCallback(_get_resp2) |
|---|
| 169 | |
|---|
| 170 | # Test that the --verbose option prints correct indices (#1805). |
|---|
| 171 | d.addCallback(lambda ign: |
|---|
| 172 | self.do_cli("cp", "--verbose", fn3, self.dircap)) |
|---|
| 173 | def _test_for_wrong_indices(args): |
|---|
| 174 | (rc, out, err) = args |
|---|
| 175 | lines = err.split('\n') |
|---|
| 176 | self.failUnlessIn('examining 1 of 1', lines) |
|---|
| 177 | self.failUnlessIn('starting copy, 1 files, 1 directories', lines) |
|---|
| 178 | self.failIfIn('examining 0 of', err) |
|---|
| 179 | d.addCallback(_test_for_wrong_indices) |
|---|
| 180 | return d |
|---|
| 181 | |
|---|
| 182 | def test_cp_with_nonexistent_alias(self): |
|---|
| 183 | # when invoked with an alias or aliases that don't exist, 'tahoe cp' |
|---|
| 184 | # should output a sensible error message rather than a stack trace. |
|---|
| 185 | self.basedir = "cli/Cp/cp_with_nonexistent_alias" |
|---|
| 186 | self.set_up_grid(oneshare=True) |
|---|
| 187 | d = self.do_cli("cp", "fake:file1", "fake:file2") |
|---|
| 188 | def _check(args): |
|---|
| 189 | (rc, out, err) = args |
|---|
| 190 | self.failUnlessReallyEqual(rc, 1) |
|---|
| 191 | self.failUnlessIn("error:", err) |
|---|
| 192 | d.addCallback(_check) |
|---|
| 193 | # 'tahoe cp' actually processes the target argument first, so we need |
|---|
| 194 | # to check to make sure that validation extends to the source |
|---|
| 195 | # argument. |
|---|
| 196 | d.addCallback(lambda ign: self.do_cli("create-alias", "tahoe")) |
|---|
| 197 | d.addCallback(lambda ign: self.do_cli("cp", "fake:file1", |
|---|
| 198 | "tahoe:file2")) |
|---|
| 199 | d.addCallback(_check) |
|---|
| 200 | return d |
|---|
| 201 | |
|---|
| 202 | def test_unicode_dirnames(self): |
|---|
| 203 | self.basedir = "cli/Cp/unicode_dirnames" |
|---|
| 204 | |
|---|
| 205 | fn1 = os.path.join(self.basedir, u"\u00C4rtonwall") |
|---|
| 206 | artonwall_arg = u"\u00C4rtonwall" |
|---|
| 207 | |
|---|
| 208 | skip_if_cannot_represent_filename(fn1) |
|---|
| 209 | |
|---|
| 210 | self.set_up_grid(oneshare=True) |
|---|
| 211 | |
|---|
| 212 | d = self.do_cli("create-alias", "tahoe") |
|---|
| 213 | d.addCallback(lambda res: self.do_cli("mkdir", "tahoe:test/" + artonwall_arg)) |
|---|
| 214 | d.addCallback(lambda res: self.do_cli("cp", "-r", "tahoe:test", "tahoe:test2")) |
|---|
| 215 | d.addCallback(lambda res: self.do_cli("ls", "tahoe:test2/test")) |
|---|
| 216 | def _check(args): |
|---|
| 217 | (rc, out, err) = args |
|---|
| 218 | try: |
|---|
| 219 | unicode_to_output(u"\u00C4rtonwall") |
|---|
| 220 | except UnicodeEncodeError: |
|---|
| 221 | self.failUnlessReallyEqual(rc, 1) |
|---|
| 222 | self.assertEqual(len(out), 0, out) |
|---|
| 223 | self.failUnlessIn(quote_output(u"\u00C4rtonwall"), err) |
|---|
| 224 | self.failUnlessIn("files whose names could not be converted", err) |
|---|
| 225 | else: |
|---|
| 226 | self.failUnlessReallyEqual(rc, 0) |
|---|
| 227 | self.failUnlessReallyEqual(out, u"\u00C4rtonwall\n") |
|---|
| 228 | self.assertEqual(len(err), 0, err) |
|---|
| 229 | d.addCallback(_check) |
|---|
| 230 | |
|---|
| 231 | return d |
|---|
| 232 | |
|---|
| 233 | @defer.inlineCallbacks |
|---|
| 234 | def test_cp_duplicate_directories(self): |
|---|
| 235 | self.basedir = "cli/Cp/cp_duplicate_directories" |
|---|
| 236 | self.set_up_grid(oneshare=True) |
|---|
| 237 | |
|---|
| 238 | filename = os.path.join(self.basedir, "file") |
|---|
| 239 | data = b"abc\xff\x00\xee" |
|---|
| 240 | with open(filename, "wb") as f: |
|---|
| 241 | f.write(data) |
|---|
| 242 | |
|---|
| 243 | yield self.do_cli("create-alias", "tahoe") |
|---|
| 244 | (rc, out, err) = yield self.do_cli("mkdir", "tahoe:test1") |
|---|
| 245 | self.assertEqual(rc, 0, (rc, err)) |
|---|
| 246 | dircap = out.strip() |
|---|
| 247 | |
|---|
| 248 | (rc, out, err) = yield self.do_cli("cp", filename, "tahoe:test1/file") |
|---|
| 249 | self.assertEqual(rc, 0, (rc, err)) |
|---|
| 250 | |
|---|
| 251 | # Now duplicate dirnode, testing duplicates on destination side: |
|---|
| 252 | (rc, out, err) = yield self.do_cli( |
|---|
| 253 | "cp", "--recursive", dircap, "tahoe:test2/") |
|---|
| 254 | self.assertEqual(rc, 0, (rc, err)) |
|---|
| 255 | (rc, out, err) = yield self.do_cli( |
|---|
| 256 | "cp", "--recursive", dircap, "tahoe:test3/") |
|---|
| 257 | self.assertEqual(rc, 0, (rc, err)) |
|---|
| 258 | |
|---|
| 259 | # Now copy to local directory, testing duplicates on origin side: |
|---|
| 260 | yield self.do_cli("cp", "--recursive", "tahoe:", self.basedir) |
|---|
| 261 | |
|---|
| 262 | for i in range(1, 4): |
|---|
| 263 | with open(os.path.join(self.basedir, "test%d" % (i,), "file"), "rb") as f: |
|---|
| 264 | self.assertEqual(f.read(), data) |
|---|
| 265 | |
|---|
| 266 | @defer.inlineCallbacks |
|---|
| 267 | def test_cp_immutable_file(self): |
|---|
| 268 | self.basedir = "cli/Cp/cp_immutable_file" |
|---|
| 269 | self.set_up_grid(oneshare=True) |
|---|
| 270 | |
|---|
| 271 | filename = os.path.join(self.basedir, "source_file") |
|---|
| 272 | data = b"abc\xff\x00\xee" |
|---|
| 273 | with open(filename, "wb") as f: |
|---|
| 274 | f.write(data) |
|---|
| 275 | |
|---|
| 276 | # Create immutable file: |
|---|
| 277 | yield self.do_cli("create-alias", "tahoe") |
|---|
| 278 | (rc, out, _) = yield self.do_cli("put", filename, "tahoe:file1") |
|---|
| 279 | filecap = out.strip() |
|---|
| 280 | self.assertEqual(rc, 0) |
|---|
| 281 | |
|---|
| 282 | # Copy it: |
|---|
| 283 | (rc, _, _) = yield self.do_cli("cp", "tahoe:file1", "tahoe:file2") |
|---|
| 284 | self.assertEqual(rc, 0) |
|---|
| 285 | |
|---|
| 286 | # Make sure resulting file is the same: |
|---|
| 287 | (rc, _, _) = yield self.do_cli("cp", "--recursive", "--caps-only", |
|---|
| 288 | "tahoe:", self.basedir) |
|---|
| 289 | self.assertEqual(rc, 0) |
|---|
| 290 | with open(os.path.join(self.basedir, "file2")) as f: |
|---|
| 291 | self.assertEqual(f.read().strip(), filecap) |
|---|
| 292 | |
|---|
| 293 | def test_cp_replaces_mutable_file_contents(self): |
|---|
| 294 | self.basedir = "cli/Cp/cp_replaces_mutable_file_contents" |
|---|
| 295 | self.set_up_grid(oneshare=True) |
|---|
| 296 | |
|---|
| 297 | # Write a test file, which we'll copy to the grid. |
|---|
| 298 | test_txt_path = os.path.join(self.basedir, "test.txt") |
|---|
| 299 | test_txt_contents = "foo bar baz" |
|---|
| 300 | f = open(test_txt_path, "w") |
|---|
| 301 | f.write(test_txt_contents) |
|---|
| 302 | f.close() |
|---|
| 303 | |
|---|
| 304 | d = self.do_cli("create-alias", "tahoe") |
|---|
| 305 | d.addCallback(lambda ignored: |
|---|
| 306 | self.do_cli("mkdir", "tahoe:test")) |
|---|
| 307 | # We have to use 'tahoe put' here because 'tahoe cp' doesn't |
|---|
| 308 | # know how to make mutable files at the destination. |
|---|
| 309 | d.addCallback(lambda ignored: |
|---|
| 310 | self.do_cli("put", "--mutable", test_txt_path, "tahoe:test/test.txt")) |
|---|
| 311 | d.addCallback(lambda ignored: |
|---|
| 312 | self.do_cli("get", "tahoe:test/test.txt")) |
|---|
| 313 | def _check(args): |
|---|
| 314 | (rc, out, err) = args |
|---|
| 315 | self.failUnlessEqual(rc, 0) |
|---|
| 316 | self.failUnlessEqual(out, test_txt_contents) |
|---|
| 317 | d.addCallback(_check) |
|---|
| 318 | |
|---|
| 319 | # We'll do ls --json to get the read uri and write uri for the |
|---|
| 320 | # file we've just uploaded. |
|---|
| 321 | d.addCallback(lambda ignored: |
|---|
| 322 | self.do_cli("ls", "--json", "tahoe:test/test.txt")) |
|---|
| 323 | def _get_test_txt_uris(args): |
|---|
| 324 | (rc, out, err) = args |
|---|
| 325 | self.failUnlessEqual(rc, 0) |
|---|
| 326 | filetype, data = json.loads(out) |
|---|
| 327 | |
|---|
| 328 | self.failUnlessEqual(filetype, "filenode") |
|---|
| 329 | self.failUnless(data['mutable']) |
|---|
| 330 | |
|---|
| 331 | self.failUnlessIn("rw_uri", data) |
|---|
| 332 | self.rw_uri = to_bytes(data["rw_uri"]) |
|---|
| 333 | self.failUnlessIn("ro_uri", data) |
|---|
| 334 | self.ro_uri = to_bytes(data["ro_uri"]) |
|---|
| 335 | d.addCallback(_get_test_txt_uris) |
|---|
| 336 | |
|---|
| 337 | # Now make a new file to copy in place of test.txt. |
|---|
| 338 | new_txt_path = os.path.join(self.basedir, "new.txt") |
|---|
| 339 | new_txt_contents = "baz bar foo" * 100000 |
|---|
| 340 | f = open(new_txt_path, "w") |
|---|
| 341 | f.write(new_txt_contents) |
|---|
| 342 | f.close() |
|---|
| 343 | |
|---|
| 344 | # Copy the new file on top of the old file. |
|---|
| 345 | d.addCallback(lambda ignored: |
|---|
| 346 | self.do_cli("cp", new_txt_path, "tahoe:test/test.txt")) |
|---|
| 347 | |
|---|
| 348 | # If we get test.txt now, we should see the new data. |
|---|
| 349 | d.addCallback(lambda ignored: |
|---|
| 350 | self.do_cli("get", "tahoe:test/test.txt")) |
|---|
| 351 | d.addCallback(lambda rc_out_err: |
|---|
| 352 | self.failUnlessEqual(rc_out_err[1], new_txt_contents)) |
|---|
| 353 | # If we get the json of the new file, we should see that the old |
|---|
| 354 | # uri is there |
|---|
| 355 | d.addCallback(lambda ignored: |
|---|
| 356 | self.do_cli("ls", "--json", "tahoe:test/test.txt")) |
|---|
| 357 | def _check_json(args): |
|---|
| 358 | (rc, out, err) = args |
|---|
| 359 | self.failUnlessEqual(rc, 0) |
|---|
| 360 | filetype, data = json.loads(out) |
|---|
| 361 | |
|---|
| 362 | self.failUnlessEqual(filetype, "filenode") |
|---|
| 363 | self.failUnless(data['mutable']) |
|---|
| 364 | |
|---|
| 365 | self.failUnlessIn("ro_uri", data) |
|---|
| 366 | self.failUnlessEqual(to_bytes(data["ro_uri"]), self.ro_uri) |
|---|
| 367 | self.failUnlessIn("rw_uri", data) |
|---|
| 368 | self.failUnlessEqual(to_bytes(data["rw_uri"]), self.rw_uri) |
|---|
| 369 | d.addCallback(_check_json) |
|---|
| 370 | |
|---|
| 371 | # and, finally, doing a GET directly on one of the old uris |
|---|
| 372 | # should give us the new contents. |
|---|
| 373 | d.addCallback(lambda ignored: |
|---|
| 374 | self.do_cli("get", self.rw_uri)) |
|---|
| 375 | d.addCallback(lambda rc_out_err: |
|---|
| 376 | self.failUnlessEqual(rc_out_err[1], new_txt_contents)) |
|---|
| 377 | # Now copy the old test.txt without an explicit destination |
|---|
| 378 | # file. tahoe cp will match it to the existing file and |
|---|
| 379 | # overwrite it appropriately. |
|---|
| 380 | d.addCallback(lambda ignored: |
|---|
| 381 | self.do_cli("cp", test_txt_path, "tahoe:test")) |
|---|
| 382 | d.addCallback(lambda ignored: |
|---|
| 383 | self.do_cli("get", "tahoe:test/test.txt")) |
|---|
| 384 | d.addCallback(lambda rc_out_err: |
|---|
| 385 | self.failUnlessEqual(rc_out_err[1], test_txt_contents)) |
|---|
| 386 | d.addCallback(lambda ignored: |
|---|
| 387 | self.do_cli("ls", "--json", "tahoe:test/test.txt")) |
|---|
| 388 | d.addCallback(_check_json) |
|---|
| 389 | d.addCallback(lambda ignored: |
|---|
| 390 | self.do_cli("get", self.rw_uri)) |
|---|
| 391 | d.addCallback(lambda rc_out_err: |
|---|
| 392 | self.failUnlessEqual(rc_out_err[1], test_txt_contents)) |
|---|
| 393 | |
|---|
| 394 | # Now we'll make a more complicated directory structure. |
|---|
| 395 | # test2/ |
|---|
| 396 | # test2/mutable1 |
|---|
| 397 | # test2/mutable2 |
|---|
| 398 | # test2/imm1 |
|---|
| 399 | # test2/imm2 |
|---|
| 400 | imm_test_txt_path = os.path.join(self.basedir, "imm_test.txt") |
|---|
| 401 | imm_test_txt_contents = test_txt_contents * 10000 |
|---|
| 402 | fileutil.write(imm_test_txt_path, imm_test_txt_contents) |
|---|
| 403 | d.addCallback(lambda ignored: |
|---|
| 404 | self.do_cli("mkdir", "tahoe:test2")) |
|---|
| 405 | d.addCallback(lambda ignored: |
|---|
| 406 | self.do_cli("put", "--mutable", new_txt_path, |
|---|
| 407 | "tahoe:test2/mutable1")) |
|---|
| 408 | d.addCallback(lambda ignored: |
|---|
| 409 | self.do_cli("put", "--mutable", new_txt_path, |
|---|
| 410 | "tahoe:test2/mutable2")) |
|---|
| 411 | d.addCallback(lambda ignored: |
|---|
| 412 | self.do_cli('put', new_txt_path, "tahoe:test2/imm1")) |
|---|
| 413 | d.addCallback(lambda ignored: |
|---|
| 414 | self.do_cli("put", imm_test_txt_path, "tahoe:test2/imm2")) |
|---|
| 415 | d.addCallback(lambda ignored: |
|---|
| 416 | self.do_cli("ls", "--json", "tahoe:test2")) |
|---|
| 417 | def _process_directory_json(args): |
|---|
| 418 | (rc, out, err) = args |
|---|
| 419 | self.failUnlessEqual(rc, 0) |
|---|
| 420 | |
|---|
| 421 | filetype, data = json.loads(out) |
|---|
| 422 | self.failUnlessEqual(filetype, "dirnode") |
|---|
| 423 | self.failUnless(data['mutable']) |
|---|
| 424 | self.failUnlessIn("children", data) |
|---|
| 425 | children = data['children'] |
|---|
| 426 | |
|---|
| 427 | # Store the URIs for later use. |
|---|
| 428 | self.childuris = {} |
|---|
| 429 | for k in ["mutable1", "mutable2", "imm1", "imm2"]: |
|---|
| 430 | self.failUnlessIn(k, children) |
|---|
| 431 | childtype, childdata = children[k] |
|---|
| 432 | self.failUnlessEqual(childtype, "filenode") |
|---|
| 433 | if "mutable" in k: |
|---|
| 434 | self.failUnless(childdata['mutable']) |
|---|
| 435 | self.failUnlessIn("rw_uri", childdata) |
|---|
| 436 | uri_key = "rw_uri" |
|---|
| 437 | else: |
|---|
| 438 | self.failIf(childdata['mutable']) |
|---|
| 439 | self.failUnlessIn("ro_uri", childdata) |
|---|
| 440 | uri_key = "ro_uri" |
|---|
| 441 | self.childuris[k] = to_bytes(childdata[uri_key]) |
|---|
| 442 | d.addCallback(_process_directory_json) |
|---|
| 443 | # Now build a local directory to copy into place, like the following: |
|---|
| 444 | # test2/ |
|---|
| 445 | # test2/mutable1 |
|---|
| 446 | # test2/mutable2 |
|---|
| 447 | # test2/imm1 |
|---|
| 448 | # test2/imm3 |
|---|
| 449 | def _build_local_directory(ignored): |
|---|
| 450 | test2_path = os.path.join(self.basedir, "test2") |
|---|
| 451 | fileutil.make_dirs(test2_path) |
|---|
| 452 | for fn in ("mutable1", "mutable2", "imm1", "imm3"): |
|---|
| 453 | fileutil.write(os.path.join(test2_path, fn), fn * 1000) |
|---|
| 454 | self.test2_path = test2_path |
|---|
| 455 | d.addCallback(_build_local_directory) |
|---|
| 456 | d.addCallback(lambda ignored: |
|---|
| 457 | self.do_cli("cp", "-r", self.test2_path, "tahoe:")) |
|---|
| 458 | |
|---|
| 459 | # We expect that mutable1 and mutable2 are overwritten in-place, |
|---|
| 460 | # so they'll retain their URIs but have different content. |
|---|
| 461 | def _process_file_json(args, fn): |
|---|
| 462 | (rc, out, err) = args |
|---|
| 463 | self.failUnlessEqual(rc, 0) |
|---|
| 464 | filetype, data = json.loads(out) |
|---|
| 465 | self.failUnlessEqual(filetype, "filenode") |
|---|
| 466 | |
|---|
| 467 | if "mutable" in fn: |
|---|
| 468 | self.failUnless(data['mutable']) |
|---|
| 469 | self.failUnlessIn("rw_uri", data) |
|---|
| 470 | self.failUnlessEqual(to_bytes(data["rw_uri"]), self.childuris[fn]) |
|---|
| 471 | else: |
|---|
| 472 | self.failIf(data['mutable']) |
|---|
| 473 | self.failUnlessIn("ro_uri", data) |
|---|
| 474 | self.failIfEqual(to_bytes(data["ro_uri"]), self.childuris[fn]) |
|---|
| 475 | |
|---|
| 476 | for fn in ("mutable1", "mutable2"): |
|---|
| 477 | d.addCallback(lambda ignored, fn=fn: |
|---|
| 478 | self.do_cli("get", "tahoe:test2/%s" % fn)) |
|---|
| 479 | d.addCallback(lambda rc_out_err, fn=fn: |
|---|
| 480 | self.failUnlessEqual(rc_out_err[1], fn * 1000)) |
|---|
| 481 | d.addCallback(lambda ignored, fn=fn: |
|---|
| 482 | self.do_cli("ls", "--json", "tahoe:test2/%s" % fn)) |
|---|
| 483 | d.addCallback(_process_file_json, fn=fn) |
|---|
| 484 | |
|---|
| 485 | # imm1 should have been replaced, so both its uri and content |
|---|
| 486 | # should be different. |
|---|
| 487 | d.addCallback(lambda ignored: |
|---|
| 488 | self.do_cli("get", "tahoe:test2/imm1")) |
|---|
| 489 | d.addCallback(lambda rc_out_err: |
|---|
| 490 | self.failUnlessEqual(rc_out_err[1], "imm1" * 1000)) |
|---|
| 491 | d.addCallback(lambda ignored: |
|---|
| 492 | self.do_cli("ls", "--json", "tahoe:test2/imm1")) |
|---|
| 493 | d.addCallback(_process_file_json, fn="imm1") |
|---|
| 494 | |
|---|
| 495 | # imm3 should have been created. |
|---|
| 496 | d.addCallback(lambda ignored: |
|---|
| 497 | self.do_cli("get", "tahoe:test2/imm3")) |
|---|
| 498 | d.addCallback(lambda rc_out_err: |
|---|
| 499 | self.failUnlessEqual(rc_out_err[1], "imm3" * 1000)) |
|---|
| 500 | |
|---|
| 501 | # imm2 should be exactly as we left it, since our newly-copied |
|---|
| 502 | # directory didn't contain an imm2 entry. |
|---|
| 503 | d.addCallback(lambda ignored: |
|---|
| 504 | self.do_cli("get", "tahoe:test2/imm2")) |
|---|
| 505 | d.addCallback(lambda rc_out_err: |
|---|
| 506 | self.failUnlessEqual(rc_out_err[1], imm_test_txt_contents)) |
|---|
| 507 | d.addCallback(lambda ignored: |
|---|
| 508 | self.do_cli("ls", "--json", "tahoe:test2/imm2")) |
|---|
| 509 | def _process_imm2_json(args): |
|---|
| 510 | (rc, out, err) = args |
|---|
| 511 | self.failUnlessEqual(rc, 0) |
|---|
| 512 | filetype, data = json.loads(out) |
|---|
| 513 | self.failUnlessEqual(filetype, "filenode") |
|---|
| 514 | self.failIf(data['mutable']) |
|---|
| 515 | self.failUnlessIn("ro_uri", data) |
|---|
| 516 | self.failUnlessEqual(to_bytes(data["ro_uri"]), self.childuris["imm2"]) |
|---|
| 517 | d.addCallback(_process_imm2_json) |
|---|
| 518 | return d |
|---|
| 519 | |
|---|
| 520 | def test_cp_overwrite_readonly_mutable_file(self): |
|---|
| 521 | # tahoe cp should print an error when asked to overwrite a |
|---|
| 522 | # mutable file that it can't overwrite. |
|---|
| 523 | self.basedir = "cli/Cp/overwrite_readonly_mutable_file" |
|---|
| 524 | self.set_up_grid(oneshare=True) |
|---|
| 525 | |
|---|
| 526 | # This is our initial file. We'll link its readcap into the |
|---|
| 527 | # tahoe: alias. |
|---|
| 528 | test_file_path = os.path.join(self.basedir, "test_file.txt") |
|---|
| 529 | test_file_contents = "This is a test file." |
|---|
| 530 | fileutil.write(test_file_path, test_file_contents) |
|---|
| 531 | |
|---|
| 532 | # This is our replacement file. We'll try and fail to upload it |
|---|
| 533 | # over the readcap that we linked into the tahoe: alias. |
|---|
| 534 | replacement_file_path = os.path.join(self.basedir, "replacement.txt") |
|---|
| 535 | replacement_file_contents = "These are new contents." |
|---|
| 536 | fileutil.write(replacement_file_path, replacement_file_contents) |
|---|
| 537 | |
|---|
| 538 | d = self.do_cli("create-alias", "tahoe:") |
|---|
| 539 | d.addCallback(lambda ignored: |
|---|
| 540 | self.do_cli("put", "--mutable", test_file_path)) |
|---|
| 541 | def _get_test_uri(args): |
|---|
| 542 | (rc, out, err) = args |
|---|
| 543 | self.failUnlessEqual(rc, 0) |
|---|
| 544 | # this should be a write uri |
|---|
| 545 | self._test_write_uri = out |
|---|
| 546 | d.addCallback(_get_test_uri) |
|---|
| 547 | d.addCallback(lambda ignored: |
|---|
| 548 | self.do_cli("ls", "--json", self._test_write_uri)) |
|---|
| 549 | def _process_test_json(args): |
|---|
| 550 | (rc, out, err) = args |
|---|
| 551 | self.failUnlessEqual(rc, 0) |
|---|
| 552 | filetype, data = json.loads(out) |
|---|
| 553 | |
|---|
| 554 | self.failUnlessEqual(filetype, "filenode") |
|---|
| 555 | self.failUnless(data['mutable']) |
|---|
| 556 | self.failUnlessIn("ro_uri", data) |
|---|
| 557 | self._test_read_uri = to_bytes(data["ro_uri"]) |
|---|
| 558 | d.addCallback(_process_test_json) |
|---|
| 559 | # Now we'll link the readonly URI into the tahoe: alias. |
|---|
| 560 | d.addCallback(lambda ignored: |
|---|
| 561 | self.do_cli("ln", self._test_read_uri, "tahoe:test_file.txt")) |
|---|
| 562 | d.addCallback(lambda rc_out_err: |
|---|
| 563 | self.failUnlessEqual(rc_out_err[0], 0)) |
|---|
| 564 | # Let's grab the json of that to make sure that we did it right. |
|---|
| 565 | d.addCallback(lambda ignored: |
|---|
| 566 | self.do_cli("ls", "--json", "tahoe:")) |
|---|
| 567 | def _process_tahoe_json(args): |
|---|
| 568 | (rc, out, err) = args |
|---|
| 569 | self.failUnlessEqual(rc, 0) |
|---|
| 570 | |
|---|
| 571 | filetype, data = json.loads(out) |
|---|
| 572 | self.failUnlessEqual(filetype, "dirnode") |
|---|
| 573 | self.failUnlessIn("children", data) |
|---|
| 574 | kiddata = data['children'] |
|---|
| 575 | |
|---|
| 576 | self.failUnlessIn("test_file.txt", kiddata) |
|---|
| 577 | testtype, testdata = kiddata['test_file.txt'] |
|---|
| 578 | self.failUnlessEqual(testtype, "filenode") |
|---|
| 579 | self.failUnless(testdata['mutable']) |
|---|
| 580 | self.failUnlessIn("ro_uri", testdata) |
|---|
| 581 | self.failUnlessEqual(to_bytes(testdata["ro_uri"]), self._test_read_uri) |
|---|
| 582 | self.failIfIn("rw_uri", testdata) |
|---|
| 583 | d.addCallback(_process_tahoe_json) |
|---|
| 584 | # Okay, now we're going to try uploading another mutable file in |
|---|
| 585 | # place of that one. We should get an error. |
|---|
| 586 | d.addCallback(lambda ignored: |
|---|
| 587 | self.do_cli("cp", replacement_file_path, "tahoe:test_file.txt")) |
|---|
| 588 | def _check_error_message(args): |
|---|
| 589 | (rc, out, err) = args |
|---|
| 590 | self.failUnlessEqual(rc, 1) |
|---|
| 591 | self.failUnlessIn("replace or update requested with read-only cap", err) |
|---|
| 592 | d.addCallback(_check_error_message) |
|---|
| 593 | # Make extra sure that that didn't work. |
|---|
| 594 | d.addCallback(lambda ignored: |
|---|
| 595 | self.do_cli("get", "tahoe:test_file.txt")) |
|---|
| 596 | d.addCallback(lambda rc_out_err: |
|---|
| 597 | self.failUnlessEqual(rc_out_err[1], test_file_contents)) |
|---|
| 598 | d.addCallback(lambda ignored: |
|---|
| 599 | self.do_cli("get", self._test_read_uri)) |
|---|
| 600 | d.addCallback(lambda rc_out_err: |
|---|
| 601 | self.failUnlessEqual(rc_out_err[1], test_file_contents)) |
|---|
| 602 | # Now we'll do it without an explicit destination. |
|---|
| 603 | d.addCallback(lambda ignored: |
|---|
| 604 | self.do_cli("cp", test_file_path, "tahoe:")) |
|---|
| 605 | d.addCallback(_check_error_message) |
|---|
| 606 | d.addCallback(lambda ignored: |
|---|
| 607 | self.do_cli("get", "tahoe:test_file.txt")) |
|---|
| 608 | d.addCallback(lambda rc_out_err: |
|---|
| 609 | self.failUnlessEqual(rc_out_err[1], test_file_contents)) |
|---|
| 610 | d.addCallback(lambda ignored: |
|---|
| 611 | self.do_cli("get", self._test_read_uri)) |
|---|
| 612 | d.addCallback(lambda rc_out_err: |
|---|
| 613 | self.failUnlessEqual(rc_out_err[1], test_file_contents)) |
|---|
| 614 | # Now we'll link a readonly file into a subdirectory. |
|---|
| 615 | d.addCallback(lambda ignored: |
|---|
| 616 | self.do_cli("mkdir", "tahoe:testdir")) |
|---|
| 617 | d.addCallback(lambda rc_out_err: |
|---|
| 618 | self.failUnlessEqual(rc_out_err[0], 0)) |
|---|
| 619 | d.addCallback(lambda ignored: |
|---|
| 620 | self.do_cli("ln", self._test_read_uri, "tahoe:test/file2.txt")) |
|---|
| 621 | d.addCallback(lambda rc_out_err: |
|---|
| 622 | self.failUnlessEqual(rc_out_err[0], 0)) |
|---|
| 623 | |
|---|
| 624 | test_dir_path = os.path.join(self.basedir, "test") |
|---|
| 625 | fileutil.make_dirs(test_dir_path) |
|---|
| 626 | for f in ("file1.txt", "file2.txt"): |
|---|
| 627 | fileutil.write(os.path.join(test_dir_path, f), f * 10000) |
|---|
| 628 | |
|---|
| 629 | d.addCallback(lambda ignored: |
|---|
| 630 | self.do_cli("cp", "-r", test_dir_path, "tahoe:")) |
|---|
| 631 | d.addCallback(_check_error_message) |
|---|
| 632 | d.addCallback(lambda ignored: |
|---|
| 633 | self.do_cli("ls", "--json", "tahoe:test")) |
|---|
| 634 | def _got_testdir_json(args): |
|---|
| 635 | (rc, out, err) = args |
|---|
| 636 | self.failUnlessEqual(rc, 0) |
|---|
| 637 | |
|---|
| 638 | filetype, data = json.loads(out) |
|---|
| 639 | self.failUnlessEqual(filetype, "dirnode") |
|---|
| 640 | |
|---|
| 641 | self.failUnlessIn("children", data) |
|---|
| 642 | childdata = data['children'] |
|---|
| 643 | |
|---|
| 644 | self.failUnlessIn("file2.txt", childdata) |
|---|
| 645 | file2type, file2data = childdata['file2.txt'] |
|---|
| 646 | self.failUnlessEqual(file2type, "filenode") |
|---|
| 647 | self.failUnless(file2data['mutable']) |
|---|
| 648 | self.failUnlessIn("ro_uri", file2data) |
|---|
| 649 | self.failUnlessEqual(to_bytes(file2data["ro_uri"]), self._test_read_uri) |
|---|
| 650 | self.failIfIn("rw_uri", file2data) |
|---|
| 651 | d.addCallback(_got_testdir_json) |
|---|
| 652 | return d |
|---|
| 653 | |
|---|
| 654 | def test_cp_verbose(self): |
|---|
| 655 | self.basedir = "cli/Cp/cp_verbose" |
|---|
| 656 | self.set_up_grid(oneshare=True) |
|---|
| 657 | |
|---|
| 658 | # Write two test files, which we'll copy to the grid. |
|---|
| 659 | test1_path = os.path.join(self.basedir, "test1") |
|---|
| 660 | test2_path = os.path.join(self.basedir, "test2") |
|---|
| 661 | fileutil.write(test1_path, "test1") |
|---|
| 662 | fileutil.write(test2_path, "test2") |
|---|
| 663 | |
|---|
| 664 | d = self.do_cli("create-alias", "tahoe") |
|---|
| 665 | d.addCallback(lambda ign: |
|---|
| 666 | self.do_cli("cp", "--verbose", test1_path, test2_path, "tahoe:")) |
|---|
| 667 | def _check(res): |
|---|
| 668 | (rc, out, err) = res |
|---|
| 669 | self.failUnlessEqual(rc, 0, str(res)) |
|---|
| 670 | self.failUnlessIn("Success: files copied", out, str(res)) |
|---|
| 671 | self.failUnlessEqual(err, """\ |
|---|
| 672 | attaching sources to targets, 2 files / 0 dirs in root |
|---|
| 673 | targets assigned, 1 dirs, 2 files |
|---|
| 674 | starting copy, 2 files, 1 directories |
|---|
| 675 | 1/2 files, 0/1 directories |
|---|
| 676 | 2/2 files, 0/1 directories |
|---|
| 677 | 1/1 directories |
|---|
| 678 | """, str(res)) |
|---|
| 679 | d.addCallback(_check) |
|---|
| 680 | return d |
|---|
| 681 | |
|---|
| 682 | def test_cp_copies_dir(self): |
|---|
| 683 | # This test ensures that a directory is copied using |
|---|
| 684 | # tahoe cp -r. Refer to ticket #712: |
|---|
| 685 | # https://tahoe-lafs.org/trac/tahoe-lafs/ticket/712 |
|---|
| 686 | |
|---|
| 687 | self.basedir = "cli/Cp/cp_copies_dir" |
|---|
| 688 | self.set_up_grid(oneshare=True) |
|---|
| 689 | subdir = os.path.join(self.basedir, "foo") |
|---|
| 690 | os.mkdir(subdir) |
|---|
| 691 | test1_path = os.path.join(subdir, "test1") |
|---|
| 692 | fileutil.write(test1_path, "test1") |
|---|
| 693 | |
|---|
| 694 | d = self.do_cli("create-alias", "tahoe") |
|---|
| 695 | d.addCallback(lambda ign: |
|---|
| 696 | self.do_cli("cp", "-r", subdir, "tahoe:")) |
|---|
| 697 | d.addCallback(lambda ign: |
|---|
| 698 | self.do_cli("ls", "tahoe:")) |
|---|
| 699 | def _check(res, item): |
|---|
| 700 | (rc, out, err) = res |
|---|
| 701 | self.failUnlessEqual(rc, 0) |
|---|
| 702 | self.failUnlessEqual(err, "") |
|---|
| 703 | self.failUnlessIn(item, out, str(res)) |
|---|
| 704 | d.addCallback(_check, "foo") |
|---|
| 705 | d.addCallback(lambda ign: |
|---|
| 706 | self.do_cli("ls", "tahoe:foo/")) |
|---|
| 707 | d.addCallback(_check, "test1") |
|---|
| 708 | |
|---|
| 709 | d.addCallback(lambda ign: fileutil.rm_dir(subdir)) |
|---|
| 710 | d.addCallback(lambda ign: self.do_cli("cp", "-r", "tahoe:foo", self.basedir)) |
|---|
| 711 | def _check_local_fs(ign): |
|---|
| 712 | self.failUnless(os.path.isdir(self.basedir)) |
|---|
| 713 | self.failUnless(os.path.isfile(test1_path)) |
|---|
| 714 | d.addCallback(_check_local_fs) |
|---|
| 715 | return d |
|---|
| 716 | |
|---|
| 717 | def test_ticket_2027(self): |
|---|
| 718 | # This test ensures that tahoe will copy a file from the grid to |
|---|
| 719 | # a local directory without a specified file name. |
|---|
| 720 | # https://tahoe-lafs.org/trac/tahoe-lafs/ticket/2027 |
|---|
| 721 | self.basedir = "cli/Cp/ticket_2027" |
|---|
| 722 | self.set_up_grid(oneshare=True) |
|---|
| 723 | |
|---|
| 724 | # Write a test file, which we'll copy to the grid. |
|---|
| 725 | test1_path = os.path.join(self.basedir, "test1") |
|---|
| 726 | fileutil.write(test1_path, "test1") |
|---|
| 727 | |
|---|
| 728 | d = self.do_cli("create-alias", "tahoe") |
|---|
| 729 | d.addCallback(lambda ign: |
|---|
| 730 | self.do_cli("cp", test1_path, "tahoe:")) |
|---|
| 731 | d.addCallback(lambda ign: |
|---|
| 732 | self.do_cli("cp", "tahoe:test1", self.basedir)) |
|---|
| 733 | def _check(res): |
|---|
| 734 | (rc, out, err) = res |
|---|
| 735 | self.failUnlessIn("Success: file copied", out, str(res)) |
|---|
| 736 | return d |
|---|
| 737 | |
|---|
| 738 | # these test cases come from ticket #2329 comment 40 |
|---|
| 739 | # trailing slash on target *directory* should not matter, test both |
|---|
| 740 | # trailing slash on target files should cause error |
|---|
| 741 | # trailing slash on source directory should not matter, test a few |
|---|
| 742 | # trailing slash on source files should cause error |
|---|
| 743 | |
|---|
| 744 | COPYOUT_TESTCASES = """ |
|---|
| 745 | cp $FILECAP to/existing-file : to/existing-file |
|---|
| 746 | cp -r $FILECAP to/existing-file : to/existing-file |
|---|
| 747 | cp $DIRCAP/file $PARENTCAP/dir2/file2 to/existing-file : E6-MANYONE |
|---|
| 748 | cp -r $DIRCAP/file $PARENTCAP/dir2/file2 to/existing-file : E6-MANYONE |
|---|
| 749 | cp $DIRCAP to/existing-file : E4-NEED-R |
|---|
| 750 | cp -r $DIRCAP to/existing-file : E5-DIRTOFILE |
|---|
| 751 | cp $FILECAP $DIRCAP to/existing-file : E4-NEED-R |
|---|
| 752 | cp -r $FILECAP $DIRCAP to/existing-file : E6-MANYONE |
|---|
| 753 | |
|---|
| 754 | cp $FILECAP to/existing-file/ : E7-BADSLASH |
|---|
| 755 | cp -r $FILECAP to/existing-file/ : E7-BADSLASH |
|---|
| 756 | cp $DIRCAP/file $PARENTCAP/dir2/file2 to/existing-file/ : E7-BADSLASH |
|---|
| 757 | cp -r $DIRCAP/file $PARENTCAP/dir2/file2 to/existing-file/ : E7-BADSLASH |
|---|
| 758 | cp $DIRCAP to/existing-file/ : E4-NEED-R |
|---|
| 759 | cp -r $DIRCAP to/existing-file/ : E7-BADSLASH |
|---|
| 760 | cp $FILECAP $DIRCAP to/existing-file/ : E4-NEED-R |
|---|
| 761 | cp -r $FILECAP $DIRCAP to/existing-file/ : E7-BADSLASH |
|---|
| 762 | |
|---|
| 763 | # single source to a (present) target directory |
|---|
| 764 | cp $FILECAP to : E2-DESTNAME |
|---|
| 765 | cp -r $FILECAP to : E2-DESTNAME |
|---|
| 766 | cp $DIRCAP/file to : to/file |
|---|
| 767 | cp -r $DIRCAP/file to : to/file |
|---|
| 768 | # these two are errors |
|---|
| 769 | cp $DIRCAP/file/ to : E8-BADSLASH |
|---|
| 770 | cp -r $DIRCAP/file/ to : E8-BADSLASH |
|---|
| 771 | cp $PARENTCAP/dir to : E4-NEED-R |
|---|
| 772 | cp -r $PARENTCAP/dir to : to/dir/file |
|---|
| 773 | # but these two should ignore the trailing source slash |
|---|
| 774 | cp $PARENTCAP/dir/ to : E4-NEED-R |
|---|
| 775 | cp -r $PARENTCAP/dir/ to : to/dir/file |
|---|
| 776 | cp $DIRCAP to : E4-NEED-R |
|---|
| 777 | cp -r $DIRCAP to : to/file |
|---|
| 778 | cp $DIRALIAS to : E4-NEED-R |
|---|
| 779 | cp -r $DIRALIAS to : to/file |
|---|
| 780 | |
|---|
| 781 | cp $FILECAP to/ : E2-DESTNAME |
|---|
| 782 | cp -r $FILECAP to/ : E2-DESTNAME |
|---|
| 783 | cp $DIRCAP/file to/ : to/file |
|---|
| 784 | cp -r $DIRCAP/file to/ : to/file |
|---|
| 785 | cp $PARENTCAP/dir to/ : E4-NEED-R |
|---|
| 786 | cp -r $PARENTCAP/dir to/ : to/dir/file |
|---|
| 787 | cp $DIRCAP to/ : E4-NEED-R |
|---|
| 788 | cp -r $DIRCAP to/ : to/file |
|---|
| 789 | cp $DIRALIAS to/ : E4-NEED-R |
|---|
| 790 | cp -r $DIRALIAS to/ : to/file |
|---|
| 791 | |
|---|
| 792 | # multiple sources to a (present) target directory |
|---|
| 793 | cp $DIRCAP/file $PARENTCAP/dir2/file2 to : to/file,to/file2 |
|---|
| 794 | cp $DIRCAP/file $FILECAP to : E2-DESTNAME |
|---|
| 795 | cp $DIRCAP $FILECAP to : E4-NEED-R |
|---|
| 796 | cp -r $DIRCAP $FILECAP to : E2-DESTNAME |
|---|
| 797 | # namedfile, unnameddir, nameddir |
|---|
| 798 | cp $PARENTCAP/dir3/file3 $DIRCAP $PARENTCAP/dir2 to : E4-NEED-R |
|---|
| 799 | cp -r $PARENTCAP/dir3/file3 $DIRCAP $PARENTCAP/dir2 to : to/file3,to/file,to/dir2/file2 |
|---|
| 800 | # namedfile, unnameddir, nameddir, unnamedfile |
|---|
| 801 | cp $PARENTCAP/dir3/file3 $DIRCAP $PARENTCAP/dir2 $FILECAP to : E4-NEED-R |
|---|
| 802 | cp -r $PARENTCAP/dir3/file3 $DIRCAP $PARENTCAP/dir2 $FILECAP to : E2-DESTNAME |
|---|
| 803 | |
|---|
| 804 | cp $DIRCAP/file $PARENTCAP/dir2/file2 to/ : to/file,to/file2 |
|---|
| 805 | cp $DIRCAP/file $FILECAP to/ : E2-DESTNAME |
|---|
| 806 | cp $DIRCAP $FILECAP to/ : E4-NEED-R |
|---|
| 807 | cp -r $DIRCAP $FILECAP to/ : E2-DESTNAME |
|---|
| 808 | # namedfile, unnameddir, nameddir |
|---|
| 809 | cp $PARENTCAP/dir3/file3 $DIRCAP $PARENTCAP/dir2 to/ : E4-NEED-R |
|---|
| 810 | cp -r $PARENTCAP/dir3/file3 $DIRCAP $PARENTCAP/dir2 to/ : to/file3,to/file,to/dir2/file2 |
|---|
| 811 | # namedfile, unnameddir, nameddir, unnamedfile |
|---|
| 812 | cp $PARENTCAP/dir3/file3 $DIRCAP $PARENTCAP/dir2 $FILECAP to/ : E4-NEED-R |
|---|
| 813 | cp -r $PARENTCAP/dir3/file3 $DIRCAP $PARENTCAP/dir2 $FILECAP to/ : E2-DESTNAME |
|---|
| 814 | |
|---|
| 815 | # single sources to a missing target: should mkdir or create a file |
|---|
| 816 | cp $FILECAP to/missing : to/missing |
|---|
| 817 | cp -r $FILECAP to/missing : to/missing |
|---|
| 818 | cp $DIRCAP/file to/missing : to/missing |
|---|
| 819 | cp -r $DIRCAP/file to/missing : to/missing |
|---|
| 820 | cp $PARENTCAP/dir to/missing : E4-NEED-R |
|---|
| 821 | cp -r $PARENTCAP/dir to/missing : to/missing/dir/file |
|---|
| 822 | cp $DIRCAP to/missing : E4-NEED-R |
|---|
| 823 | cp -r $DIRCAP to/missing : to/missing/file |
|---|
| 824 | cp $DIRALIAS to/missing : E4-NEED-R |
|---|
| 825 | cp -r $DIRALIAS to/missing : to/missing/file |
|---|
| 826 | |
|---|
| 827 | cp $FILECAP to/missing/ : E7-BADSLASH |
|---|
| 828 | cp -r $FILECAP to/missing/ : E7-BADSLASH |
|---|
| 829 | cp $DIRCAP/file to/missing/ : E7-BADSLASH |
|---|
| 830 | cp -r $DIRCAP/file to/missing/ : E7-BADSLASH |
|---|
| 831 | cp $PARENTCAP/dir to/missing/ : E4-NEED-R |
|---|
| 832 | cp -r $PARENTCAP/dir to/missing/ : to/missing/dir/file |
|---|
| 833 | cp $DIRCAP to/missing/ : E4-NEED-R |
|---|
| 834 | cp -r $DIRCAP to/missing/ : to/missing/file |
|---|
| 835 | cp $DIRALIAS to/missing/ : E4-NEED-R |
|---|
| 836 | cp -r $DIRALIAS to/missing/ : to/missing/file |
|---|
| 837 | |
|---|
| 838 | # multiple things to a missing target: should mkdir |
|---|
| 839 | cp $DIRCAP/file $PARENTCAP/dir2/file2 to/missing : to/missing/file,to/missing/file2 |
|---|
| 840 | cp -r $DIRCAP/file $PARENTCAP/dir2/file2 to/missing : to/missing/file,to/missing/file2 |
|---|
| 841 | cp $DIRCAP/file $FILECAP to/missing : E2-DESTNAME |
|---|
| 842 | cp -r $DIRCAP/file $FILECAP to/missing : E2-DESTNAME |
|---|
| 843 | cp $DIRCAP $FILECAP to/missing : E4-NEED-R |
|---|
| 844 | cp -r $DIRCAP $FILECAP to/missing : E2-DESTNAME |
|---|
| 845 | # namedfile, unnameddir, nameddir |
|---|
| 846 | cp $PARENTCAP/dir3/file3 $DIRCAP $PARENTCAP/dir2 to/missing : E4-NEED-R |
|---|
| 847 | cp -r $PARENTCAP/dir3/file3 $DIRCAP $PARENTCAP/dir2 to/missing : to/missing/file3,to/missing/file,to/missing/dir2/file2 |
|---|
| 848 | # namedfile, unnameddir, nameddir, unnamedfile |
|---|
| 849 | cp $PARENTCAP/dir3/file3 $DIRCAP $PARENTCAP/dir2 $FILECAP to/missing : E4-NEED-R |
|---|
| 850 | cp -r $PARENTCAP/dir3/file3 $DIRCAP $PARENTCAP/dir2 $FILECAP to/missing : E2-DESTNAME |
|---|
| 851 | |
|---|
| 852 | cp $DIRCAP/file $PARENTCAP/dir2/file2 to/missing/ : to/missing/file,to/missing/file2 |
|---|
| 853 | cp -r $DIRCAP/file $PARENTCAP/dir2/file2 to/missing/ : to/missing/file,to/missing/file2 |
|---|
| 854 | cp $DIRCAP/file $FILECAP to/missing/ : E2-DESTNAME |
|---|
| 855 | cp -r $DIRCAP/file $FILECAP to/missing/ : E2-DESTNAME |
|---|
| 856 | cp $DIRCAP $FILECAP to/missing/ : E4-NEED-R |
|---|
| 857 | cp -r $DIRCAP $FILECAP to/missing/ : E2-DESTNAME |
|---|
| 858 | # namedfile, unnameddir, nameddir |
|---|
| 859 | cp $PARENTCAP/dir3/file3 $DIRCAP $PARENTCAP/dir2 to/missing/ : E4-NEED-R |
|---|
| 860 | cp -r $PARENTCAP/dir3/file3 $DIRCAP $PARENTCAP/dir2 to/missing/ : to/missing/file3,to/missing/file,to/missing/dir2/file2 |
|---|
| 861 | # namedfile, unnameddir, nameddir, unnamedfile |
|---|
| 862 | cp $PARENTCAP/dir3/file3 $DIRCAP $PARENTCAP/dir2 $FILECAP to/missing/ : E4-NEED-R |
|---|
| 863 | cp -r $PARENTCAP/dir3/file3 $DIRCAP $PARENTCAP/dir2 $FILECAP to/missing/ : E2-DESTNAME |
|---|
| 864 | |
|---|
| 865 | # make sure empty directories are copied too |
|---|
| 866 | cp -r $PARENTCAP/dir4 to : to/dir4/emptydir/ |
|---|
| 867 | cp -r $PARENTCAP/dir4 to/ : to/dir4/emptydir/ |
|---|
| 868 | |
|---|
| 869 | # name collisions should cause errors, not overwrites |
|---|
| 870 | cp -r $PARENTCAP/dir6/dir $PARENTCAP/dir5/dir to : E9-COLLIDING-TARGETS |
|---|
| 871 | cp -r $PARENTCAP/dir5/dir $PARENTCAP/dir6/dir to : E9-COLLIDING-TARGETS |
|---|
| 872 | cp -r $DIRCAP6 $DIRCAP5 to : E9-COLLIDING-TARGETS |
|---|
| 873 | cp -r $DIRCAP5 $DIRCAP6 to : E9-COLLIDING-TARGETS |
|---|
| 874 | |
|---|
| 875 | """ |
|---|
| 876 | |
|---|
| 877 | class CopyOut(GridTestMixin, CLITestMixin, unittest.TestCase): |
|---|
| 878 | FILE_CONTENTS = b"file text" |
|---|
| 879 | FILE_CONTENTS_5 = b"5" |
|---|
| 880 | FILE_CONTENTS_6 = b"6" |
|---|
| 881 | |
|---|
| 882 | def do_setup(self): |
|---|
| 883 | # first we build a tahoe filesystem that contains: |
|---|
| 884 | # $PARENTCAP |
|---|
| 885 | # $PARENTCAP/dir == $DIRCAP == alias: |
|---|
| 886 | # $PARENTCAP/dir/file == $FILECAP |
|---|
| 887 | # $PARENTCAP/dir2 (named directory) |
|---|
| 888 | # $PARENTCAP/dir2/file2 |
|---|
| 889 | # $PARENTCAP/dir3/file3 (a second named file) |
|---|
| 890 | # $PARENTCAP/dir4 |
|---|
| 891 | # $PARENTCAP/dir4/emptydir/ (an empty directory) |
|---|
| 892 | # $PARENTCAP/dir5 == $DIRCAP5 |
|---|
| 893 | # $PARENTCAP/dir5/dir/collide (contents are "5") |
|---|
| 894 | # $PARENTCAP/dir6 == $DIRCAP6 |
|---|
| 895 | # $PARENTCAP/dir6/dir/collide (contents are "6") |
|---|
| 896 | |
|---|
| 897 | source_file = os.path.join(self.basedir, "file") |
|---|
| 898 | fileutil.write(source_file, self.FILE_CONTENTS) |
|---|
| 899 | source_file_5 = os.path.join(self.basedir, "file5") |
|---|
| 900 | fileutil.write(source_file_5, self.FILE_CONTENTS_5) |
|---|
| 901 | source_file_6 = os.path.join(self.basedir, "file6") |
|---|
| 902 | fileutil.write(source_file_6, self.FILE_CONTENTS_6) |
|---|
| 903 | |
|---|
| 904 | d = self.do_cli("mkdir") |
|---|
| 905 | def _stash_parentdircap(res): |
|---|
| 906 | (rc, out, err) = res |
|---|
| 907 | self.failUnlessEqual(rc, 0, str(res)) |
|---|
| 908 | self.failUnlessEqual(err, "", str(res)) |
|---|
| 909 | self.PARENTCAP = out.strip() |
|---|
| 910 | return self.do_cli("mkdir", "%s/dir" % self.PARENTCAP) |
|---|
| 911 | d.addCallback(_stash_parentdircap) |
|---|
| 912 | def _stash_dircap(res): |
|---|
| 913 | (rc, out, err) = res |
|---|
| 914 | self.failUnlessEqual(rc, 0, str(res)) |
|---|
| 915 | self.failUnlessEqual(err, "", str(res)) |
|---|
| 916 | self.DIRCAP = out.strip() |
|---|
| 917 | return self.do_cli("add-alias", "ALIAS", self.DIRCAP) |
|---|
| 918 | d.addCallback(_stash_dircap) |
|---|
| 919 | d.addCallback(lambda ign: |
|---|
| 920 | self.do_cli("put", source_file, "%s/dir/file" % self.PARENTCAP)) |
|---|
| 921 | def _stash_filecap(res): |
|---|
| 922 | (rc, out, err) = res |
|---|
| 923 | self.failUnlessEqual(rc, 0, str(res)) |
|---|
| 924 | self.failUnlessEqual(err.strip(), "201 Created", str(res)) |
|---|
| 925 | self.FILECAP = out.strip() |
|---|
| 926 | assert self.FILECAP.startswith("URI:LIT:") |
|---|
| 927 | d.addCallback(_stash_filecap) |
|---|
| 928 | d.addCallback(lambda ign: |
|---|
| 929 | self.do_cli("mkdir", "%s/dir2" % self.PARENTCAP)) |
|---|
| 930 | d.addCallback(lambda ign: |
|---|
| 931 | self.do_cli("put", source_file, "%s/dir2/file2" % self.PARENTCAP)) |
|---|
| 932 | d.addCallback(lambda ign: |
|---|
| 933 | self.do_cli("mkdir", "%s/dir3" % self.PARENTCAP)) |
|---|
| 934 | d.addCallback(lambda ign: |
|---|
| 935 | self.do_cli("put", source_file, "%s/dir3/file3" % self.PARENTCAP)) |
|---|
| 936 | d.addCallback(lambda ign: |
|---|
| 937 | self.do_cli("mkdir", "%s/dir4" % self.PARENTCAP)) |
|---|
| 938 | d.addCallback(lambda ign: |
|---|
| 939 | self.do_cli("mkdir", "%s/dir4/emptydir" % self.PARENTCAP)) |
|---|
| 940 | |
|---|
| 941 | d.addCallback(lambda ign: |
|---|
| 942 | self.do_cli("mkdir", "%s/dir5" % self.PARENTCAP)) |
|---|
| 943 | def _stash_dircap_5(res): |
|---|
| 944 | (rc, out, err) = res |
|---|
| 945 | self.failUnlessEqual(rc, 0, str(res)) |
|---|
| 946 | self.failUnlessEqual(err, "", str(res)) |
|---|
| 947 | self.DIRCAP5 = out.strip() |
|---|
| 948 | d.addCallback(_stash_dircap_5) |
|---|
| 949 | d.addCallback(lambda ign: |
|---|
| 950 | self.do_cli("mkdir", "%s/dir5/dir" % self.PARENTCAP)) |
|---|
| 951 | d.addCallback(lambda ign: |
|---|
| 952 | self.do_cli("put", source_file_5, "%s/dir5/dir/collide" % self.PARENTCAP)) |
|---|
| 953 | |
|---|
| 954 | d.addCallback(lambda ign: |
|---|
| 955 | self.do_cli("mkdir", "%s/dir6" % self.PARENTCAP)) |
|---|
| 956 | def _stash_dircap_6(res): |
|---|
| 957 | (rc, out, err) = res |
|---|
| 958 | self.failUnlessEqual(rc, 0, str(res)) |
|---|
| 959 | self.failUnlessEqual(err, "", str(res)) |
|---|
| 960 | self.DIRCAP6 = out.strip() |
|---|
| 961 | d.addCallback(_stash_dircap_6) |
|---|
| 962 | d.addCallback(lambda ign: |
|---|
| 963 | self.do_cli("mkdir", "%s/dir6/dir" % self.PARENTCAP)) |
|---|
| 964 | d.addCallback(lambda ign: |
|---|
| 965 | self.do_cli("put", source_file_6, "%s/dir6/dir/collide" % self.PARENTCAP)) |
|---|
| 966 | |
|---|
| 967 | return d |
|---|
| 968 | |
|---|
| 969 | def check_output(self): |
|---|
| 970 | # locate the files and directories created (if any) under to/ |
|---|
| 971 | top = os.path.join(self.basedir, "to") |
|---|
| 972 | results = set() |
|---|
| 973 | for (dirpath, dirnames, filenames) in os.walk(top): |
|---|
| 974 | assert dirpath.startswith(top) |
|---|
| 975 | here = "/".join(dirpath.split(os.sep)[len(top.split(os.sep))-1:]) |
|---|
| 976 | results.add(here+"/") |
|---|
| 977 | for fn in filenames: |
|---|
| 978 | contents = fileutil.read(os.path.join(dirpath, fn)) |
|---|
| 979 | if contents == self.FILE_CONTENTS: |
|---|
| 980 | results.add("%s/%s" % (here, fn)) |
|---|
| 981 | elif contents == self.FILE_CONTENTS_5: |
|---|
| 982 | results.add("%s/%s=5" % (here, fn)) |
|---|
| 983 | elif contents == self.FILE_CONTENTS_6: |
|---|
| 984 | results.add("%s/%s=6" % (here, fn)) |
|---|
| 985 | return results |
|---|
| 986 | |
|---|
| 987 | def run_one_case(self, case): |
|---|
| 988 | cmd = (case |
|---|
| 989 | .replace("$PARENTCAP", self.PARENTCAP) |
|---|
| 990 | .replace("$DIRCAP5", self.DIRCAP5) |
|---|
| 991 | .replace("$DIRCAP6", self.DIRCAP6) |
|---|
| 992 | .replace("$DIRCAP", self.DIRCAP) |
|---|
| 993 | .replace("$DIRALIAS", "ALIAS:") |
|---|
| 994 | .replace("$FILECAP", self.FILECAP) |
|---|
| 995 | .split()) |
|---|
| 996 | target = cmd[-1] |
|---|
| 997 | _assert(target == "to" or target.startswith("to/"), target) |
|---|
| 998 | cmd[-1] = os.path.abspath(os.path.join(self.basedir, cmd[-1])) |
|---|
| 999 | |
|---|
| 1000 | # reset |
|---|
| 1001 | targetdir = os.path.abspath(os.path.join(self.basedir, "to")) |
|---|
| 1002 | fileutil.rm_dir(targetdir) |
|---|
| 1003 | os.mkdir(targetdir) |
|---|
| 1004 | |
|---|
| 1005 | if target.rstrip("/") == "to/existing-file": |
|---|
| 1006 | fileutil.write(cmd[-1], "existing file contents\n") |
|---|
| 1007 | |
|---|
| 1008 | # The abspath() for cmd[-1] strips a trailing slash, and we want to |
|---|
| 1009 | # test what happens when it is present. So put it back. |
|---|
| 1010 | if target.endswith("/"): |
|---|
| 1011 | cmd[-1] += "/" |
|---|
| 1012 | |
|---|
| 1013 | d = self.do_cli(*cmd) |
|---|
| 1014 | def _check(res): |
|---|
| 1015 | (rc, out, err) = res |
|---|
| 1016 | err = err.strip() |
|---|
| 1017 | if rc == 0: |
|---|
| 1018 | return self.check_output() |
|---|
| 1019 | if rc == 1: |
|---|
| 1020 | self.failUnlessEqual(out, "", str(res)) |
|---|
| 1021 | if "when copying into a directory, all source files must have names, but" in err: |
|---|
| 1022 | return set(["E2-DESTNAME"]) |
|---|
| 1023 | if err == "cannot copy directories without --recursive": |
|---|
| 1024 | return set(["E4-NEED-R"]) |
|---|
| 1025 | if err == "cannot copy directory into a file": |
|---|
| 1026 | return set(["E5-DIRTOFILE"]) |
|---|
| 1027 | if err == "copying multiple things requires target be a directory": |
|---|
| 1028 | return set(["E6-MANYONE"]) |
|---|
| 1029 | if err == "target is not a directory, but ends with a slash": |
|---|
| 1030 | return set(["E7-BADSLASH"]) |
|---|
| 1031 | if (err.startswith("source ") and |
|---|
| 1032 | "is not a directory, but ends with a slash" in err): |
|---|
| 1033 | return set(["E8-BADSLASH"]) |
|---|
| 1034 | if err == "cannot copy multiple files with the same name into the same target directory": |
|---|
| 1035 | return set(["E9-COLLIDING-TARGETS"]) |
|---|
| 1036 | self.fail("unrecognized error ('%s') %s" % (case, res)) |
|---|
| 1037 | d.addCallback(_check) |
|---|
| 1038 | return d |
|---|
| 1039 | |
|---|
| 1040 | def do_one_test(self, case, orig_expected): |
|---|
| 1041 | expected = set(orig_expected) |
|---|
| 1042 | printable_expected = ",".join(sorted(expected)) |
|---|
| 1043 | #print("---", case, ":", printable_expected) |
|---|
| 1044 | |
|---|
| 1045 | for f in orig_expected: |
|---|
| 1046 | # f is "dir/file" or "dir/sub/file" or "dir/" or "dir/sub/" |
|---|
| 1047 | # we want all parent directories in the set, with trailing / |
|---|
| 1048 | pieces = f.rstrip("/").split("/") |
|---|
| 1049 | for i in range(1,len(pieces)): |
|---|
| 1050 | parent = "/".join(pieces[:i]) |
|---|
| 1051 | expected.add(parent+"/") |
|---|
| 1052 | |
|---|
| 1053 | d = self.run_one_case(case) |
|---|
| 1054 | def _dump(got): |
|---|
| 1055 | ok = "ok" if got == expected else "FAIL" |
|---|
| 1056 | printable_got = ",".join(sorted(got)) |
|---|
| 1057 | print("%-31s: got %-19s, want %-19s %s" % (case, printable_got, |
|---|
| 1058 | printable_expected, ok)) |
|---|
| 1059 | return got |
|---|
| 1060 | #d.addCallback(_dump) |
|---|
| 1061 | def _check(got): |
|---|
| 1062 | self.failUnlessEqual(got, expected, case) |
|---|
| 1063 | d.addCallback(_check) |
|---|
| 1064 | return d |
|---|
| 1065 | |
|---|
| 1066 | def do_tests(self): |
|---|
| 1067 | # then we run various forms of "cp [-r] TAHOETHING to[/missing]" |
|---|
| 1068 | # and see what happens. |
|---|
| 1069 | d = defer.succeed(None) |
|---|
| 1070 | #print() |
|---|
| 1071 | |
|---|
| 1072 | for line in COPYOUT_TESTCASES.splitlines(): |
|---|
| 1073 | if "#" in line: |
|---|
| 1074 | line = line[:line.find("#")] |
|---|
| 1075 | line = line.strip() |
|---|
| 1076 | if not line: |
|---|
| 1077 | continue |
|---|
| 1078 | case, expected = line.split(":") |
|---|
| 1079 | case = case.strip() |
|---|
| 1080 | expected = frozenset(expected.strip().split(",")) |
|---|
| 1081 | |
|---|
| 1082 | d.addCallback(lambda ign, case=case, expected=expected: |
|---|
| 1083 | self.do_one_test(case, expected)) |
|---|
| 1084 | |
|---|
| 1085 | return d |
|---|
| 1086 | |
|---|
| 1087 | def test_cp_out(self): |
|---|
| 1088 | # test copying all sorts of things out of a tahoe filesystem |
|---|
| 1089 | self.basedir = "cli_cp/CopyOut/cp_out" |
|---|
| 1090 | self.set_up_grid(num_servers=1, oneshare=True) |
|---|
| 1091 | |
|---|
| 1092 | d = self.do_setup() |
|---|
| 1093 | d.addCallback(lambda ign: self.do_tests()) |
|---|
| 1094 | return d |
|---|