source: trunk/libtransmission/rename-test.c @ 13911

Last change on this file since 13911 was 13911, checked in by jordan, 8 years ago

(libT) maybe fix removed-file-found issue reported by Lacrocivious

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