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

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

(libT) #1220 'change file and folder names': add a unit test for the r13837 bugfix of .part files + tr_torrentRenamePath()

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