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

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

separate out the sandbox code so that test sandboxes can be created without a tr_session

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