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

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

(libT) change the API signature for tr_torrentVerify() s.t. client code can be notified when the verify is finished

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