transmission 2.83
[tomato.git] / release / src / router / transmission / libtransmission / rename-test.c
blobda336ed2fcfb5bcbcba55934da9242ab33992e48
1 /*
2 * This file Copyright (C) 2013-2014 Mnemosyne LLC
4 * It may be used under the GNU GPL versions 2 or 3
5 * or any future license endorsed by Mnemosyne LLC.
7 * $Id: rename-test.c 14241 2014-01-21 03:10:30Z jordan $
8 */
10 #include <assert.h>
11 #include <errno.h>
12 #include <stdio.h> /* fopen() */
13 #include <string.h> /* strcmp() */
14 #include <stdio.h>
16 #include <sys/types.h> /* stat() */
17 #include <sys/stat.h> /* stat() */
18 #include <unistd.h> /* stat(), sync() */
20 #include "transmission.h"
21 #include "resume.h"
22 #include "torrent.h" /* tr_isTorrent() */
23 #include "utils.h" /* tr_mkdirp() */
24 #include "variant.h"
26 #include "libtransmission-test.h"
28 /***
29 ****
30 ***/
32 static tr_session * session = NULL;
34 #define check_have_none(tor, totalSize) \
35 do { \
36 const tr_stat * st = tr_torrentStat(tor); \
37 check_int_eq (TR_STATUS_STOPPED, st->activity); \
38 check_int_eq (TR_STAT_OK, st->error); \
39 check_int_eq (totalSize, st->sizeWhenDone); \
40 check_int_eq (totalSize, st->leftUntilDone); \
41 check_int_eq (totalSize, tor->info.totalSize); \
42 check_int_eq (0, st->haveValid); \
43 } while (0)
45 static bool
46 testFileExistsAndConsistsOfThisString (const tr_torrent * tor, tr_file_index_t fileIndex, const char * str)
48 char * path;
49 const size_t str_len = strlen (str);
50 bool success = false;
52 path = tr_torrentFindFile (tor, fileIndex);
53 if (path != NULL)
55 uint8_t * contents;
56 size_t contents_len;
58 assert (tr_fileExists (path, NULL));
60 contents = tr_loadFile (path, &contents_len);
62 success = (str_len == contents_len)
63 && (!memcmp (contents, str, contents_len));
65 tr_free (contents);
66 tr_free (path);
69 return success;
72 static void
73 onRenameDone (tr_torrent * tor UNUSED, const char * oldpath UNUSED, const char * newname UNUSED, int error, void * user_data)
75 *(int*)user_data = error;
78 static int
79 torrentRenameAndWait (tr_torrent * tor,
80 const char * oldpath,
81 const char * newname)
83 int error = -1;
84 tr_torrentRenamePath (tor, oldpath, newname, onRenameDone, &error);
85 do {
86 tr_wait_msec (10);
87 } while (error == -1);
88 return error;
91 /***
92 ****
93 ***/
95 static void
96 create_file_with_contents (const char * path, const char * str)
98 FILE * fp;
99 char * dir;
100 const int tmperr = errno;
102 dir = tr_dirname (path);
103 errno = 0;
104 tr_mkdirp (dir, 0700);
105 assert (errno == 0);
106 tr_free (dir);
108 tr_remove (path);
109 fp = fopen (path, "wb");
110 fprintf (fp, "%s", str);
111 fclose (fp);
113 sync ();
115 errno = tmperr;
118 static void
119 create_single_file_torrent_contents (const char * top)
121 char * path = tr_buildPath (top, "hello-world.txt", NULL);
122 create_file_with_contents (path, "hello, world!\n");
123 tr_free (path);
126 static tr_torrent *
127 create_torrent_from_base64_metainfo (tr_ctor * ctor, const char * metainfo_base64)
129 int err;
130 int metainfo_len;
131 char * metainfo;
132 tr_torrent * tor;
134 /* create the torrent ctor */
135 metainfo = tr_base64_decode (metainfo_base64, -1, &metainfo_len);
136 assert (metainfo != NULL);
137 assert (metainfo_len > 0);
138 tr_ctorSetMetainfo (ctor, (uint8_t*)metainfo, metainfo_len);
139 tr_ctorSetPaused (ctor, TR_FORCE, true);
141 /* create the torrent */
142 err = 0;
143 tor = tr_torrentNew (ctor, &err, NULL);
144 assert (!err);
146 /* cleanup */
147 tr_free (metainfo);
148 return tor;
151 static int
152 test_single_filename_torrent (void)
154 uint64_t loaded;
155 tr_torrent * tor;
156 char * tmpstr;
157 const size_t totalSize = 14;
158 tr_ctor * ctor;
159 const tr_stat * st;
161 /* this is a single-file torrent whose file is hello-world.txt, holding the string "hello, world!" */
162 ctor = tr_ctorNew (session);
163 tor = create_torrent_from_base64_metainfo (ctor,
164 "ZDEwOmNyZWF0ZWQgYnkyNTpUcmFuc21pc3Npb24vMi42MSAoMTM0MDcpMTM6Y3JlYXRpb24gZGF0"
165 "ZWkxMzU4NTQ5MDk4ZTg6ZW5jb2Rpbmc1OlVURi04NDppbmZvZDY6bGVuZ3RoaTE0ZTQ6bmFtZTE1"
166 "OmhlbGxvLXdvcmxkLnR4dDEyOnBpZWNlIGxlbmd0aGkzMjc2OGU2OnBpZWNlczIwOukboJcrkFUY"
167 "f6LvqLXBVvSHqCk6Nzpwcml2YXRlaTBlZWU=");
168 check (tr_isTorrent (tor));
170 /* sanity check the info */
171 check_int_eq (1, tor->info.fileCount);
172 check_streq ("hello-world.txt", tor->info.files[0].name);
173 check (!tor->info.files[0].is_renamed);
175 /* sanity check the (empty) stats */
176 libttest_blockingTorrentVerify (tor);
177 check_have_none (tor, totalSize);
179 create_single_file_torrent_contents (tor->currentDir);
181 /* sanity check the stats again, now that we've added the file */
182 libttest_blockingTorrentVerify (tor);
183 st = tr_torrentStat (tor);
184 check_int_eq (TR_STATUS_STOPPED, st->activity);
185 check_int_eq (TR_STAT_OK, st->error);
186 check_int_eq (0, st->leftUntilDone);
187 check_int_eq (0, st->haveUnchecked);
188 check_int_eq (0, st->desiredAvailable);
189 check_int_eq (totalSize, st->sizeWhenDone);
190 check_int_eq (totalSize, st->haveValid);
193 *** okay! we've finally put together all the scaffolding to test
194 *** renaming a single-file torrent
197 /* confirm that bad inputs get caught */
199 check_int_eq (EINVAL, torrentRenameAndWait (tor, "hello-world.txt", NULL));
200 check_int_eq (EINVAL, torrentRenameAndWait (tor, "hello-world.txt", ""));
201 check_int_eq (EINVAL, torrentRenameAndWait (tor, "hello-world.txt", "."));
202 check_int_eq (EINVAL, torrentRenameAndWait (tor, "hello-world.txt", ".."));
203 check_int_eq (0, torrentRenameAndWait (tor, "hello-world.txt", "hello-world.txt"));
204 check_int_eq (EINVAL, torrentRenameAndWait (tor, "hello-world.txt", "hello/world.txt"));
206 check (!tor->info.files[0].is_renamed);
207 check_streq ("hello-world.txt", tor->info.files[0].name);
209 /***
210 **** Now try a rename that should succeed
211 ***/
213 tmpstr = tr_buildPath (tor->currentDir, "hello-world.txt", NULL);
214 check (tr_fileExists (tmpstr, NULL));
215 check_streq ("hello-world.txt", tr_torrentName(tor));
216 check_int_eq (0, torrentRenameAndWait (tor, tor->info.name, "foobar"));
217 check (!tr_fileExists (tmpstr, NULL)); /* confirm the old filename can't be found */
218 tr_free (tmpstr);
219 check (tor->info.files[0].is_renamed); /* confirm the file's 'renamed' flag is set */
220 check_streq ("foobar", tr_torrentName(tor)); /* confirm the torrent's name is now 'foobar' */
221 check_streq ("foobar", tor->info.files[0].name); /* confirm the file's name is now 'foobar' in our struct */
222 check (strstr (tor->info.torrent, "foobar") == NULL); /* confirm the name in the .torrent file hasn't changed */
223 tmpstr = tr_buildPath (tor->currentDir, "foobar", NULL);
224 check (tr_fileExists (tmpstr, NULL)); /* confirm the file's name is now 'foobar' on the disk */
225 tr_free (tmpstr);
226 check (testFileExistsAndConsistsOfThisString (tor, 0, "hello, world!\n")); /* confirm the contents are right */
228 /* (while it's renamed: confirm that the .resume file remembers the changes) */
229 tr_torrentSaveResume (tor);
230 sync ();
231 loaded = tr_torrentLoadResume (tor, ~0, ctor);
232 check_streq ("foobar", tr_torrentName(tor));
233 check ((loaded & TR_FR_NAME) != 0);
235 /***
236 **** ...and rename it back again
237 ***/
239 tmpstr = tr_buildPath (tor->currentDir, "foobar", NULL);
240 check (tr_fileExists (tmpstr, NULL));
241 check_int_eq (0, torrentRenameAndWait (tor, "foobar", "hello-world.txt"));
242 check (!tr_fileExists (tmpstr, NULL));
243 check (tor->info.files[0].is_renamed);
244 check_streq ("hello-world.txt", tor->info.files[0].name);
245 check_streq ("hello-world.txt", tr_torrentName(tor));
246 tr_free (tmpstr);
247 check (testFileExistsAndConsistsOfThisString (tor, 0, "hello, world!\n"));
249 /* cleanup */
250 tr_ctorFree (ctor);
251 tr_torrentRemove (tor, false, NULL);
252 return 0;
255 /***
256 ****
257 ****
258 ****
259 ***/
261 static void
262 create_multifile_torrent_contents (const char * top)
264 char * path;
266 path = tr_buildPath (top, "Felidae", "Felinae", "Acinonyx", "Cheetah", "Chester", NULL);
267 create_file_with_contents (path, "It ain't easy bein' cheesy.\n");
268 tr_free (path);
270 path = tr_buildPath (top, "Felidae", "Pantherinae", "Panthera", "Tiger", "Tony", NULL);
271 create_file_with_contents (path, "They’re Grrrrreat!\n");
272 tr_free (path);
274 path = tr_buildPath (top, "Felidae", "Felinae", "Felis", "catus", "Kyphi", NULL);
275 create_file_with_contents (path, "Inquisitive\n");
276 tr_free (path);
278 path = tr_buildPath (top, "Felidae", "Felinae", "Felis", "catus", "Saffron", NULL);
279 create_file_with_contents (path, "Tough\n");
280 tr_free (path);
282 sync ();
285 static int
286 test_multifile_torrent (void)
288 tr_file_index_t i;
289 uint64_t loaded;
290 tr_torrent * tor;
291 tr_ctor * ctor;
292 char * str;
293 char * tmp;
294 static const size_t totalSize = 67;
295 const tr_stat * st;
296 const tr_file * files;
297 const char * strings[4];
298 const char * expected_files[4] = {
299 "Felidae/Felinae/Acinonyx/Cheetah/Chester",
300 "Felidae/Felinae/Felis/catus/Kyphi",
301 "Felidae/Felinae/Felis/catus/Saffron",
302 "Felidae/Pantherinae/Panthera/Tiger/Tony"
304 const char * expected_contents[4] = {
305 "It ain't easy bein' cheesy.\n",
306 "Inquisitive\n",
307 "Tough\n",
308 "They’re Grrrrreat!\n"
311 ctor = tr_ctorNew (session);
312 tor = create_torrent_from_base64_metainfo (ctor,
313 "ZDEwOmNyZWF0ZWQgYnkyNTpUcmFuc21pc3Npb24vMi42MSAoMTM0MDcpMTM6Y3JlYXRpb24gZGF0"
314 "ZWkxMzU4NTU1NDIwZTg6ZW5jb2Rpbmc1OlVURi04NDppbmZvZDU6ZmlsZXNsZDY6bGVuZ3RoaTI4"
315 "ZTQ6cGF0aGw3OkZlbGluYWU4OkFjaW5vbnl4NzpDaGVldGFoNzpDaGVzdGVyZWVkNjpsZW5ndGhp"
316 "MTJlNDpwYXRobDc6RmVsaW5hZTU6RmVsaXM1OmNhdHVzNTpLeXBoaWVlZDY6bGVuZ3RoaTZlNDpw"
317 "YXRobDc6RmVsaW5hZTU6RmVsaXM1OmNhdHVzNzpTYWZmcm9uZWVkNjpsZW5ndGhpMjFlNDpwYXRo"
318 "bDExOlBhbnRoZXJpbmFlODpQYW50aGVyYTU6VGlnZXI0OlRvbnllZWU0Om5hbWU3OkZlbGlkYWUx"
319 "MjpwaWVjZSBsZW5ndGhpMzI3NjhlNjpwaWVjZXMyMDp27buFkmy8ICfNX4nsJmt0Ckm2Ljc6cHJp"
320 "dmF0ZWkwZWVl");
321 check (tr_isTorrent (tor));
322 files = tor->info.files;
324 /* sanity check the info */
325 check_streq (tor->info.name, "Felidae");
326 check_int_eq (totalSize, tor->info.totalSize);
327 check_int_eq (4, tor->info.fileCount);
328 for (i=0; i<4; ++i)
329 check_streq (expected_files[i], files[i].name);
331 /* sanity check the (empty) stats */
332 libttest_blockingTorrentVerify (tor);
333 check_have_none (tor, totalSize);
335 /* build the local data */
336 create_multifile_torrent_contents (tor->currentDir);
338 /* sanity check the (full) stats */
339 libttest_blockingTorrentVerify (tor);
340 st = tr_torrentStat (tor);
341 check_int_eq (TR_STATUS_STOPPED, st->activity);
342 check_int_eq (TR_STAT_OK, st->error);
343 check_int_eq (0, st->leftUntilDone);
344 check_int_eq (0, st->haveUnchecked);
345 check_int_eq (0, st->desiredAvailable);
346 check_int_eq (totalSize, st->sizeWhenDone);
347 check_int_eq (totalSize, st->haveValid);
351 *** okay! let's test renaming.
354 /* rename a leaf... */
355 check_int_eq (0, torrentRenameAndWait (tor, "Felidae/Felinae/Felis/catus/Kyphi", "placeholder"));
356 check_streq (files[1].name, "Felidae/Felinae/Felis/catus/placeholder");
357 check (testFileExistsAndConsistsOfThisString (tor, 1, "Inquisitive\n"));
359 /* ...and back again */
360 check_int_eq (0, torrentRenameAndWait (tor, "Felidae/Felinae/Felis/catus/placeholder", "Kyphi"));
361 check_streq (files[1].name, "Felidae/Felinae/Felis/catus/Kyphi");
362 testFileExistsAndConsistsOfThisString (tor, 1, "Inquisitive\n");
364 /* rename a branch... */
365 check_int_eq (0, torrentRenameAndWait (tor, "Felidae/Felinae/Felis/catus", "placeholder"));
366 check_streq (expected_files[0], files[0].name);
367 check_streq ("Felidae/Felinae/Felis/placeholder/Kyphi", files[1].name);
368 check_streq ("Felidae/Felinae/Felis/placeholder/Saffron", files[2].name);
369 check_streq (expected_files[3], files[3].name);
370 check (testFileExistsAndConsistsOfThisString (tor, 1, expected_contents[1]));
371 check (testFileExistsAndConsistsOfThisString (tor, 2, expected_contents[2]));
372 check (files[0].is_renamed == false);
373 check (files[1].is_renamed == true);
374 check (files[2].is_renamed == true);
375 check (files[3].is_renamed == false);
377 /* (while the branch is renamed: confirm that the .resume file remembers the changes) */
378 tr_torrentSaveResume (tor);
379 /* this is a bit dodgy code-wise, but let's make sure the .resume file got the name */
380 tr_free (files[1].name);
381 tor->info.files[1].name = tr_strdup ("gabba gabba hey");
382 loaded = tr_torrentLoadResume (tor, ~0, ctor);
383 check ((loaded & TR_FR_FILENAMES) != 0);
384 check_streq (expected_files[0], files[0].name);
385 check_streq ("Felidae/Felinae/Felis/placeholder/Kyphi", files[1].name);
386 check_streq ("Felidae/Felinae/Felis/placeholder/Saffron", files[2].name);
387 check_streq (expected_files[3], files[3].name);
389 /* ...and back again */
390 check_int_eq (0, torrentRenameAndWait (tor, "Felidae/Felinae/Felis/placeholder", "catus"));
391 for (i=0; i<4; ++i)
393 check_streq (expected_files[i], files[i].name);
394 check (testFileExistsAndConsistsOfThisString (tor, 1, expected_contents[1]));
396 check (files[0].is_renamed == false);
397 check (files[1].is_renamed == true);
398 check (files[2].is_renamed == true);
399 check (files[3].is_renamed == false);
401 /***
402 **** Test it an incomplete torrent...
403 ***/
405 /* remove the directory Felidae/Felinae/Felis/catus */
406 str = tr_torrentFindFile (tor, 1);
407 check (str != NULL);
408 tr_remove (str);
409 tr_free (str);
410 str = tr_torrentFindFile (tor, 2);
411 check (str != NULL);
412 tr_remove (str);
413 tmp = tr_dirname (str);
414 tr_remove (tmp);
415 tr_free (tmp);
416 tr_free (str);
417 sync ();
418 libttest_blockingTorrentVerify (tor);
419 testFileExistsAndConsistsOfThisString (tor, 0, expected_contents[0]);
420 for (i=1; i<=2; ++i)
422 str = tr_torrentFindFile (tor, i);
423 check_streq (NULL, str);
424 tr_free (str);
426 testFileExistsAndConsistsOfThisString (tor, 3, expected_contents[3]);
428 /* rename a branch... */
429 check_int_eq (0, torrentRenameAndWait (tor, "Felidae/Felinae/Felis/catus", "foo"));
430 check_streq (expected_files[0], files[0].name);
431 check_streq ("Felidae/Felinae/Felis/foo/Kyphi", files[1].name);
432 check_streq ("Felidae/Felinae/Felis/foo/Saffron", files[2].name);
433 check_streq (expected_files[3], files[3].name);
435 /* ...and back again */
436 check_int_eq (0, torrentRenameAndWait (tor, "Felidae/Felinae/Felis/foo", "catus"));
437 for (i=0; i<4; ++i)
438 check_streq (expected_files[i], files[i].name);
440 check_int_eq (0, torrentRenameAndWait (tor, "Felidae", "gabba"));
441 strings[0] = "gabba/Felinae/Acinonyx/Cheetah/Chester";
442 strings[1] = "gabba/Felinae/Felis/catus/Kyphi";
443 strings[2] = "gabba/Felinae/Felis/catus/Saffron";
444 strings[3] = "gabba/Pantherinae/Panthera/Tiger/Tony";
445 for (i=0; i<4; ++i)
447 check_streq (strings[i], files[i].name);
448 testFileExistsAndConsistsOfThisString (tor, i, expected_contents[i]);
451 /* rename the root, then a branch, and then a leaf... */
452 check_int_eq (0, torrentRenameAndWait (tor, "gabba", "Felidae"));
453 check_int_eq (0, torrentRenameAndWait (tor, "Felidae/Pantherinae/Panthera/Tiger", "Snow Leopard"));
454 check_int_eq (0, torrentRenameAndWait (tor, "Felidae/Pantherinae/Panthera/Snow Leopard/Tony", "10.6"));
455 strings[0] = "Felidae/Felinae/Acinonyx/Cheetah/Chester";
456 strings[1] = "Felidae/Felinae/Felis/catus/Kyphi";
457 strings[2] = "Felidae/Felinae/Felis/catus/Saffron";
458 strings[3] = "Felidae/Pantherinae/Panthera/Snow Leopard/10.6";
459 for (i=0; i<4; ++i)
461 check_streq (strings[i], files[i].name);
462 testFileExistsAndConsistsOfThisString (tor, i, expected_contents[i]);
465 /***
466 ****
467 ***/
469 /* cleanup */
470 tr_ctorFree (ctor);
471 tr_torrentRemove (tor, false, NULL);
472 return 0;
475 /***
476 ****
477 ***/
479 static int
480 test_partial_file (void)
482 tr_file_index_t i;
483 tr_torrent * tor;
484 const tr_stat * st;
485 tr_file_stat * fst;
486 const uint32_t pieceCount = 33;
487 const uint32_t pieceSize = 32768;
488 const uint32_t length[] = { 1048576, 4096, 512 };
489 const uint64_t totalSize = length[0] + length[1] + length[2];
490 const char * strings[3];
492 /***
493 **** create our test torrent with an incomplete .part file
494 ***/
496 tor = libttest_zero_torrent_init (session);
497 check_int_eq (totalSize, tor->info.totalSize);
498 check_int_eq (pieceSize, tor->info.pieceSize);
499 check_int_eq (pieceCount, tor->info.pieceCount);
500 check_streq ("files-filled-with-zeroes/1048576", tor->info.files[0].name);
501 check_streq ("files-filled-with-zeroes/4096", tor->info.files[1].name);
502 check_streq ("files-filled-with-zeroes/512", tor->info.files[2].name);
504 libttest_zero_torrent_populate (tor, false);
505 fst = tr_torrentFiles (tor, NULL);
506 check_int_eq (length[0] - pieceSize, fst[0].bytesCompleted);
507 check_int_eq (length[1], fst[1].bytesCompleted);
508 check_int_eq (length[2], fst[2].bytesCompleted);
509 tr_torrentFilesFree (fst, tor->info.fileCount);
510 st = tr_torrentStat (tor);
511 check_int_eq (totalSize, st->sizeWhenDone);
512 check_int_eq (pieceSize, st->leftUntilDone);
514 /***
515 ****
516 ***/
518 check_int_eq (0, torrentRenameAndWait (tor, "files-filled-with-zeroes", "foo"));
519 check_int_eq (0, torrentRenameAndWait (tor, "foo/1048576", "bar"));
520 strings[0] = "foo/bar";
521 strings[1] = "foo/4096";
522 strings[2] = "foo/512";
523 for (i=0; i<3; ++i)
525 check_streq (strings[i], tor->info.files[i].name);
528 strings[0] = "foo/bar.part";
529 for (i=0; i<3; ++i)
531 char * expected = tr_buildPath (tor->currentDir, strings[i], NULL);
532 char * path = tr_torrentFindFile (tor, i);
533 check_streq (expected, path);
534 tr_free (path);
535 tr_free (expected);
538 tr_torrentRemove (tor, false, NULL);
539 return 0;
542 /***
543 ****
544 ***/
547 main (void)
549 int ret;
550 const testFunc tests[] = { test_single_filename_torrent,
551 test_multifile_torrent,
552 test_partial_file };
554 session = libttest_session_init (NULL);
555 ret = runTests (tests, NUM_TESTS (tests));
556 libttest_session_close (session);
558 return ret;