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 $
12 #include <stdio.h> /* fopen() */
13 #include <string.h> /* strcmp() */
16 #include <sys/types.h> /* stat() */
17 #include <sys/stat.h> /* stat() */
18 #include <unistd.h> /* stat(), sync() */
20 #include "transmission.h"
22 #include "torrent.h" /* tr_isTorrent() */
23 #include "utils.h" /* tr_mkdirp() */
26 #include "libtransmission-test.h"
32 static tr_session
* session
= NULL
;
34 #define check_have_none(tor, totalSize) \
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); \
46 testFileExistsAndConsistsOfThisString (const tr_torrent
* tor
, tr_file_index_t fileIndex
, const char * str
)
49 const size_t str_len
= strlen (str
);
52 path
= tr_torrentFindFile (tor
, fileIndex
);
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
));
73 onRenameDone (tr_torrent
* tor UNUSED
, const char * oldpath UNUSED
, const char * newname UNUSED
, int error
, void * user_data
)
75 *(int*)user_data
= error
;
79 torrentRenameAndWait (tr_torrent
* tor
,
84 tr_torrentRenamePath (tor
, oldpath
, newname
, onRenameDone
, &error
);
87 } while (error
== -1);
96 create_file_with_contents (const char * path
, const char * str
)
100 const int tmperr
= errno
;
102 dir
= tr_dirname (path
);
104 tr_mkdirp (dir
, 0700);
109 fp
= fopen (path
, "wb");
110 fprintf (fp
, "%s", str
);
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");
127 create_torrent_from_base64_metainfo (tr_ctor
* ctor
, const char * metainfo_base64
)
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 */
143 tor
= tr_torrentNew (ctor
, &err
, NULL
);
152 test_single_filename_torrent (void)
157 const size_t totalSize
= 14;
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
);
210 **** Now try a rename that should succeed
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 */
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 */
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
);
231 loaded
= tr_torrentLoadResume (tor
, ~0, ctor
);
232 check_streq ("foobar", tr_torrentName(tor
));
233 check ((loaded
& TR_FR_NAME
) != 0);
236 **** ...and rename it back again
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
));
247 check (testFileExistsAndConsistsOfThisString (tor
, 0, "hello, world!\n"));
251 tr_torrentRemove (tor
, false, NULL
);
262 create_multifile_torrent_contents (const char * top
)
266 path
= tr_buildPath (top
, "Felidae", "Felinae", "Acinonyx", "Cheetah", "Chester", NULL
);
267 create_file_with_contents (path
, "It ain't easy bein' cheesy.\n");
270 path
= tr_buildPath (top
, "Felidae", "Pantherinae", "Panthera", "Tiger", "Tony", NULL
);
271 create_file_with_contents (path
, "They’re Grrrrreat!\n");
274 path
= tr_buildPath (top
, "Felidae", "Felinae", "Felis", "catus", "Kyphi", NULL
);
275 create_file_with_contents (path
, "Inquisitive\n");
278 path
= tr_buildPath (top
, "Felidae", "Felinae", "Felis", "catus", "Saffron", NULL
);
279 create_file_with_contents (path
, "Tough\n");
286 test_multifile_torrent (void)
294 static const size_t totalSize
= 67;
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",
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"
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
);
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"));
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);
402 **** Test it an incomplete torrent...
405 /* remove the directory Felidae/Felinae/Felis/catus */
406 str
= tr_torrentFindFile (tor
, 1);
410 str
= tr_torrentFindFile (tor
, 2);
413 tmp
= tr_dirname (str
);
418 libttest_blockingTorrentVerify (tor
);
419 testFileExistsAndConsistsOfThisString (tor
, 0, expected_contents
[0]);
422 str
= tr_torrentFindFile (tor
, i
);
423 check_streq (NULL
, 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"));
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";
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";
461 check_streq (strings
[i
], files
[i
].name
);
462 testFileExistsAndConsistsOfThisString (tor
, i
, expected_contents
[i
]);
471 tr_torrentRemove (tor
, false, NULL
);
480 test_partial_file (void)
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];
493 **** create our test torrent with an incomplete .part file
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
);
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";
525 check_streq (strings
[i
], tor
->info
.files
[i
].name
);
528 strings
[0] = "foo/bar.part";
531 char * expected
= tr_buildPath (tor
->currentDir
, strings
[i
], NULL
);
532 char * path
= tr_torrentFindFile (tor
, i
);
533 check_streq (expected
, path
);
538 tr_torrentRemove (tor
, false, NULL
);
550 const testFunc tests
[] = { test_single_filename_torrent
,
551 test_multifile_torrent
,
554 session
= libttest_session_init (NULL
);
555 ret
= runTests (tests
, NUM_TESTS (tests
));
556 libttest_session_close (session
);