1 /*
2 * This file Copyright (C) 2013-2014 Mnemosyne LLC
3 *
4 * It may be used under the GNU GPL versions 2 or 3
5 * or any future license endorsed by Mnemosyne LLC.
6 *
7 */
8
9 #include <string.h> /* strcmp() */
10 #include <stdio.h>
11
12 #include <event2/buffer.h>
13
14 #include "transmission.h"
15 #include "cache.h"
16 #include "file.h"
17 #include "resume.h"
18 #include "trevent.h"
19 #include "torrent.h" /* tr_isTorrent() */
20 #include "variant.h"
21
22 #include "libtransmission-test.h"
23
24 /***
25 ****
26 ***/
27
zeroes_completeness_func(tr_torrent * torrent UNUSED,tr_completeness completeness,bool wasRunning UNUSED,void * user_data)28 static void zeroes_completeness_func(tr_torrent* torrent UNUSED, tr_completeness completeness, bool wasRunning UNUSED,
29 void* user_data)
30 {
31 *(tr_completeness*)user_data = completeness;
32 }
33
34 #define check_file_location(tor, i, expected_path) \
35 do \
36 { \
37 char* path = tr_torrentFindFile(tor, i); \
38 char* expected = expected_path; \
39 check_str(path, ==, expected); \
40 tr_free(expected); \
41 tr_free(path); \
42 } \
43 while (0)
44
45 struct test_incomplete_dir_data
46 {
47 tr_session* session;
48 tr_torrent* tor;
49 tr_block_index_t block;
50 tr_piece_index_t pieceIndex;
51 uint32_t offset;
52 struct evbuffer* buf;
53 bool done;
54 };
55
test_incomplete_dir_threadfunc(void * vdata)56 static void test_incomplete_dir_threadfunc(void* vdata)
57 {
58 struct test_incomplete_dir_data* data = vdata;
59 tr_cacheWriteBlock(data->session->cache, data->tor, 0, data->offset, data->tor->blockSize, data->buf);
60 tr_torrentGotBlock(data->tor, data->block);
61 data->done = true;
62 }
63
test_incomplete_dir_impl(char const * incomplete_dir,char const * download_dir)64 static int test_incomplete_dir_impl(char const* incomplete_dir, char const* download_dir)
65 {
66 tr_session* session;
67 tr_torrent* tor;
68 tr_completeness completeness;
69 tr_completeness const completeness_unset = -1;
70 time_t const deadline = time(NULL) + 300;
71 tr_variant settings;
72
73 /* init the session */
74 tr_variantInitDict(&settings, 3);
75 tr_variantDictAddStr(&settings, TR_KEY_download_dir, download_dir);
76 tr_variantDictAddStr(&settings, TR_KEY_incomplete_dir, incomplete_dir);
77 tr_variantDictAddBool(&settings, TR_KEY_incomplete_dir_enabled, true);
78 session = libttest_session_init(&settings);
79 tr_variantFree(&settings);
80 download_dir = tr_sessionGetDownloadDir(session);
81 incomplete_dir = tr_sessionGetIncompleteDir(session);
82
83 /* init an incomplete torrent.
84 the test zero_torrent will be missing its first piece */
85 tor = libttest_zero_torrent_init(session);
86 libttest_zero_torrent_populate(tor, false);
87 check_uint(tr_torrentStat(tor)->leftUntilDone, ==, tor->info.pieceSize);
88 check_file_location(tor, 0, tr_strdup_printf("%s/%s.part", incomplete_dir, tor->info.files[0].name));
89 check_file_location(tor, 1, tr_buildPath(incomplete_dir, tor->info.files[1].name, NULL));
90 check_uint(tr_torrentStat(tor)->leftUntilDone, ==, tor->info.pieceSize);
91
92 completeness = completeness_unset;
93 tr_torrentSetCompletenessCallback(tor, zeroes_completeness_func, &completeness);
94
95 /* now finish writing it */
96 {
97 tr_block_index_t first;
98 tr_block_index_t last;
99 char* zero_block = tr_new0(char, tor->blockSize);
100 struct test_incomplete_dir_data data;
101
102 data.session = session;
103 data.tor = tor;
104 data.pieceIndex = 0;
105 data.buf = evbuffer_new();
106
107 tr_torGetPieceBlockRange(tor, data.pieceIndex, &first, &last);
108
109 for (tr_block_index_t block_index = first; block_index <= last; ++block_index)
110 {
111 evbuffer_add(data.buf, zero_block, tor->blockSize);
112 data.block = block_index;
113 data.done = false;
114 data.offset = data.block * tor->blockSize;
115 tr_runInEventThread(session, test_incomplete_dir_threadfunc, &data);
116
117 do
118 {
119 tr_wait_msec(50);
120 }
121 while (!data.done);
122 }
123
124 evbuffer_free(data.buf);
125 tr_free(zero_block);
126 }
127
128 libttest_blockingTorrentVerify(tor);
129 check_uint(tr_torrentStat(tor)->leftUntilDone, ==, 0);
130
131 while (completeness == completeness_unset && time(NULL) <= deadline)
132 {
133 tr_wait_msec(50);
134 }
135
136 check_int(completeness, ==, TR_SEED);
137
138 for (tr_file_index_t file_index = 0; file_index < tor->info.fileCount; ++file_index)
139 {
140 check_file_location(tor, file_index, tr_buildPath(download_dir, tor->info.files[file_index].name, NULL));
141 }
142
143 /* cleanup */
144 tr_torrentRemove(tor, true, tr_sys_path_remove);
145 libttest_session_close(session);
146 return 0;
147 }
148
test_incomplete_dir(void)149 static int test_incomplete_dir(void)
150 {
151 int rv;
152
153 /* test what happens when incompleteDir is a subdir of downloadDir*/
154 if ((rv = test_incomplete_dir_impl("Downloads/Incomplete", "Downloads")) != 0)
155 {
156 return rv;
157 }
158
159 /* test what happens when downloadDir is a subdir of incompleteDir */
160 if ((rv = test_incomplete_dir_impl("Downloads", "Downloads/Complete")) != 0)
161 {
162 return rv;
163 }
164
165 /* test what happens when downloadDir and incompleteDir are siblings */
166 if ((rv = test_incomplete_dir_impl("Incomplete", "Downloads")) != 0)
167 {
168 return rv;
169 }
170
171 return 0;
172 }
173
174 /***
175 ****
176 ***/
177
test_set_location(void)178 static int test_set_location(void)
179 {
180 int state;
181 char* target_dir;
182 tr_torrent* tor;
183 tr_session* session;
184 time_t const deadline = time(NULL) + 300;
185
186 /* init the session */
187 session = libttest_session_init(NULL);
188 target_dir = tr_buildPath(tr_sessionGetConfigDir(session), "target", NULL);
189 tr_sys_dir_create(target_dir, TR_SYS_DIR_CREATE_PARENTS, 0777, NULL);
190
191 /* init a torrent. */
192 tor = libttest_zero_torrent_init(session);
193 libttest_zero_torrent_populate(tor, true);
194 libttest_blockingTorrentVerify(tor);
195 check_uint(tr_torrentStat(tor)->leftUntilDone, ==, 0);
196
197 /* now move it */
198 state = -1;
199 tr_torrentSetLocation(tor, target_dir, true, NULL, &state);
200
201 while (state == TR_LOC_MOVING && time(NULL) <= deadline)
202 {
203 tr_wait_msec(50);
204 }
205
206 check_int(state, ==, TR_LOC_DONE);
207
208 /* confirm the torrent is still complete after being moved */
209 libttest_blockingTorrentVerify(tor);
210 check_uint(tr_torrentStat(tor)->leftUntilDone, ==, 0);
211
212 /* confirm the filest really got moved */
213 libttest_sync();
214
215 for (tr_file_index_t file_index = 0; file_index < tor->info.fileCount; ++file_index)
216 {
217 check_file_location(tor, file_index, tr_buildPath(target_dir, tor->info.files[file_index].name, NULL));
218 }
219
220 /* cleanup */
221 tr_free(target_dir);
222 tr_torrentRemove(tor, true, tr_sys_path_remove);
223 libttest_session_close(session);
224 return 0;
225 }
226
227 /***
228 ****
229 ***/
230
main(void)231 int main(void)
232 {
233 testFunc const tests[] =
234 {
235 test_incomplete_dir,
236 test_set_location
237 };
238
239 return runTests(tests, NUM_TESTS(tests));
240 }
241