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

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

(libT) add to rename-tests

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 <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    tr_torrentVerify (tor); \
26    do { \
27      tr_wait_msec (10); \
28    } while (tor->verifyState != TR_VERIFY_NONE); \
29  } while (0)
30
31#define check_have_none(tor, totalSize) \
32  do { \
33    const tr_stat * st = tr_torrentStat(tor); \
34    check_int_eq (TR_STATUS_STOPPED, st->activity); \
35    check_int_eq (TR_STAT_OK, st->error); \
36    check_int_eq (totalSize, st->sizeWhenDone); \
37    check_int_eq (totalSize, st->leftUntilDone); \
38    check_int_eq (totalSize, tor->info.totalSize); \
39    check_int_eq (0, st->haveValid); \
40  } while (0)
41
42static bool
43testFileExistsAndConsistsOfThisString (const tr_torrent * tor, tr_file_index_t fileIndex, const char * str)
44{
45  char * path;
46  const size_t str_len = strlen (str);
47  bool success = false;
48
49  path = tr_torrentFindFile (tor, fileIndex);
50  if (path != NULL)
51    {
52      uint8_t * contents;
53      size_t contents_len;
54
55      assert (tr_fileExists (path, NULL));
56
57      contents = tr_loadFile (path, &contents_len);
58
59      success = (str_len == contents_len)
60             && (!memcmp (contents, str, contents_len));
61
62      tr_free (contents);
63      tr_free (path);
64    }
65
66  return success;
67}
68
69static void
70onRenameDone (tr_torrent * tor UNUSED, const char * oldpath UNUSED, const char * newname UNUSED, int error, void * user_data)
71{
72  *(int*)user_data = error;
73}
74
75static int
76torrentRenameAndWait (tr_torrent * tor,
77                      const char * oldpath,
78                      const char * newname)
79{
80  int error = -1;
81  tr_torrentRenamePath (tor, oldpath, newname, onRenameDone, &error);
82  do {
83    tr_wait_msec (10);
84  } while (error == -1);
85  return error;
86}
87
88/***
89****
90***/
91
92static void
93create_file_with_contents (const char * path, const char * str)
94{
95  int rv;
96  FILE * fp;
97  char * dir;
98  const int tmperr = errno;
99
100  dir = tr_dirname (path);
101  errno = 0;
102  rv = tr_mkdirp (dir, 0700);
103  assert (errno == 0);
104  assert (rv == 0);
105  tr_free (dir);
106
107  remove (path);
108  fp = fopen (path, "wb");
109  fprintf (fp, "%s", str);
110  fclose (fp);
111
112  sync ();
113
114  errno = tmperr;
115}
116
117static void
118create_single_file_torrent_contents (const char * top)
119{
120  char * path = tr_buildPath (top, "hello-world.txt", NULL);
121  create_file_with_contents (path, "hello, world!\n");
122  tr_free (path);
123}
124
125static tr_torrent *
126create_torrent_from_base64_metainfo (tr_ctor * ctor, const char * metainfo_base64)
127{
128  int err;
129  int metainfo_len;
130  char * metainfo;
131  tr_torrent * tor;
132
133  /* create the torrent ctor */
134  metainfo = tr_base64_decode (metainfo_base64, -1, &metainfo_len);
135  assert (metainfo != NULL);
136  assert (metainfo_len > 0);
137  assert (session != NULL);
138  tr_ctorSetMetainfo (ctor, (uint8_t*)metainfo, metainfo_len);
139  tr_ctorSetPaused (ctor, TR_FORCE, true);
140
141  /* create the torrent */
142  err = 0;
143  tor = tr_torrentNew (ctor, &err);
144  assert (!err);
145
146  /* cleanup */
147  tr_free (metainfo); 
148  return tor;
149}
150
151static int
152test_single_filename_torrent (void)
153{
154  uint64_t loaded;
155  tr_torrent * tor;
156  char * tmpstr;
157  const size_t totalSize = 14;
158  tr_ctor * ctor;
159  const tr_stat * st;
160
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));
169
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);
174
175  /* sanity check the (empty) stats */
176  verify_and_block_until_done (tor);
177  check_have_none (tor, totalSize);
178
179  create_single_file_torrent_contents (tor->currentDir);
180
181  /* sanity check the stats again, now that we've added the file */
182  verify_and_block_until_done (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);
191
192  /**
193  ***  okay! we've finally put together all the scaffolding to test
194  ***  renaming a single-file torrent
195  **/
196
197  /* confirm that bad inputs get caught */
198
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"));
205
206  check (!tor->info.files[0].is_renamed);
207  check_streq ("hello-world.txt", tor->info.files[0].name);
208
209  /***
210  ****  Now try a rename that should succeed
211  ***/
212
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 */
218  tr_free (tmpstr);
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 */
225  tr_free (tmpstr);
226  check (testFileExistsAndConsistsOfThisString (tor, 0, "hello, world!\n")); /* confirm the contents are right */
227
228  /* (while it's renamed: confirm that the .resume file remembers the changes) */
229  tr_torrentSaveResume (tor);
230  sync ();
231  loaded = tr_torrentLoadResume (tor, ~0, ctor);
232  check_streq ("foobar", tr_torrentName(tor));
233  check ((loaded & TR_FR_NAME) != 0);
234
235  /***
236  ****  ...and rename it back again
237  ***/
238
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));
246  tr_free (tmpstr);
247  check (testFileExistsAndConsistsOfThisString (tor, 0, "hello, world!\n"));
248
249  /* cleanup */
250  tr_ctorFree (ctor);
251  tr_torrentRemove (tor, false, NULL);
252  return 0;
253}
254
255/***
256****
257****
258****
259***/
260
261static void
262create_multifile_torrent_contents (const char * top)
263{
264  char * path;
265
266  path = tr_buildPath (top, "Felidae", "Felinae", "Acinonyx", "Cheetah", "Chester", NULL);
267  create_file_with_contents (path, "It ain't easy bein' cheesy.\n");
268  tr_free (path);
269
270  path = tr_buildPath (top, "Felidae", "Pantherinae", "Panthera", "Tiger", "Tony", NULL);
271  create_file_with_contents (path, "They’re Grrrrreat!\n");
272  tr_free (path);
273
274  path = tr_buildPath (top, "Felidae", "Felinae", "Felis", "catus", "Kyphi", NULL);
275  create_file_with_contents (path, "Inquisitive\n");
276  tr_free (path);
277
278  path = tr_buildPath (top, "Felidae", "Felinae", "Felis", "catus", "Saffron", NULL);
279  create_file_with_contents (path, "Tough\n");
280  tr_free (path);
281
282  sync ();
283}
284
285static int
286test_multifile_torrent (void)
287{
288  tr_file_index_t i;
289  uint64_t loaded;
290  tr_torrent * tor;
291  tr_ctor * ctor;
292  char * str;
293  char * tmp;
294  static const size_t totalSize = 67;
295  const tr_stat * st;
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"
303  };
304  const char * expected_contents[4] = {
305   "It ain't easy bein' cheesy.\n",
306   "Inquisitive\n",
307   "Tough\n",
308   "They’re Grrrrreat!\n"
309  };
310
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"
320    "dmF0ZWkwZWVl");
321  check (tr_isTorrent (tor));
322  files = tor->info.files;
323
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);
328  for (i=0; i<4; ++i)
329    check_streq (expected_files[i], files[i].name);
330
331  /* sanity check the (empty) stats */
332  verify_and_block_until_done (tor);
333  check_have_none (tor, totalSize);
334
335  /* build the local data */
336  create_multifile_torrent_contents (tor->currentDir);
337
338  /* sanity check the (full) stats */
339  verify_and_block_until_done (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);
348
349
350  /**
351  ***  okay! let's test renaming.
352  **/
353
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"));
358
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");
363
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);
376
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);
388
389  /* ...and back again */
390  check_int_eq (0, torrentRenameAndWait (tor, "Felidae/Felinae/Felis/placeholder", "catus"));
391  for (i=0; i<4; ++i)
392    {
393      check_streq (expected_files[i], files[i].name);
394      check (testFileExistsAndConsistsOfThisString (tor, 1, expected_contents[1]));
395    }
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);
400
401  /***
402  ****  Test it an incomplete torrent...
403  ***/
404
405  /* remove the directory Felidae/Felinae/Felis/catus */
406  str = tr_buildPath (tor->currentDir, files[1].name, NULL);
407  remove (str);
408  tr_free (str);
409  str = tr_buildPath (tor->currentDir, files[2].name, NULL);
410  remove (str);
411  tmp = tr_dirname (str);
412  remove (tmp);
413  tr_free (tmp);
414  tr_free (str);
415  verify_and_block_until_done (tor);
416  testFileExistsAndConsistsOfThisString (tor, 0, expected_contents[0]);
417  check (tr_torrentFindFile (tor, 1) == NULL);
418  check (tr_torrentFindFile (tor, 2) == NULL);
419  testFileExistsAndConsistsOfThisString (tor, 3, expected_contents[3]);
420
421  /* rename a branch... */
422  check_int_eq (0, torrentRenameAndWait (tor, "Felidae/Felinae/Felis/catus", "foo"));
423  check_streq (expected_files[0],                   files[0].name);
424  check_streq ("Felidae/Felinae/Felis/foo/Kyphi",   files[1].name);
425  check_streq ("Felidae/Felinae/Felis/foo/Saffron", files[2].name);
426  check_streq (expected_files[3],                   files[3].name);
427
428  /* ...and back again */
429  check_int_eq (0, torrentRenameAndWait (tor, "Felidae/Felinae/Felis/foo", "catus"));
430  for (i=0; i<4; ++i)
431    check_streq (expected_files[i], files[i].name);
432
433  check_int_eq (0, torrentRenameAndWait (tor, "Felidae", "gabba"));
434  strings[0] = "gabba/Felinae/Acinonyx/Cheetah/Chester";
435  strings[1] = "gabba/Felinae/Felis/catus/Kyphi";
436  strings[2] = "gabba/Felinae/Felis/catus/Saffron";
437  strings[3] = "gabba/Pantherinae/Panthera/Tiger/Tony";
438  for (i=0; i<4; ++i)
439    {
440      check_streq (strings[i], files[i].name);
441      testFileExistsAndConsistsOfThisString (tor, i, expected_contents[i]);
442    }
443
444  /* rename the root, then a branch, and then a leaf... */
445  check_int_eq (0, torrentRenameAndWait (tor, "gabba", "Felidae"));
446  check_int_eq (0, torrentRenameAndWait (tor, "Felidae/Pantherinae/Panthera/Tiger", "Snow Leopard"));
447  check_int_eq (0, torrentRenameAndWait (tor, "Felidae/Pantherinae/Panthera/Snow Leopard/Tony", "10.6"));
448  strings[0] = "Felidae/Felinae/Acinonyx/Cheetah/Chester";
449  strings[1] = "Felidae/Felinae/Felis/catus/Kyphi";
450  strings[2] = "Felidae/Felinae/Felis/catus/Saffron";
451  strings[3] = "Felidae/Pantherinae/Panthera/Snow Leopard/10.6";
452  for (i=0; i<4; ++i)
453    {
454      check_streq (strings[i], files[i].name);
455      testFileExistsAndConsistsOfThisString (tor, i, expected_contents[i]);
456    }
457
458  /***
459  ****
460  ***/
461
462  /* cleanup */
463  tr_ctorFree (ctor);
464  tr_torrentRemove (tor, false, NULL);
465  return 0;
466}
467
468/***
469****
470***/
471
472static int
473test_partial_file (void)
474{
475  tr_file_index_t i;
476  tr_torrent * tor;
477  const tr_stat * st;
478  tr_file_stat * fst;
479  const uint32_t pieceCount = 33;
480  const uint32_t pieceSize = 32768;
481  const uint32_t length[] = { 1048576, 4096, 512 };
482  const uint64_t totalSize = length[0] + length[1] + length[2];
483  const char * strings[3];
484
485  /***
486  ****  create our test torrent with an incomplete .part file
487  ***/
488
489  tor = libtransmission_test_zero_torrent_init ();
490  check_int_eq (totalSize, tor->info.totalSize);
491  check_int_eq (pieceSize, tor->info.pieceSize);
492  check_int_eq (pieceCount, tor->info.pieceCount);
493  check_streq ("files-filled-with-zeroes/1048576", tor->info.files[0].name);
494  check_streq ("files-filled-with-zeroes/4096",    tor->info.files[1].name);
495  check_streq ("files-filled-with-zeroes/512",     tor->info.files[2].name);
496
497  libtransmission_test_zero_torrent_populate (tor, false);
498  fst = tr_torrentFiles (tor, NULL);
499  check_int_eq (length[0] - pieceSize, fst[0].bytesCompleted);
500  check_int_eq (length[1],             fst[1].bytesCompleted);
501  check_int_eq (length[2],             fst[2].bytesCompleted);
502  tr_torrentFilesFree (fst, tor->info.fileCount);
503  st = tr_torrentStat (tor);
504  check_int_eq (totalSize, st->sizeWhenDone);
505  check_int_eq (pieceSize, st->leftUntilDone);
506
507  /***
508  ****
509  ***/
510
511  check_int_eq (0, torrentRenameAndWait (tor, "files-filled-with-zeroes", "foo"));
512  check_int_eq (0, torrentRenameAndWait (tor, "foo/1048576", "bar"));
513  strings[0] = "foo/bar";
514  strings[1] = "foo/4096";
515  strings[2] = "foo/512";
516  for (i=0; i<3; ++i)
517    {
518      check_streq (strings[i], tor->info.files[i].name);
519    }
520
521  strings[0] = "foo/bar.part";
522  for (i=0; i<3; ++i)
523    {
524      char * expected = tr_buildPath (tor->currentDir, strings[i], NULL);
525      char * path = tr_torrentFindFile (tor, i);
526      check_streq (expected, path);
527      tr_free (path);
528      tr_free (expected);
529    }
530
531  tr_torrentRemove (tor, false, NULL);
532  return 0;
533}
534
535/***
536****
537***/
538
539int
540main (void)
541{
542  int ret;
543  const testFunc tests[] = { test_single_filename_torrent,
544                             test_multifile_torrent,
545                             test_partial_file };
546
547  libtransmission_test_session_init ();
548  ret = runTests (tests, NUM_TESTS (tests));
549  libtransmission_test_session_close ();
550
551  return ret;
552}
Note: See TracBrowser for help on using the repository browser.