| 1 | """ |
|---|
| 2 | Integration tests for getting and putting files, including reading from stdin |
|---|
| 3 | and stdout. |
|---|
| 4 | """ |
|---|
| 5 | |
|---|
| 6 | from subprocess import Popen, PIPE, check_output, check_call |
|---|
| 7 | |
|---|
| 8 | import pytest |
|---|
| 9 | from twisted.internet import reactor |
|---|
| 10 | from twisted.internet.threads import blockingCallFromThread |
|---|
| 11 | |
|---|
| 12 | from .util import run_in_thread, cli |
|---|
| 13 | |
|---|
| 14 | DATA = b"abc123 this is not utf-8 decodable \xff\x00\x33 \x11" |
|---|
| 15 | try: |
|---|
| 16 | DATA.decode("utf-8") |
|---|
| 17 | except UnicodeDecodeError: |
|---|
| 18 | pass # great, what we want |
|---|
| 19 | else: |
|---|
| 20 | raise ValueError("BUG, the DATA string was decoded from UTF-8") |
|---|
| 21 | |
|---|
| 22 | |
|---|
| 23 | @pytest.fixture(scope="session") |
|---|
| 24 | def get_put_alias(alice): |
|---|
| 25 | cli(alice.process, "create-alias", "getput") |
|---|
| 26 | |
|---|
| 27 | |
|---|
| 28 | def read_bytes(path): |
|---|
| 29 | with open(path, "rb") as f: |
|---|
| 30 | return f.read() |
|---|
| 31 | |
|---|
| 32 | |
|---|
| 33 | @run_in_thread |
|---|
| 34 | def test_put_from_stdin(alice, get_put_alias, tmpdir): |
|---|
| 35 | """ |
|---|
| 36 | It's possible to upload a file via `tahoe put`'s STDIN, and then download |
|---|
| 37 | it to a file. |
|---|
| 38 | """ |
|---|
| 39 | tempfile = str(tmpdir.join("file")) |
|---|
| 40 | p = Popen( |
|---|
| 41 | ["tahoe", "--node-directory", alice.process.node_dir, "put", "-", "getput:fromstdin"], |
|---|
| 42 | stdin=PIPE |
|---|
| 43 | ) |
|---|
| 44 | p.stdin.write(DATA) |
|---|
| 45 | p.stdin.close() |
|---|
| 46 | assert p.wait() == 0 |
|---|
| 47 | |
|---|
| 48 | cli(alice.process, "get", "getput:fromstdin", tempfile) |
|---|
| 49 | assert read_bytes(tempfile) == DATA |
|---|
| 50 | |
|---|
| 51 | |
|---|
| 52 | @run_in_thread |
|---|
| 53 | def test_get_to_stdout(alice, get_put_alias, tmpdir): |
|---|
| 54 | """ |
|---|
| 55 | It's possible to upload a file, and then download it to stdout. |
|---|
| 56 | """ |
|---|
| 57 | tempfile = tmpdir.join("file") |
|---|
| 58 | with tempfile.open("wb") as f: |
|---|
| 59 | f.write(DATA) |
|---|
| 60 | cli(alice.process, "put", str(tempfile), "getput:tostdout") |
|---|
| 61 | |
|---|
| 62 | p = Popen( |
|---|
| 63 | ["tahoe", "--node-directory", alice.process.node_dir, "get", "getput:tostdout", "-"], |
|---|
| 64 | stdout=PIPE |
|---|
| 65 | ) |
|---|
| 66 | assert p.stdout.read() == DATA |
|---|
| 67 | assert p.wait() == 0 |
|---|
| 68 | |
|---|
| 69 | |
|---|
| 70 | @run_in_thread |
|---|
| 71 | def test_large_file(alice, get_put_alias, tmp_path): |
|---|
| 72 | """ |
|---|
| 73 | It's possible to upload and download a larger file. |
|---|
| 74 | |
|---|
| 75 | We avoid stdin/stdout since that's flaky on Windows. |
|---|
| 76 | """ |
|---|
| 77 | tempfile = tmp_path / "file" |
|---|
| 78 | with tempfile.open("wb") as f: |
|---|
| 79 | f.write(DATA * 1_000_000) |
|---|
| 80 | cli(alice.process, "put", str(tempfile), "getput:largefile") |
|---|
| 81 | |
|---|
| 82 | outfile = tmp_path / "out" |
|---|
| 83 | check_call( |
|---|
| 84 | ["tahoe", "--node-directory", alice.process.node_dir, "get", "getput:largefile", str(outfile)], |
|---|
| 85 | ) |
|---|
| 86 | assert outfile.read_bytes() == tempfile.read_bytes() |
|---|
| 87 | |
|---|
| 88 | |
|---|
| 89 | @run_in_thread |
|---|
| 90 | def test_upload_download_immutable_different_default_max_segment_size(alice, get_put_alias, tmpdir, request): |
|---|
| 91 | """ |
|---|
| 92 | Tahoe-LAFS used to have a default max segment size of 128KB, and is now |
|---|
| 93 | 1MB. Test that an upload created when 128KB was the default can be |
|---|
| 94 | downloaded with 1MB as the default (i.e. old uploader, new downloader), and |
|---|
| 95 | vice versa, (new uploader, old downloader). |
|---|
| 96 | """ |
|---|
| 97 | tempfile = tmpdir.join("file") |
|---|
| 98 | large_data = DATA * 100_000 |
|---|
| 99 | assert len(large_data) > 2 * 1024 * 1024 |
|---|
| 100 | with tempfile.open("wb") as f: |
|---|
| 101 | f.write(large_data) |
|---|
| 102 | |
|---|
| 103 | def set_segment_size(segment_size): |
|---|
| 104 | return blockingCallFromThread( |
|---|
| 105 | reactor, |
|---|
| 106 | lambda: alice.reconfigure_zfec( |
|---|
| 107 | reactor, |
|---|
| 108 | (1, 1, 1), |
|---|
| 109 | None, |
|---|
| 110 | max_segment_size=segment_size |
|---|
| 111 | ) |
|---|
| 112 | ) |
|---|
| 113 | |
|---|
| 114 | # 1. Upload file 1 with default segment size set to 1MB |
|---|
| 115 | set_segment_size(1024 * 1024) |
|---|
| 116 | cli(alice.process, "put", str(tempfile), "getput:seg1024kb") |
|---|
| 117 | |
|---|
| 118 | # 2. Download file 1 with default segment size set to 128KB |
|---|
| 119 | set_segment_size(128 * 1024) |
|---|
| 120 | assert large_data == check_output( |
|---|
| 121 | ["tahoe", "--node-directory", alice.process.node_dir, "get", "getput:seg1024kb", "-"] |
|---|
| 122 | ) |
|---|
| 123 | |
|---|
| 124 | # 3. Upload file 2 with default segment size set to 128KB |
|---|
| 125 | cli(alice.process, "put", str(tempfile), "getput:seg128kb") |
|---|
| 126 | |
|---|
| 127 | # 4. Download file 2 with default segment size set to 1MB |
|---|
| 128 | set_segment_size(1024 * 1024) |
|---|
| 129 | assert large_data == check_output( |
|---|
| 130 | ["tahoe", "--node-directory", alice.process.node_dir, "get", "getput:seg128kb", "-"] |
|---|
| 131 | ) |
|---|