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