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

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

(libT) rename-test tracers

File size: 18.2 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  int rv;
106  FILE * fp;
107  char * dir;
108  const int tmperr = errno;
109
110  dir = tr_dirname (path);
111  errno = 0;
112  rv = tr_mkdirp (dir, 0700);
113  assert (errno == 0);
114  assert (rv == 0);
115  tr_free (dir);
116
117  remove (path);
118  fp = fopen (path, "wb");
119  fprintf (fp, "%s", str);
120  fclose (fp);
121
122  sync ();
123
124  errno = tmperr;
125}
126
127static void
128create_single_file_torrent_contents (const char * top)
129{
130  char * path = tr_buildPath (top, "hello-world.txt", NULL);
131  create_file_with_contents (path, "hello, world!\n");
132  tr_free (path);
133}
134
135static tr_torrent *
136create_torrent_from_base64_metainfo (tr_ctor * ctor, const char * metainfo_base64)
137{
138  int err;
139  int metainfo_len;
140  char * metainfo;
141  tr_torrent * tor;
142
143  /* create the torrent ctor */
144  metainfo = tr_base64_decode (metainfo_base64, -1, &metainfo_len);
145  assert (metainfo != NULL);
146  assert (metainfo_len > 0);
147  assert (session != NULL);
148  tr_ctorSetMetainfo (ctor, (uint8_t*)metainfo, metainfo_len);
149  tr_ctorSetPaused (ctor, TR_FORCE, true);
150
151  /* create the torrent */
152  err = 0;
153  tor = tr_torrentNew (ctor, &err);
154  assert (!err);
155
156  /* cleanup */
157  tr_free (metainfo); 
158  return tor;
159}
160
161static int
162test_single_filename_torrent (void)
163{
164  uint64_t loaded;
165  tr_torrent * tor;
166  char * tmpstr;
167  const size_t totalSize = 14;
168  tr_ctor * ctor;
169
170  /* this is a single-file torrent whose file is hello-world.txt, holding the string "hello, world!" */
171  ctor = tr_ctorNew (session);
172  tor = create_torrent_from_base64_metainfo (ctor,
173    "ZDEwOmNyZWF0ZWQgYnkyNTpUcmFuc21pc3Npb24vMi42MSAoMTM0MDcpMTM6Y3JlYXRpb24gZGF0"
174    "ZWkxMzU4NTQ5MDk4ZTg6ZW5jb2Rpbmc1OlVURi04NDppbmZvZDY6bGVuZ3RoaTE0ZTQ6bmFtZTE1"
175    "OmhlbGxvLXdvcmxkLnR4dDEyOnBpZWNlIGxlbmd0aGkzMjc2OGU2OnBpZWNlczIwOukboJcrkFUY"
176    "f6LvqLXBVvSHqCk6Nzpwcml2YXRlaTBlZWU=");
177  check (tr_isTorrent (tor));
178
179  /* sanity check the info */
180  check_int_eq (1, tor->info.fileCount);
181  check_streq ("hello-world.txt", tor->info.files[0].name);
182  check (!tor->info.files[0].is_renamed);
183
184  /* sanity check the (empty) stats */
185  verify_and_block_until_done (tor);
186  check_have_none (tor, totalSize);
187
188  create_single_file_torrent_contents (tor->downloadDir);
189
190  /* sanity check the stats again, now that we've added the file */
191  verify_and_block_until_done (tor);
192  check_have_all (tor, totalSize);
193
194  /**
195  ***  okay! we've finally put together all the scaffolding to test
196  ***  renaming a single-file torrent
197  **/
198
199  /* confirm that bad inputs get caught */
200
201  check_int_eq (EINVAL, torrentRenameAndWait (tor, "hello-world.txt", NULL));
202  check_int_eq (EINVAL, torrentRenameAndWait (tor, "hello-world.txt", ""));
203  check_int_eq (EINVAL, torrentRenameAndWait (tor, "hello-world.txt", "."));
204  check_int_eq (EINVAL, torrentRenameAndWait (tor, "hello-world.txt", ".."));
205  check_int_eq (0, torrentRenameAndWait (tor, "hello-world.txt", "hello-world.txt"));
206  check_int_eq (EINVAL, torrentRenameAndWait (tor, "hello-world.txt", "hello/world.txt"));
207
208  check (!tor->info.files[0].is_renamed);
209  check_streq ("hello-world.txt", tor->info.files[0].name);
210
211  /***
212  ****  Now try a rename that should succeed
213  ***/
214
215  tmpstr = tr_buildPath (tor->downloadDir, "hello-world.txt", NULL); 
216  check (tr_fileExists (tmpstr, NULL));
217  check_streq ("hello-world.txt", tr_torrentName(tor));
218  check_int_eq (0, torrentRenameAndWait (tor, tor->info.name, "foobar"));
219  check (!tr_fileExists (tmpstr, NULL)); /* confirm the old filename can't be found */
220  check (tor->info.files[0].is_renamed); /* confirm the file's 'renamed' flag is set */
221  check_streq ("foobar", tr_torrentName(tor)); /* confirm the torrent's name is now 'foobar' */
222  check_streq ("foobar", tor->info.files[0].name); /* confirm the file's name is now 'foobar' */
223  check (strstr (tor->info.torrent, "foobar") == NULL); /* confirm the name in the .torrent file hasn't changed */
224  check (testFileExistsAndConsistsOfThisString (tor, 0, "hello, world!\n")); /* confirm the contents are right */
225  tr_free (tmpstr);
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->downloadDir, "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_file * files;
295  const char * strings[4];
296  const char * expected_files[4] = {
297    "Felidae/Felinae/Acinonyx/Cheetah/Chester",
298    "Felidae/Felinae/Felis/catus/Kyphi",
299    "Felidae/Felinae/Felis/catus/Saffron",
300    "Felidae/Pantherinae/Panthera/Tiger/Tony"
301  };
302  const char * expected_contents[4] = {
303   "It ain't easy bein' cheesy.\n",
304   "Inquisitive\n",
305   "Tough\n",
306   "They’re Grrrrreat!\n"
307  };
308
309  ctor = tr_ctorNew (session);
310  tor = create_torrent_from_base64_metainfo (ctor,
311    "ZDEwOmNyZWF0ZWQgYnkyNTpUcmFuc21pc3Npb24vMi42MSAoMTM0MDcpMTM6Y3JlYXRpb24gZGF0"
312    "ZWkxMzU4NTU1NDIwZTg6ZW5jb2Rpbmc1OlVURi04NDppbmZvZDU6ZmlsZXNsZDY6bGVuZ3RoaTI4"
313    "ZTQ6cGF0aGw3OkZlbGluYWU4OkFjaW5vbnl4NzpDaGVldGFoNzpDaGVzdGVyZWVkNjpsZW5ndGhp"
314    "MTJlNDpwYXRobDc6RmVsaW5hZTU6RmVsaXM1OmNhdHVzNTpLeXBoaWVlZDY6bGVuZ3RoaTZlNDpw"
315    "YXRobDc6RmVsaW5hZTU6RmVsaXM1OmNhdHVzNzpTYWZmcm9uZWVkNjpsZW5ndGhpMjFlNDpwYXRo"
316    "bDExOlBhbnRoZXJpbmFlODpQYW50aGVyYTU6VGlnZXI0OlRvbnllZWU0Om5hbWU3OkZlbGlkYWUx"
317    "MjpwaWVjZSBsZW5ndGhpMzI3NjhlNjpwaWVjZXMyMDp27buFkmy8ICfNX4nsJmt0Ckm2Ljc6cHJp"
318    "dmF0ZWkwZWVl");
319  check (tr_isTorrent (tor));
320  files = tor->info.files;
321
322  /* sanity check the info */
323  check_streq (tor->info.name, "Felidae");
324  check_int_eq (totalSize, tor->info.totalSize);
325  check_int_eq (4, tor->info.fileCount);
326  for (i=0; i<4; ++i)
327    check_streq (expected_files[i], files[i].name);
328
329  /* sanity check the (empty) stats */
330  verify_and_block_until_done (tor);
331  check_have_none (tor, totalSize);
332
333  /* build the local data */
334  create_multifile_torrent_contents (tor->downloadDir);
335
336  /* sanity check the (full) stats */
337  verify_and_block_until_done (tor);
338  check_have_all (tor, totalSize);
339
340  /**
341  ***  okay! let's test renaming.
342  **/
343
344  /* rename a leaf... */
345  check_int_eq (0, torrentRenameAndWait (tor, "Felidae/Felinae/Felis/catus/Kyphi", "placeholder"));
346  check_streq (files[1].name, "Felidae/Felinae/Felis/catus/placeholder");
347  check (testFileExistsAndConsistsOfThisString (tor, 1, "Inquisitive\n"));
348
349  /* ...and back again */
350  check_int_eq (0, torrentRenameAndWait (tor, "Felidae/Felinae/Felis/catus/placeholder", "Kyphi"));
351  check_streq (files[1].name, "Felidae/Felinae/Felis/catus/Kyphi");
352  testFileExistsAndConsistsOfThisString (tor, 1, "Inquisitive\n");
353
354  /* rename a branch... */
355  check_int_eq (0, torrentRenameAndWait (tor, "Felidae/Felinae/Felis/catus", "placeholder"));
356  check_streq (expected_files[0],                           files[0].name);
357  check_streq ("Felidae/Felinae/Felis/placeholder/Kyphi",   files[1].name);
358  check_streq ("Felidae/Felinae/Felis/placeholder/Saffron", files[2].name);
359  check_streq (expected_files[3],                           files[3].name);
360  check (testFileExistsAndConsistsOfThisString (tor, 1, expected_contents[1]));
361  check (testFileExistsAndConsistsOfThisString (tor, 2, expected_contents[2]));
362  check (files[0].is_renamed == false);
363  check (files[1].is_renamed == true);
364  check (files[2].is_renamed == true);
365  check (files[3].is_renamed == false);
366
367  /* (while the branch is renamed: confirm that the .resume file remembers the changes) */
368  tr_torrentSaveResume (tor);
369  /* this is a bit dodgy code-wise, but let's make sure the .resume file got the name */
370  tr_free (files[1].name);
371  tor->info.files[1].name = tr_strdup ("gabba gabba hey");
372  loaded = tr_torrentLoadResume (tor, ~0, ctor);
373  check ((loaded & TR_FR_FILENAMES) != 0);
374  check_streq (expected_files[0],                           files[0].name);
375  check_streq ("Felidae/Felinae/Felis/placeholder/Kyphi",   files[1].name);
376  check_streq ("Felidae/Felinae/Felis/placeholder/Saffron", files[2].name);
377  check_streq (expected_files[3],                           files[3].name);
378
379  /* ...and back again */
380  check_int_eq (0, torrentRenameAndWait (tor, "Felidae/Felinae/Felis/placeholder", "catus"));
381  for (i=0; i<4; ++i)
382    {
383      check_streq (expected_files[i], files[i].name);
384      check (testFileExistsAndConsistsOfThisString (tor, 1, expected_contents[1]));
385    }
386  check (files[0].is_renamed == false);
387  check (files[1].is_renamed == true);
388  check (files[2].is_renamed == true);
389  check (files[3].is_renamed == false);
390
391  /***
392  ****  Test it an incomplete torrent...
393  ***/
394
395  /* remove the directory Felidae/Felinae/Felis/catus */
396  str = tr_buildPath (tor->downloadDir, files[1].name, NULL);
397  remove (str);
398  tr_free (str);
399  str = tr_buildPath (tor->downloadDir, files[2].name, NULL);
400  remove (str);
401  tmp = tr_dirname (str);
402  remove (tmp);
403  tr_free (tmp);
404  tr_free (str);
405  verify_and_block_until_done (tor);
406  testFileExistsAndConsistsOfThisString (tor, 0, expected_contents[0]);
407  check (tr_torrentFindFile (tor, 1) == NULL);
408  check (tr_torrentFindFile (tor, 2) == NULL);
409  testFileExistsAndConsistsOfThisString (tor, 3, expected_contents[3]);
410
411  /* rename a branch... */
412  check_int_eq (0, torrentRenameAndWait (tor, "Felidae/Felinae/Felis/catus", "foo"));
413  check_streq (expected_files[0],                   files[0].name);
414  check_streq ("Felidae/Felinae/Felis/foo/Kyphi",   files[1].name);
415  check_streq ("Felidae/Felinae/Felis/foo/Saffron", files[2].name);
416  check_streq (expected_files[3],                   files[3].name);
417
418  /* ...and back again */
419  check_int_eq (0, torrentRenameAndWait (tor, "Felidae/Felinae/Felis/foo", "catus"));
420  for (i=0; i<4; ++i)
421    check_streq (expected_files[i], files[i].name);
422
423  check_int_eq (0, torrentRenameAndWait (tor, "Felidae", "gabba"));
424  strings[0] = "gabba/Felinae/Acinonyx/Cheetah/Chester";
425  strings[1] = "gabba/Felinae/Felis/catus/Kyphi";
426  strings[2] = "gabba/Felinae/Felis/catus/Saffron";
427  strings[3] = "gabba/Pantherinae/Panthera/Tiger/Tony";
428  for (i=0; i<4; ++i)
429    {
430      check_streq (strings[i], files[i].name);
431      testFileExistsAndConsistsOfThisString (tor, i, expected_contents[i]);
432    }
433
434  /* rename the root, then a branch, and then a leaf... */
435  check_int_eq (0, torrentRenameAndWait (tor, "gabba", "Felidae"));
436  check_int_eq (0, torrentRenameAndWait (tor, "Felidae/Pantherinae/Panthera/Tiger", "Snow Leopard"));
437  check_int_eq (0, torrentRenameAndWait (tor, "Felidae/Pantherinae/Panthera/Snow Leopard/Tony", "10.6"));
438  strings[0] = "Felidae/Felinae/Acinonyx/Cheetah/Chester";
439  strings[1] = "Felidae/Felinae/Felis/catus/Kyphi";
440  strings[2] = "Felidae/Felinae/Felis/catus/Saffron";
441  strings[3] = "Felidae/Pantherinae/Panthera/Snow Leopard/10.6";
442  for (i=0; i<4; ++i)
443    {
444      check_streq (strings[i], files[i].name);
445      testFileExistsAndConsistsOfThisString (tor, i, expected_contents[i]);
446    }
447
448  /***
449  ****
450  ***/
451
452  /* cleanup */
453  tr_ctorFree (ctor);
454  tr_torrentRemove (tor, false, NULL);
455  return 0;
456}
457
458/***
459****
460***/
461
462static void
463create_zero_torrent_partial_contents (tr_torrent * tor, bool incomplete)
464{
465  tr_file_index_t i;
466  const tr_stat * st;
467
468  for (i=0; i<tor->info.fileCount; ++i)
469    {
470      uint64_t j;
471      FILE * fp;
472      char * path;
473      char * dirname;
474      const tr_file * file = &tor->info.files[i];
475
476      path = tr_buildPath (tor->downloadDir, file->name, NULL);
477      dirname = tr_dirname (path);
478      tr_mkdirp (dirname, 0700);
479      fp = fopen (path, "wb+");
480      for (j=0; j<file->length; ++j)
481        fputc ('\0', fp);
482      fclose (fp);
483      sync ();
484
485      tr_free (dirname);
486      tr_free (path);
487    }
488
489  verify_and_block_until_done (tor);
490  st = tr_torrentStat (tor);
491  assert (st->leftUntilDone == 0);
492
493  if (incomplete)
494    {
495      FILE * fp;
496      char * oldpath = tr_torrentFindFile (tor, 0);
497      char * newpath = tr_strdup_printf ("%s.part", oldpath);
498
499      rename (oldpath, newpath);
500
501      /* invalidate one piece */
502      fp = fopen (newpath, "rb+");
503      fputc ('\1', fp);
504      fclose (fp);
505      sync ();
506
507      tr_free (newpath);
508      tr_free (oldpath);
509
510      verify_and_block_until_done (tor);
511      st = tr_torrentStat (tor);
512      assert (st->leftUntilDone == tor->info.pieceSize);
513    }
514}
515
516static int
517test_partial_file (void)
518{
519  tr_file_index_t i;
520  tr_torrent * tor;
521  const tr_stat * st;
522  tr_file_stat * fst;
523  const uint32_t pieceCount = 33;
524  const uint32_t pieceSize = 32768;
525  const uint32_t length[] = { 1048576, 4096, 512 };
526  const uint64_t totalSize = length[0] + length[1] + length[2];
527  const char * strings[3];
528
529  /***
530  ****  create our test torrent with an incomplete .part file
531  ***/
532
533  tor = libtransmission_test_zero_torrent_init ();
534  check_int_eq (totalSize, tor->info.totalSize);
535  check_int_eq (pieceSize, tor->info.pieceSize);
536  check_int_eq (pieceCount, tor->info.pieceCount);
537  check_streq ("files-filled-with-zeroes/1048576", tor->info.files[0].name);
538  check_streq ("files-filled-with-zeroes/4096",    tor->info.files[1].name);
539  check_streq ("files-filled-with-zeroes/512",     tor->info.files[2].name);
540
541  create_zero_torrent_partial_contents (tor, true);
542  fst = tr_torrentFiles (tor, NULL);
543  check_int_eq (length[0] - pieceSize, fst[0].bytesCompleted);
544  check_int_eq (length[1],             fst[1].bytesCompleted);
545  check_int_eq (length[2],             fst[2].bytesCompleted);
546  tr_torrentFilesFree (fst, tor->info.fileCount);
547  st = tr_torrentStat (tor);
548  check_int_eq (totalSize, st->sizeWhenDone);
549  check_int_eq (pieceSize, st->leftUntilDone);
550
551  /***
552  ****
553  ***/
554
555  check_int_eq (0, torrentRenameAndWait (tor, "files-filled-with-zeroes", "foo"));
556  check_int_eq (0, torrentRenameAndWait (tor, "foo/1048576", "bar"));
557  strings[0] = "foo/bar";
558  strings[1] = "foo/4096";
559  strings[2] = "foo/512";
560  for (i=0; i<3; ++i)
561    {
562      check_streq (strings[i], tor->info.files[i].name);
563    }
564
565  strings[0] = "foo/bar.part";
566  for (i=0; i<3; ++i)
567    {
568      char * expected = tr_buildPath (tor->downloadDir, strings[i], NULL);
569      char * path = tr_torrentFindFile (tor, i);
570      check_streq (expected, path);
571      tr_free (path);
572      tr_free (expected);
573    }
574
575  tr_torrentRemove (tor, false, NULL);
576  return 0;
577}
578
579/***
580****
581***/
582
583int
584main (void)
585{
586  int ret;
587  const testFunc tests[] = { //test_single_filename_torrent,
588                             //test_multifile_torrent,
589                             test_partial_file };
590
591  verbose = 1;
592
593  libtransmission_test_session_init ();
594  ret = runTests (tests, NUM_TESTS (tests));
595  libtransmission_test_session_close ();
596
597  return ret;
598}
Note: See TracBrowser for help on using the repository browser.