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.assertEquals(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 |
---|