/** @file
  Tests handling of filetype changes.
*/

#include "backup.h"

#include "test.h"
#include "test-common.h"
#include "backup-common.h"
#include "safe-wrappers.h"
#include "backup-dummy-hashes.h"

/** Prepares replacing a directory with a file/symlink. */
static void initNoneFiletypeChange(SearchNode *none_filetype_node)
{
  /* Generate various dummy files. */
  resetStatCache();
  assertTmpIsCleared();
  makeDir("tmp/files/a");
  makeDir("tmp/files/a/b");
  makeDir("tmp/files/a/b/2");
  makeDir("tmp/files/a/d");
  makeDir("tmp/files/e");
  makeDir("tmp/files/e/f");
  makeDir("tmp/files/e/f/g");
  generateFile("tmp/files/a/b/1",   "foo bar", 1);
  generateFile("tmp/files/a/b/2/1", "Foo",     6);
  generateFile("tmp/files/a/c",     "nested ", 8);
  generateFile("tmp/files/a/d/1",   "BAR",     4);
  generateFile("tmp/files/e/f/h",   "Large\n", 200);
  makeSymlink("non-existing.txt", "tmp/files/e/f/i");

  /* Initiate the backup. */
  Metadata *metadata = metadataNew();
  initiateBackup(metadata, none_filetype_node);

  /* Check the initiated backup. */
  checkMetadata(metadata, 0, false);
  assert_true(metadata->current_backup.ref_count == cwd_depth() + 15);
  assert_true(metadata->backup_history_length == 0);
  assert_true(metadata->total_path_count == cwd_depth() + 15);

  PathNode *files = findFilesNode(metadata, BH_added, 2);

  PathNode *a = findSubnode(files, "a", BH_added, BPOL_none, 1, 3);
  mustHaveDirectoryCached(a, &metadata->current_backup);
  PathNode *b = findSubnode(a, "b", BH_added, BPOL_track, 1, 2);
  mustHaveDirectoryCached(b, &metadata->current_backup);
  PathNode *b_1 = findSubnode(b, "1", BH_added, BPOL_track, 1, 0);
  mustHaveRegularCached(b_1, &metadata->current_backup, 7, NULL, 0);
  PathNode *b_2 = findSubnode(b, "2", BH_added, BPOL_copy, 1, 1);
  mustHaveDirectoryCached(b_2, &metadata->current_backup);
  PathNode *b_2_1 = findSubnode(b_2, "1", BH_added, BPOL_track, 1, 0);
  mustHaveRegularCached(b_2_1, &metadata->current_backup, 18, NULL, 0);
  PathNode *c = findSubnode(a, "c", BH_added, BPOL_copy, 1, 0);
  mustHaveRegularCached(c, &metadata->current_backup, 56, NULL, 0);
  PathNode *d = findSubnode(a, "d", BH_added, BPOL_mirror, 1, 1);
  mustHaveDirectoryCached(d, &metadata->current_backup);
  PathNode *d_1 = findSubnode(d, "1", BH_added, BPOL_mirror, 1, 0);
  mustHaveRegularCached(d_1, &metadata->current_backup, 12, NULL, 0);

  PathNode *e = findSubnode(files, "e", BH_added, BPOL_none, 1, 1);
  mustHaveDirectoryCached(e, &metadata->current_backup);
  PathNode *f = findSubnode(e, "f", BH_added, BPOL_none, 1, 3);
  mustHaveDirectoryCached(f, &metadata->current_backup);
  PathNode *g = findSubnode(f, "g", BH_added, BPOL_track, 1, 0);
  mustHaveDirectoryCached(g, &metadata->current_backup);
  PathNode *h = findSubnode(f, "h", BH_added, BPOL_mirror, 1, 0);
  mustHaveRegularCached(h, &metadata->current_backup, 1200, NULL, 0);
  PathNode *i = findSubnode(f, "i", BH_added, BPOL_copy, 1, 0);
  mustHaveSymlinkLCached(i, &metadata->current_backup, "non-existing.txt");

  /* Finish the backup and perform additional checks. */
  completeBackup(metadata);
  assert_true(countItemsInDir("tmp/repo") == 7);
  mustHaveRegularCached(b_1,   &metadata->current_backup, 7,    (uint8_t *)"foo bar",            0);
  mustHaveRegularCached(b_2_1, &metadata->current_backup, 18,   (uint8_t *)"FooFooFooFooFooFoo", 0);
  mustHaveRegularCached(c,     &metadata->current_backup, 56,   nested_2_hash,                   0);
  mustHaveRegularCached(d_1,   &metadata->current_backup, 12,   (uint8_t *)"BARBARBARBAR",       0);
  mustHaveRegularCached(h,     &metadata->current_backup, 1200, data_d_hash,                     0);
}

/** Removes "tmp/files/a" generated by initNoneFiletypeChange(). */
static void removeNoneFiletypeA(void)
{
  removePath("tmp/files/a/d/1");
  removePath("tmp/files/a/d");
  removePath("tmp/files/a/c");
  removePath("tmp/files/a/b/2/1");
  removePath("tmp/files/a/b/2");
  removePath("tmp/files/a/b/1");
  removePath("tmp/files/a/b");
  removePath("tmp/files/a");
}

/** Replaces a directory with a regular file and modifies the current
  metadata. */
static void change1NoneFiletypeChange(SearchNode *none_filetype_node)
{
  /* Replace directory with regular file. */
  removeNoneFiletypeA();
  generateFile("tmp/files/a", "a/b/c/", 7);
  removePath("tmp/files/e/f/g");

  /* Initiate the backup. */
  Metadata *metadata = metadataLoad(strWrap("tmp/repo/metadata"));
  assert_true(metadata->total_path_count == cwd_depth() + 15);
  checkHistPoint(metadata, 0, 0, phase_timestamps(backup_counter() - 1), cwd_depth() + 15);
  initiateBackup(metadata, none_filetype_node);

  /* Check the initiated backup. */
  checkMetadata(metadata, 0, true);
  assert_true(metadata->current_backup.ref_count == cwd_depth() + 6);
  assert_true(metadata->backup_history_length == 1);
  assert_true(metadata->total_path_count == cwd_depth() + 15);
  checkHistPoint(metadata, 0, 0, phase_timestamps(backup_counter() - 1), 10);

  PathNode *files = findFilesNode(metadata, BH_unchanged, 2);

  PathNode *a = findSubnode(files, "a", BH_directory_to_regular, BPOL_none, 1, 3);
  mustHaveDirectoryCached(a, &metadata->current_backup);
  PathNode *b = findSubnode(a, "b", BH_removed, BPOL_track, 1, 2);
  mustHaveDirectoryCached(b, &metadata->backup_history[0]);
  PathNode *b_1 = findSubnode(b, "1", BH_removed, BPOL_track, 1, 0);
  mustHaveRegularCached(b_1, &metadata->backup_history[0], 7, (uint8_t *)"foo bar", 0);
  PathNode *b_2 = findSubnode(b, "2", BH_removed, BPOL_copy, 1, 1);
  mustHaveDirectoryCached(b_2, &metadata->backup_history[0]);
  PathNode *b_2_1 = findSubnode(b_2, "1", BH_removed, BPOL_track, 1, 0);
  mustHaveRegularCached(b_2_1, &metadata->backup_history[0], 18, (uint8_t *)"FooFooFooFooFooFoo", 0);
  PathNode *c = findSubnode(a, "c", BH_removed, BPOL_copy, 1, 0);
  mustHaveRegularCached(c, &metadata->backup_history[0], 56, nested_2_hash, 0);
  PathNode *d = findSubnode(a, "d", BH_removed, BPOL_mirror, 1, 1);
  mustHaveDirectoryCached(d, &metadata->backup_history[0]);
  PathNode *d_1 = findSubnode(d, "1", BH_removed, BPOL_mirror, 1, 0);
  mustHaveRegularCached(d_1, &metadata->backup_history[0], 12, (uint8_t *)"BARBARBARBAR", 0);

  PathNode *e = findSubnode(files, "e", BH_unchanged, BPOL_none, 1, 1);
  mustHaveDirectoryCached(e, &metadata->current_backup);
  PathNode *f = findSubnode(e, "f", BH_unchanged, BPOL_none, 1, 3);
  mustHaveDirectoryCached(f, &metadata->current_backup);
  PathNode *g = findSubnode(f, "g", BH_removed, BPOL_track, 2, 0);
  mustHaveNonExisting(g, &metadata->current_backup);
  mustHaveDirectoryCached(g, &metadata->backup_history[0]);
  PathNode *h = findSubnode(f, "h", BH_unchanged, BPOL_mirror, 1, 0);
  mustHaveRegularCached(h, &metadata->backup_history[0], 1200, data_d_hash, 0);
  PathNode *i = findSubnode(f, "i", BH_unchanged, BPOL_copy, 1, 0);
  mustHaveSymlinkLCached(i, &metadata->backup_history[0], "non-existing.txt");

  /* Modify various path nodes. */
  e->history->state.uid++;
  e->history->state.metadata.dir.timestamp++;

  /* Finish the backup and perform additional checks. */
  completeBackup(metadata);
  assert_true(countItemsInDir("tmp/repo") == 7);
}

/** Like change1NoneFiletypeChange(), but replaces a directory with a
  symlink to a regular file. */
static void change2NoneFiletypeChange(SearchNode *none_filetype_node)
{
  /* Replace directory with symlink to regular file. */
  removePath("tmp/files/e/f/h");
  removePath("tmp/files/e/f/i");
  removePath("tmp/files/e/f");
  removePath("tmp/files/e");
  makeSymlink("a", "tmp/files/e");

  /* Initiate the backup. */
  Metadata *metadata = metadataLoad(strWrap("tmp/repo/metadata"));
  assert_true(metadata->total_path_count == cwd_depth() + 15);
  checkHistPoint(metadata, 0, 0, phase_timestamps(backup_counter() - 1), cwd_depth() + 6);
  checkHistPoint(metadata, 1, 1, phase_timestamps(backup_counter() - 2), 10);
  initiateBackup(metadata, none_filetype_node);

  /* Check the initiated backup. */
  checkMetadata(metadata, 0, true);
  assert_true(metadata->current_backup.ref_count == cwd_depth() + 4);
  assert_true(metadata->backup_history_length == 2);
  assert_true(metadata->total_path_count == cwd_depth() + 15);
  checkHistPoint(metadata, 0, 0, phase_timestamps(backup_counter() - 1), 2);
  checkHistPoint(metadata, 1, 1, phase_timestamps(backup_counter() - 2), 10);

  PathNode *files = findFilesNode(metadata, BH_unchanged, 2);

  PathNode *a = findSubnode(files, "a", BH_directory_to_regular, BPOL_none, 1, 3);
  mustHaveDirectoryCached(a, &metadata->current_backup);
  PathNode *b = findSubnode(a, "b", BH_removed, BPOL_track, 1, 2);
  mustHaveDirectoryCached(b, &metadata->backup_history[1]);
  PathNode *b_1 = findSubnode(b, "1", BH_removed, BPOL_track, 1, 0);
  mustHaveRegularCached(b_1, &metadata->backup_history[1], 7, (uint8_t *)"foo bar", 0);
  PathNode *b_2 = findSubnode(b, "2", BH_removed, BPOL_copy, 1, 1);
  mustHaveDirectoryCached(b_2, &metadata->backup_history[1]);
  PathNode *b_2_1 = findSubnode(b_2, "1", BH_removed, BPOL_track, 1, 0);
  mustHaveRegularCached(b_2_1, &metadata->backup_history[1], 18, (uint8_t *)"FooFooFooFooFooFoo", 0);
  PathNode *c = findSubnode(a, "c", BH_removed, BPOL_copy, 1, 0);
  mustHaveRegularCached(c, &metadata->backup_history[1], 56, nested_2_hash, 0);
  PathNode *d = findSubnode(a, "d", BH_removed, BPOL_mirror, 1, 1);
  mustHaveDirectoryCached(d, &metadata->backup_history[1]);
  PathNode *d_1 = findSubnode(d, "1", BH_removed, BPOL_mirror, 1, 0);
  mustHaveRegularCached(d_1, &metadata->backup_history[1], 12, (uint8_t *)"BARBARBARBAR", 0);

  PathNode *e = findSubnode(files, "e", BH_directory_to_regular, BPOL_none, 1, 1);
  struct stat e_stats = cachedStat(e->path, sStat);
  e_stats.st_uid++;
  e_stats.st_mtime++;
  mustHaveDirectoryStats(e, &metadata->current_backup, e_stats);
  PathNode *f = findSubnode(e, "f", BH_removed, BPOL_none, 1, 3);
  mustHaveDirectoryCached(f, &metadata->backup_history[0]);
  PathNode *g = findSubnode(f, "g", BH_unchanged, BPOL_track, 2, 0);
  mustHaveNonExisting(g, &metadata->backup_history[0]);
  mustHaveDirectoryCached(g, &metadata->backup_history[1]);
  PathNode *h = findSubnode(f, "h", BH_removed, BPOL_mirror, 1, 0);
  mustHaveRegularCached(h, &metadata->backup_history[1], 1200, data_d_hash, 0);
  PathNode *i = findSubnode(f, "i", BH_removed, BPOL_copy, 1, 0);
  mustHaveSymlinkLCached(i, &metadata->backup_history[1], "non-existing.txt");

  /* Finish the backup and perform additional checks. */
  completeBackup(metadata);
  assert_true(countItemsInDir("tmp/repo") == 7);
}

/** Tests the metadata written by change2NoneFiletypeChange(). */
static void postNoneFiletypeChange(SearchNode *none_filetype_node)
{
  /* Initiate the backup. */
  Metadata *metadata = metadataLoad(strWrap("tmp/repo/metadata"));
  assert_true(metadata->total_path_count == cwd_depth() + 15);
  checkHistPoint(metadata, 0, 0, phase_timestamps(backup_counter() - 1), cwd_depth() + 4);
  checkHistPoint(metadata, 1, 1, phase_timestamps(backup_counter() - 2), 2);
  checkHistPoint(metadata, 2, 2, phase_timestamps(backup_counter() - 3), 10);
  initiateBackup(metadata, none_filetype_node);

  /* Check the initiated backup. */
  checkMetadata(metadata, 0, true);
  assert_true(metadata->current_backup.ref_count == cwd_depth() + 4);
  assert_true(metadata->backup_history_length == 3);
  assert_true(metadata->total_path_count == cwd_depth() + 15);
  checkHistPoint(metadata, 0, 0, phase_timestamps(backup_counter() - 1), 0);
  checkHistPoint(metadata, 1, 1, phase_timestamps(backup_counter() - 2), 2);
  checkHistPoint(metadata, 2, 2, phase_timestamps(backup_counter() - 3), 10);

  PathNode *files = findFilesNode(metadata, BH_unchanged, 2);

  PathNode *a = findSubnode(files, "a", BH_directory_to_regular, BPOL_none, 1, 3);
  mustHaveDirectoryCached(a, &metadata->current_backup);
  PathNode *b = findSubnode(a, "b", BH_removed, BPOL_track, 1, 2);
  mustHaveDirectoryCached(b, &metadata->backup_history[2]);
  PathNode *b_1 = findSubnode(b, "1", BH_removed, BPOL_track, 1, 0);
  mustHaveRegularCached(b_1, &metadata->backup_history[2], 7, (uint8_t *)"foo bar", 0);
  PathNode *b_2 = findSubnode(b, "2", BH_removed, BPOL_copy, 1, 1);
  mustHaveDirectoryCached(b_2, &metadata->backup_history[2]);
  PathNode *b_2_1 = findSubnode(b_2, "1", BH_removed, BPOL_track, 1, 0);
  mustHaveRegularCached(b_2_1, &metadata->backup_history[2], 18, (uint8_t *)"FooFooFooFooFooFoo", 0);
  PathNode *c = findSubnode(a, "c", BH_removed, BPOL_copy, 1, 0);
  mustHaveRegularCached(c, &metadata->backup_history[2], 56, nested_2_hash, 0);
  PathNode *d = findSubnode(a, "d", BH_removed, BPOL_mirror, 1, 1);
  mustHaveDirectoryCached(d, &metadata->backup_history[2]);
  PathNode *d_1 = findSubnode(d, "1", BH_removed, BPOL_mirror, 1, 0);
  mustHaveRegularCached(d_1, &metadata->backup_history[2], 12, (uint8_t *)"BARBARBARBAR", 0);

  PathNode *e = findSubnode(files, "e", BH_directory_to_regular, BPOL_none, 1, 1);
  struct stat e_stats = cachedStat(e->path, sStat);
  e_stats.st_uid++;
  e_stats.st_mtime++;
  mustHaveDirectoryStats(e, &metadata->current_backup, e_stats);
  PathNode *f = findSubnode(e, "f", BH_removed, BPOL_none, 1, 3);
  mustHaveDirectoryCached(f, &metadata->backup_history[1]);
  PathNode *g = findSubnode(f, "g", BH_unchanged, BPOL_track, 2, 0);
  mustHaveNonExisting(g, &metadata->backup_history[1]);
  mustHaveDirectoryCached(g, &metadata->backup_history[2]);
  PathNode *h = findSubnode(f, "h", BH_removed, BPOL_mirror, 1, 0);
  mustHaveRegularCached(h, &metadata->backup_history[2], 1200, data_d_hash, 0);
  PathNode *i = findSubnode(f, "i", BH_removed, BPOL_copy, 1, 0);
  mustHaveSymlinkLCached(i, &metadata->backup_history[2], "non-existing.txt");

  /* Modify various path nodes. */
  e->history->state.uid--;
  e->history->state.metadata.dir.timestamp--;

  /* Finish the backup and perform additional checks. */
  completeBackup(metadata);
  assert_true(countItemsInDir("tmp/repo") == 7);
}

/** Restores test files to their initial state and cleans up. */
static void restoreNoneFiletypeChange(SearchNode *none_filetype_node)
{
  /* Load the metadata. */
  Metadata *metadata = metadataLoad(strWrap("tmp/repo/metadata"));
  assert_true(metadata->total_path_count == cwd_depth() + 15);
  checkHistPoint(metadata, 0, 0, phase_timestamps(backup_counter() - 1), cwd_depth() + 4);
  checkHistPoint(metadata, 1, 1, phase_timestamps(backup_counter() - 3), 2);
  checkHistPoint(metadata, 2, 2, phase_timestamps(backup_counter() - 4), 10);

  /* Restore all files and initiate the backup. */
  removePath("tmp/files/a");
  removePath("tmp/files/e");
  restoreWithTimeRecursively(metadata->paths);
  initiateBackup(metadata, none_filetype_node);

  /* Check the initiated backup. */
  checkMetadata(metadata, 0, true);
  assert_true(metadata->current_backup.ref_count == cwd_depth() + 6);
  assert_true(metadata->backup_history_length == 3);
  assert_true(metadata->total_path_count == cwd_depth() + 15);
  checkHistPoint(metadata, 0, 0, phase_timestamps(backup_counter() - 1), 0);
  checkHistPoint(metadata, 1, 1, phase_timestamps(backup_counter() - 3), 1);
  checkHistPoint(metadata, 2, 2, phase_timestamps(backup_counter() - 4), 10);

  PathNode *files = findFilesNode(metadata, BH_unchanged, 2);

  PathNode *a = findSubnode(files, "a", BH_unchanged, BPOL_none, 1, 3);
  mustHaveDirectoryStat(a, &metadata->current_backup);
  PathNode *b = findSubnode(a, "b", BH_unchanged, BPOL_track, 1, 2);
  mustHaveDirectoryStat(b, &metadata->backup_history[2]);
  PathNode *b_1 = findSubnode(b, "1", BH_unchanged, BPOL_track, 1, 0);
  mustHaveRegularStat(b_1, &metadata->backup_history[2], 7, (uint8_t *)"foo bar", 0);
  PathNode *b_2 = findSubnode(b, "2", BH_unchanged, BPOL_copy, 1, 1);
  mustHaveDirectoryStat(b_2, &metadata->backup_history[2]);
  PathNode *b_2_1 = findSubnode(b_2, "1", BH_unchanged, BPOL_track, 1, 0);
  mustHaveRegularStat(b_2_1, &metadata->backup_history[2], 18, (uint8_t *)"FooFooFooFooFooFoo", 0);
  PathNode *c = findSubnode(a, "c", BH_unchanged, BPOL_copy, 1, 0);
  mustHaveRegularStat(c, &metadata->backup_history[2], 56, nested_2_hash, 0);
  PathNode *d = findSubnode(a, "d", BH_unchanged, BPOL_mirror, 1, 1);
  mustHaveDirectoryStat(d, &metadata->backup_history[2]);
  PathNode *d_1 = findSubnode(d, "1", BH_unchanged, BPOL_mirror, 1, 0);
  mustHaveRegularStat(d_1, &metadata->backup_history[2], 12, (uint8_t *)"BARBARBARBAR", 0);

  PathNode *e = findSubnode(files, "e", BH_unchanged, BPOL_none, 1, 1);
  mustHaveDirectoryStat(e, &metadata->current_backup);
  PathNode *f = findSubnode(e, "f", BH_unchanged, BPOL_none, 1, 3);
  mustHaveDirectoryStat(f, &metadata->current_backup);
  PathNode *g = findSubnode(f, "g", BH_added, BPOL_track, 3, 0);
  mustHaveDirectoryStat(g, &metadata->current_backup);
  mustHaveNonExisting(g, &metadata->backup_history[1]);
  mustHaveDirectoryStat(g, &metadata->backup_history[2]);
  PathNode *h = findSubnode(f, "h", BH_unchanged, BPOL_mirror, 1, 0);
  mustHaveRegularStat(h, &metadata->backup_history[2], 1200, data_d_hash, 0);
  PathNode *i = findSubnode(f, "i", BH_unchanged, BPOL_copy, 1, 0);
  mustHaveSymlinkLStat(i, &metadata->backup_history[2], "non-existing.txt");

  /* Finish the backup and perform additional checks. */
  completeBackup(metadata);
  assert_true(countItemsInDir("tmp/repo") == 7);
}

/** Prepares the testing of filetype changes. */
static void initFiletypeChange(SearchNode *filetype_node,
                               BackupPolicy policy)
{
  /* Prepare the test files. */
  resetStatCache();
  assertTmpIsCleared();
  makeDir("tmp/files/5");
  makeDir("tmp/files/6");
  makeDir("tmp/files/6/a");
  makeDir("tmp/files/7");
  makeDir("tmp/files/7/a");
  makeDir("tmp/files/7/b");
  makeDir("tmp/files/7/c");
  makeDir("tmp/files/7/d");
  makeDir("tmp/files/8");
  makeDir("tmp/files/8/a");
  makeDir("tmp/files/8/a/b");
  makeDir("tmp/files/8/c");
  makeDir("tmp/files/8/c/d");
  makeDir("tmp/files/8/e");
  makeDir("tmp/files/8/e/f");
  makeDir("tmp/files/8/e/f/1");
  makeDir("tmp/files/9");
  generateFile("tmp/files/1",         "DummyFile",   1);
  generateFile("tmp/files/3",         "a/b/c/",      7);
  generateFile("tmp/files/6/a/1",     "X",           20);
  generateFile("tmp/files/6/2",       "FOO",         2);
  generateFile("tmp/files/6/3",       "0",           2123);
  generateFile("tmp/files/7/a/1",     "nested ",     9);
  generateFile("tmp/files/7/b/1",     "nested ",     2);
  generateFile("tmp/files/7/b/2",     "empty\n",     200);
  generateFile("tmp/files/7/c/2",     "dummy",       1);
  generateFile("tmp/files/7/d/1",     "DUMMY-",      3);
  generateFile("tmp/files/8/a/b/1",   "_FILE_",      2);
  generateFile("tmp/files/8/c/d/1",   "empty\n",     200);
  generateFile("tmp/files/8/e/f/1/1", "nano backup", 1);
  generateFile("tmp/files/8/e/f/1/2", "NanoBackup",  1);
  makeSymlink("target",           "tmp/files/2");
  makeSymlink("/dev/nano-backup", "tmp/files/4");
  makeSymlink("/home",            "tmp/files/7/c/1");
  makeSymlink("1",                "tmp/files/7/d/2");

  /* Initiate the backup. */
  Metadata *metadata = metadataNew();
  initiateBackup(metadata, filetype_node);

  /* Check the initiated backup. */
  checkMetadata(metadata, 0, false);
  assert_true(metadata->current_backup.ref_count == cwd_depth() + 37);
  assert_true(metadata->backup_history_length == 0);
  assert_true(metadata->total_path_count == cwd_depth() + 37);

  PathNode *files = findFilesNode(metadata, BH_added, 9);

  PathNode *node_1 = findSubnode(files, "1", BH_added, policy, 1, 0);
  mustHaveRegularCached(node_1, &metadata->current_backup, 9, NULL, 0);
  PathNode *node_2 = findSubnode(files, "2", BH_added, policy, 1, 0);
  mustHaveSymlinkLCached(node_2, &metadata->current_backup, "target");
  PathNode *node_3 = findSubnode(files, "3", BH_added, policy, 1, 0);
  mustHaveRegularCached(node_3, &metadata->current_backup, 42, NULL, 0);
  PathNode *node_4 = findSubnode(files, "4", BH_added, policy, 1, 0);
  mustHaveSymlinkLCached(node_4, &metadata->current_backup, "/dev/nano-backup");
  PathNode *node_5 = findSubnode(files, "5", BH_added, policy, 1, 0);
  mustHaveDirectoryCached(node_5, &metadata->current_backup);

  PathNode *node_6 = findSubnode(files, "6", BH_added, policy, 1, 3);
  mustHaveDirectoryCached(node_6, &metadata->current_backup);
  PathNode *node_6_a = findSubnode(node_6, "a", BH_added, policy, 1, 1);
  mustHaveDirectoryCached(node_6_a, &metadata->current_backup);
  PathNode *node_6_a_1 = findSubnode(node_6_a, "1", BH_added, policy, 1, 0);
  mustHaveRegularCached(node_6_a_1, &metadata->current_backup, 20, NULL, 0);
  PathNode *node_6_2 = findSubnode(node_6, "2", BH_added, policy, 1, 0);
  mustHaveRegularCached(node_6_2, &metadata->current_backup, 6, NULL, 0);
  PathNode *node_6_3 = findSubnode(node_6, "3", BH_added, policy, 1, 0);
  mustHaveRegularCached(node_6_3, &metadata->current_backup, 2123, NULL, 0);

  PathNode *node_7 = findSubnode(files, "7", BH_added, policy, 1, 4);
  mustHaveDirectoryCached(node_7, &metadata->current_backup);
  PathNode *node_7_a = findSubnode(node_7, "a", BH_added, BPOL_track, 1, 1);
  mustHaveDirectoryCached(node_7_a, &metadata->current_backup);
  PathNode *node_7_a_1 = findSubnode(node_7_a, "1", BH_added, BPOL_track, 1, 0);
  mustHaveRegularCached(node_7_a_1, &metadata->current_backup, 63, NULL, 0);
  PathNode *node_7_b = findSubnode(node_7, "b", BH_added, BPOL_track, 1, 2);
  mustHaveDirectoryCached(node_7_b, &metadata->current_backup);
  PathNode *node_7_b_1 = findSubnode(node_7_b, "1", BH_added, BPOL_track, 1, 0);
  mustHaveRegularCached(node_7_b_1, &metadata->current_backup, 14, NULL, 0);
  PathNode *node_7_b_2 = findSubnode(node_7_b, "2", BH_added, BPOL_track, 1, 0);
  mustHaveRegularCached(node_7_b_2, &metadata->current_backup, 1200, NULL, 0);
  PathNode *node_7_c = findSubnode(node_7, "c", BH_added, BPOL_copy, 1, 2);
  mustHaveDirectoryCached(node_7_c, &metadata->current_backup);
  PathNode *node_7_c_1 = findSubnode(node_7_c, "1", BH_added, BPOL_copy, 1, 0);
  mustHaveSymlinkLCached(node_7_c_1, &metadata->current_backup, "/home");
  PathNode *node_7_c_2 = findSubnode(node_7_c, "2", BH_added, BPOL_copy, 1, 0);
  mustHaveRegularCached(node_7_c_2, &metadata->current_backup, 5, NULL, 0);
  PathNode *node_7_d = findSubnode(node_7, "d", BH_added, BPOL_mirror, 1, 2);
  mustHaveDirectoryCached(node_7_d, &metadata->current_backup);
  PathNode *node_7_d_1 = findSubnode(node_7_d, "1", BH_added, BPOL_mirror, 1, 0);
  mustHaveRegularCached(node_7_d_1, &metadata->current_backup, 18, NULL, 0);
  PathNode *node_7_d_2 = findSubnode(node_7_d, "2", BH_added, BPOL_mirror, 1, 0);
  mustHaveSymlinkLCached(node_7_d_2, &metadata->current_backup, "1");

  PathNode *node_8 = findSubnode(files, "8", BH_added, policy, 1, 3);
  mustHaveDirectoryCached(node_8, &metadata->current_backup);
  PathNode *node_8_a = findSubnode(node_8, "a", BH_added, policy, 1, 1);
  mustHaveDirectoryCached(node_8_a, &metadata->current_backup);
  PathNode *node_8_a_b = findSubnode(node_8_a, "b", BH_added, BPOL_track, 1, 1);
  mustHaveDirectoryCached(node_8_a_b, &metadata->current_backup);
  PathNode *node_8_a_b_1 = findSubnode(node_8_a_b, "1", BH_added, BPOL_copy, 1, 0);
  mustHaveRegularCached(node_8_a_b_1, &metadata->current_backup, 12, NULL, 0);
  PathNode *node_8_c = findSubnode(node_8, "c", BH_added, policy, 1, 1);
  mustHaveDirectoryCached(node_8_c, &metadata->current_backup);
  PathNode *node_8_c_d = findSubnode(node_8_c, "d", BH_added, BPOL_copy, 1, 1);
  mustHaveDirectoryCached(node_8_c_d, &metadata->current_backup);
  PathNode *node_8_c_d_1 = findSubnode(node_8_c_d, "1", BH_added, BPOL_mirror, 1, 0);
  mustHaveRegularCached(node_8_c_d_1, &metadata->current_backup, 1200, NULL, 0);
  PathNode *node_8_e = findSubnode(node_8, "e", BH_added, policy, 1, 1);
  mustHaveDirectoryCached(node_8_e, &metadata->current_backup);
  PathNode *node_8_e_f = findSubnode(node_8_e, "f", BH_added, BPOL_mirror, 1, 1);
  mustHaveDirectoryCached(node_8_e_f, &metadata->current_backup);
  PathNode *node_8_e_f_1 = findSubnode(node_8_e_f, "1", BH_added, BPOL_track, 1, 2);
  mustHaveDirectoryCached(node_8_e_f_1, &metadata->current_backup);
  PathNode *node_8_e_f_1_1 = findSubnode(node_8_e_f_1, "1", BH_added, BPOL_track, 1, 0);
  mustHaveRegularCached(node_8_e_f_1_1, &metadata->current_backup, 11, NULL, 0);
  PathNode *node_8_e_f_1_2 = findSubnode(node_8_e_f_1, "2", BH_added, BPOL_track, 1, 0);
  mustHaveRegularCached(node_8_e_f_1_2, &metadata->current_backup, 10, NULL, 0);

  PathNode *node_9 = findSubnode(files, "9", BH_added, policy, 1, 0);
  mustHaveDirectoryCached(node_9, &metadata->current_backup);

  /* Finish the backup and perform additional checks. */
  completeBackup(metadata);
  assert_true(countItemsInDir("tmp/repo") == 13);
  mustHaveRegularCached(node_1,         &metadata->current_backup, 9,    (uint8_t *)"DummyFile",            0);
  mustHaveRegularCached(node_3,         &metadata->current_backup, 42,   test_c_hash,                       0);
  mustHaveRegularCached(node_6_a_1,     &metadata->current_backup, 20,   (uint8_t *)"XXXXXXXXXXXXXXXXXXXX", 0);
  mustHaveRegularCached(node_6_2,       &metadata->current_backup, 6,    (uint8_t *)"FOOFOO",               0);
  mustHaveRegularCached(node_6_3,       &metadata->current_backup, 2123, bin_hash,                          0);
  mustHaveRegularCached(node_7_a_1,     &metadata->current_backup, 63,   node_24_hash,                      0);
  mustHaveRegularCached(node_7_b_1,     &metadata->current_backup, 14,   (uint8_t *)"nested nested ",       0);
  mustHaveRegularCached(node_7_b_2,     &metadata->current_backup, 1200, bin_c_1_hash,                      0);
  mustHaveRegularCached(node_7_c_2,     &metadata->current_backup, 5,    (uint8_t *)"dummy",                0);
  mustHaveRegularCached(node_7_d_1,     &metadata->current_backup, 18,   (uint8_t *)"DUMMY-DUMMY-DUMMY-",   0);
  mustHaveRegularCached(node_8_a_b_1,   &metadata->current_backup, 12,   (uint8_t *)"_FILE__FILE_",         0);
  mustHaveRegularCached(node_8_c_d_1,   &metadata->current_backup, 1200, bin_c_1_hash,                      0);
  mustHaveRegularCached(node_8_e_f_1_1, &metadata->current_backup, 11,   (uint8_t *)"nano backup",          0);
  mustHaveRegularCached(node_8_e_f_1_2, &metadata->current_backup, 10,   (uint8_t *)"NanoBackup",           0);
}

/** Modifies the test files and metadata in such a way that subsequent
  backups will detect filetype changes. */
static void modifyFiletypeChange(SearchNode *filetype_node,
                                 BackupPolicy policy)
{
  /* Remove some files. */
  removePath("tmp/files/7/a/1");
  removePath("tmp/files/7/a");

  /* Initiate the backup. */
  Metadata *metadata = metadataLoad(strWrap("tmp/repo/metadata"));
  assert_true(metadata->total_path_count == cwd_depth() + 37);
  checkHistPoint(metadata, 0, 0, phase_timestamps(backup_counter() - 1), cwd_depth() + 37);
  initiateBackup(metadata, filetype_node);

  /* Check the initiated backup. */
  checkMetadata(metadata, 0, true);
  assert_true(metadata->current_backup.ref_count == cwd_depth() + 4);
  assert_true(metadata->backup_history_length == 1);
  assert_true(metadata->total_path_count == cwd_depth() + 37);
  checkHistPoint(metadata, 0, 0, phase_timestamps(backup_counter() - 1), 35);

  PathNode *files = findFilesNode(metadata, BH_unchanged, 9);

  PathNode *node_1 = findSubnode(files, "1", BH_unchanged, policy, 1, 0);
  mustHaveRegularCached(node_1, &metadata->backup_history[0], 9, (uint8_t *)"DummyFile", 0);
  PathNode *node_2 = findSubnode(files, "2", BH_unchanged, policy, 1, 0);
  mustHaveSymlinkLCached(node_2, &metadata->backup_history[0], "target");
  PathNode *node_3 = findSubnode(files, "3", BH_unchanged, policy, 1, 0);
  mustHaveRegularCached(node_3, &metadata->backup_history[0], 42, test_c_hash, 0);
  PathNode *node_4 = findSubnode(files, "4", BH_unchanged, policy, 1, 0);
  mustHaveSymlinkLCached(node_4, &metadata->backup_history[0], "/dev/nano-backup");
  PathNode *node_5 = findSubnode(files, "5", BH_unchanged, policy, 1, 0);
  mustHaveDirectoryCached(node_5, &metadata->backup_history[0]);

  PathNode *node_6 = findSubnode(files, "6", BH_unchanged, policy, 1, 3);
  mustHaveDirectoryCached(node_6, &metadata->backup_history[0]);
  PathNode *node_6_a = findSubnode(node_6, "a", BH_unchanged, policy, 1, 1);
  mustHaveDirectoryCached(node_6_a, &metadata->backup_history[0]);
  PathNode *node_6_a_1 = findSubnode(node_6_a, "1", BH_unchanged, policy, 1, 0);
  mustHaveRegularCached(node_6_a_1, &metadata->backup_history[0], 20, (uint8_t *)"XXXXXXXXXXXXXXXXXXXX", 0);
  PathNode *node_6_2 = findSubnode(node_6, "2", BH_unchanged, policy, 1, 0);
  mustHaveRegularCached(node_6_2, &metadata->backup_history[0], 6, (uint8_t *)"FOOFOO", 0);
  PathNode *node_6_3 = findSubnode(node_6, "3", BH_unchanged, policy, 1, 0);
  mustHaveRegularCached(node_6_3, &metadata->backup_history[0], 2123, bin_hash, 0);

  PathNode *node_7 = findSubnode(files, "7", BH_unchanged, policy, 1, 4);
  mustHaveDirectoryCached(node_7, &metadata->backup_history[0]);
  PathNode *node_7_a = findSubnode(node_7, "a", BH_removed, BPOL_track, 2, 1);
  mustHaveNonExisting(node_7_a, &metadata->current_backup);
  mustHaveDirectoryCached(node_7_a, &metadata->backup_history[0]);
  PathNode *node_7_a_1 = findSubnode(node_7_a, "1", BH_removed, BPOL_track, 2, 0);
  mustHaveNonExisting(node_7_a_1, &metadata->current_backup);
  mustHaveRegularCached(node_7_a_1, &metadata->backup_history[0], 63, node_24_hash, 0);
  PathNode *node_7_b = findSubnode(node_7, "b", BH_unchanged, BPOL_track, 1, 2);
  mustHaveDirectoryCached(node_7_b, &metadata->backup_history[0]);
  PathNode *node_7_b_1 = findSubnode(node_7_b, "1", BH_unchanged, BPOL_track, 1, 0);
  mustHaveRegularCached(node_7_b_1, &metadata->backup_history[0], 14, (uint8_t *)"nested nested ", 0);
  PathNode *node_7_b_2 = findSubnode(node_7_b, "2", BH_unchanged, BPOL_track, 1, 0);
  mustHaveRegularCached(node_7_b_2, &metadata->backup_history[0], 1200, bin_c_1_hash, 0);
  PathNode *node_7_c = findSubnode(node_7, "c", BH_unchanged, BPOL_copy, 1, 2);
  mustHaveDirectoryCached(node_7_c, &metadata->backup_history[0]);
  PathNode *node_7_c_1 = findSubnode(node_7_c, "1", BH_unchanged, BPOL_copy, 1, 0);
  mustHaveSymlinkLCached(node_7_c_1, &metadata->backup_history[0], "/home");
  PathNode *node_7_c_2 = findSubnode(node_7_c, "2", BH_unchanged, BPOL_copy, 1, 0);
  mustHaveRegularCached(node_7_c_2, &metadata->backup_history[0], 5, (uint8_t *)"dummy", 0);
  PathNode *node_7_d = findSubnode(node_7, "d", BH_unchanged, BPOL_mirror, 1, 2);
  mustHaveDirectoryCached(node_7_d, &metadata->backup_history[0]);
  PathNode *node_7_d_1 = findSubnode(node_7_d, "1", BH_unchanged, BPOL_mirror, 1, 0);
  mustHaveRegularCached(node_7_d_1, &metadata->backup_history[0], 18, (uint8_t *)"DUMMY-DUMMY-DUMMY-", 0);
  PathNode *node_7_d_2 = findSubnode(node_7_d, "2", BH_unchanged, BPOL_mirror, 1, 0);
  mustHaveSymlinkLCached(node_7_d_2, &metadata->backup_history[0], "1");

  PathNode *node_8 = findSubnode(files, "8", BH_unchanged, policy, 1, 3);
  mustHaveDirectoryCached(node_8, &metadata->backup_history[0]);
  PathNode *node_8_a = findSubnode(node_8, "a", BH_unchanged, policy, 1, 1);
  mustHaveDirectoryCached(node_8_a, &metadata->backup_history[0]);
  PathNode *node_8_a_b = findSubnode(node_8_a, "b", BH_unchanged, BPOL_track, 1, 1);
  mustHaveDirectoryCached(node_8_a_b, &metadata->backup_history[0]);
  PathNode *node_8_a_b_1 = findSubnode(node_8_a_b, "1", BH_unchanged, BPOL_copy, 1, 0);
  mustHaveRegularCached(node_8_a_b_1, &metadata->backup_history[0], 12, (uint8_t *)"_FILE__FILE_", 0);
  PathNode *node_8_c = findSubnode(node_8, "c", BH_unchanged, policy, 1, 1);
  mustHaveDirectoryCached(node_8_c, &metadata->backup_history[0]);
  PathNode *node_8_c_d = findSubnode(node_8_c, "d", BH_unchanged, BPOL_copy, 1, 1);
  mustHaveDirectoryCached(node_8_c_d, &metadata->backup_history[0]);
  PathNode *node_8_c_d_1 = findSubnode(node_8_c_d, "1", BH_unchanged, BPOL_mirror, 1, 0);
  mustHaveRegularCached(node_8_c_d_1, &metadata->backup_history[0], 1200, bin_c_1_hash, 0);
  PathNode *node_8_e = findSubnode(node_8, "e", BH_unchanged, policy, 1, 1);
  mustHaveDirectoryCached(node_8_e, &metadata->backup_history[0]);
  PathNode *node_8_e_f = findSubnode(node_8_e, "f", BH_unchanged, BPOL_mirror, 1, 1);
  mustHaveDirectoryCached(node_8_e_f, &metadata->backup_history[0]);
  PathNode *node_8_e_f_1 = findSubnode(node_8_e_f, "1", BH_unchanged, BPOL_track, 1, 2);
  mustHaveDirectoryCached(node_8_e_f_1, &metadata->backup_history[0]);
  PathNode *node_8_e_f_1_1 = findSubnode(node_8_e_f_1, "1", BH_unchanged, BPOL_track, 1, 0);
  mustHaveRegularCached(node_8_e_f_1_1, &metadata->backup_history[0], 11, (uint8_t *)"nano backup", 0);
  PathNode *node_8_e_f_1_2 = findSubnode(node_8_e_f_1, "2", BH_unchanged, BPOL_track, 1, 0);
  mustHaveRegularCached(node_8_e_f_1_2, &metadata->backup_history[0], 10, (uint8_t *)"NanoBackup", 0);

  PathNode *node_9 = findSubnode(files, "9", BH_unchanged, policy, 1, 0);
  mustHaveDirectoryCached(node_9, &metadata->backup_history[0]);

  /* Modify various path nodes. */
  removePath("tmp/files/1");
  makeSymlink("NewSymlink", "tmp/files/1");

  removePath("tmp/files/2");
  generateFile("tmp/files/2", "Backup\n", 74);

  node_3->history->state.gid++;
  removePath("tmp/files/3");
  makeDir("tmp/files/3");
  makeDir("tmp/files/3/a");
  makeDir("tmp/files/3/a/c");
  generateFile("tmp/files/3/a/b",   "nano-backup", 1);
  generateFile("tmp/files/3/a/c/1", "test 123",    1);
  generateFile("tmp/files/3/a/c/2", "TEST_TEST",   1);

  removePath("tmp/files/4");
  makeDir("tmp/files/4");
  makeDir("tmp/files/4/a");
  makeDir("tmp/files/4/a/c");
  generateFile("tmp/files/4/a/b",   "backup", 2);
  generateFile("tmp/files/4/a/c/1", "q",      21);
  generateFile("tmp/files/4/a/c/2", "=",      20);

  node_5->history->state.metadata.dir.mode++;
  removePath("tmp/files/5");
  generateFile("tmp/files/5", "?", 13);

  removePath("tmp/files/6/3");
  removePath("tmp/files/6/2");
  removePath("tmp/files/6/a/1");
  removePath("tmp/files/6/a");
  removePath("tmp/files/6");
  makeSymlink("3", "tmp/files/6");

  removePath("tmp/files/7/b/2");
  removePath("tmp/files/7/b/1");
  removePath("tmp/files/7/b");
  removePath("tmp/files/7/c/2");
  removePath("tmp/files/7/c/1");
  removePath("tmp/files/7/c");
  removePath("tmp/files/7/d/2");
  removePath("tmp/files/7/d/1");
  removePath("tmp/files/7/d");
  removePath("tmp/files/7");
  generateFile("tmp/files/7", "", 0);

  removePath("tmp/files/8/a/b/1");
  removePath("tmp/files/8/a/b");
  removePath("tmp/files/8/a");
  removePath("tmp/files/8/c/d/1");
  removePath("tmp/files/8/c/d");
  removePath("tmp/files/8/c");
  removePath("tmp/files/8/e/f/1/2");
  removePath("tmp/files/8/e/f/1/1");
  removePath("tmp/files/8/e/f/1");
  removePath("tmp/files/8/e/f");
  removePath("tmp/files/8/e");
  removePath("tmp/files/8");
  node_8->history->state.metadata.dir.mode++;
  makeSymlink("2", "tmp/files/8");

  removePath("tmp/files/9");
  makeSymlink("/dev/null", "tmp/files/9");

  /* Finish the backup and perform additional checks. */
  completeBackup(metadata);
  assert_true(countItemsInDir("tmp/repo") == 13);
}

/** Checks the changes injected by modifyFiletypeChange(). */
static void changeFiletypeChange(SearchNode *filetype_node,
                                 BackupPolicy policy)
{
  /* Initiate the backup. */
  Metadata *metadata = metadataLoad(strWrap("tmp/repo/metadata"));
  assert_true(metadata->total_path_count == cwd_depth() + 37);
  checkHistPoint(metadata, 0, 0, phase_timestamps(backup_counter() - 1), cwd_depth() + 4);
  checkHistPoint(metadata, 1, 1, phase_timestamps(backup_counter() - 2), 35);
  initiateBackup(metadata, filetype_node);

  /* Check the initiated backup. */
  checkMetadata(metadata, 0, false);
  assert_true(metadata->current_backup.ref_count == cwd_depth() + 21);
  assert_true(metadata->backup_history_length == 2);
  assert_true(metadata->total_path_count == cwd_depth() + 21);
  checkHistPoint(metadata, 0, 0, phase_timestamps(backup_counter() - 1), 0);
  checkHistPoint(metadata, 1, 1, phase_timestamps(backup_counter() - 2), 0);

  PathNode *files = findFilesNode(metadata, BH_unchanged, 9);

  PathNode *node_1 = findSubnode(files, "1", BH_regular_to_symlink, policy, 1, 0);
  mustHaveSymlinkLStat(node_1, &metadata->current_backup, "NewSymlink");
  PathNode *node_2 = findSubnode(files, "2", BH_symlink_to_regular, policy, 1, 0);
  mustHaveRegularStat(node_2, &metadata->current_backup, 518, NULL, 0);
  PathNode *node_3 = findSubnode(files, "3", BH_regular_to_directory, policy, 1, 1);
  mustHaveDirectoryStat(node_3, &metadata->current_backup);
  PathNode *node_3_a = findSubnode(node_3, "a", BH_added, policy, 1, 2);
  mustHaveDirectoryStat(node_3_a, &metadata->current_backup);
  PathNode *node_3_b = findSubnode(node_3_a, "b", BH_added, BPOL_track, 1, 0);
  mustHaveRegularStat(node_3_b, &metadata->current_backup, 11, NULL, 0);
  PathNode *node_3_c = findSubnode(node_3_a, "c", BH_added, policy, 1, 2);
  mustHaveDirectoryStat(node_3_c, &metadata->current_backup);
  PathNode *node_3_1 = findSubnode(node_3_c, "1", BH_added, BPOL_copy, 1, 0);
  mustHaveRegularStat(node_3_1, &metadata->current_backup, 8, NULL, 0);
  PathNode *node_3_2 = findSubnode(node_3_c, "2", BH_added, BPOL_mirror, 1, 0);
  mustHaveRegularStat(node_3_2, &metadata->current_backup, 9, NULL, 0);
  PathNode *node_4 = findSubnode(files, "4", BH_symlink_to_directory, policy, 1, 1);
  mustHaveDirectoryStat(node_4, &metadata->current_backup);
  PathNode *node_4_a = findSubnode(node_4, "a", BH_added, policy, 1, 2);
  mustHaveDirectoryStat(node_4_a, &metadata->current_backup);
  PathNode *node_4_b = findSubnode(node_4_a, "b", BH_added, policy, 1, 0);
  mustHaveRegularStat(node_4_b, &metadata->current_backup, 12, NULL, 0);
  PathNode *node_4_c = findSubnode(node_4_a, "c", BH_added, policy, 1, 2);
  mustHaveDirectoryStat(node_4_c, &metadata->current_backup);
  PathNode *node_4_1 = findSubnode(node_4_c, "1", BH_added, policy, 1, 0);
  mustHaveRegularStat(node_4_1, &metadata->current_backup, 21, NULL, 0);
  PathNode *node_4_2 = findSubnode(node_4_c, "2", BH_added, policy, 1, 0);
  mustHaveRegularStat(node_4_2, &metadata->current_backup, 20, NULL, 0);
  PathNode *node_5 = findSubnode(files, "5", BH_directory_to_regular, policy, 1, 0);
  mustHaveRegularStat(node_5, &metadata->current_backup, 13, NULL, 0);

  PathNode *node_6 = findSubnode(files, "6", BH_directory_to_symlink, policy, 1, 3);
  mustHaveSymlinkLStat(node_6, &metadata->current_backup, "3");
  PathNode *node_6_a = findSubnode(node_6, "a", BH_not_part_of_repository, policy, 1, 1);
  mustHaveDirectoryCached(node_6_a, &metadata->backup_history[1]);
  PathNode *node_6_a_1 = findSubnode(node_6_a, "1", BH_not_part_of_repository, policy, 1, 0);
  mustHaveRegularCached(node_6_a_1, &metadata->backup_history[1], 20, (uint8_t *)"XXXXXXXXXXXXXXXXXXXX", 0);
  PathNode *node_6_2 = findSubnode(node_6, "2", BH_not_part_of_repository, policy, 1, 0);
  mustHaveRegularCached(node_6_2, &metadata->backup_history[1], 6, (uint8_t *)"FOOFOO", 0);
  PathNode *node_6_3 = findSubnode(node_6, "3", BH_not_part_of_repository, policy, 1, 0);
  mustHaveRegularCached(node_6_3, &metadata->backup_history[1], 2123, bin_hash, 0);

  PathNode *node_7 = findSubnode(files, "7", BH_directory_to_regular, policy, 1, 4);
  mustHaveRegularStat(node_7, &metadata->current_backup, 0, NULL, 0);
  PathNode *node_7_a = findSubnode(node_7, "a", BH_not_part_of_repository, BPOL_track, 2, 1);
  mustHaveNonExisting(node_7_a, &metadata->backup_history[0]);
  mustHaveDirectoryCached(node_7_a, &metadata->backup_history[1]);
  PathNode *node_7_a_1 = findSubnode(node_7_a, "1", BH_not_part_of_repository, BPOL_track, 2, 0);
  mustHaveNonExisting(node_7_a_1, &metadata->backup_history[0]);
  mustHaveRegularCached(node_7_a_1, &metadata->backup_history[1], 63, node_24_hash, 0);
  PathNode *node_7_b = findSubnode(node_7, "b", BH_not_part_of_repository, BPOL_track, 1, 2);
  mustHaveDirectoryCached(node_7_b, &metadata->backup_history[1]);
  PathNode *node_7_b_1 = findSubnode(node_7_b, "1", BH_not_part_of_repository, BPOL_track, 1, 0);
  mustHaveRegularCached(node_7_b_1, &metadata->backup_history[1], 14, (uint8_t *)"nested nested ", 0);
  PathNode *node_7_b_2 = findSubnode(node_7_b, "2", BH_not_part_of_repository, BPOL_track, 1, 0);
  mustHaveRegularCached(node_7_b_2, &metadata->backup_history[1], 1200, bin_c_1_hash, 0);
  PathNode *node_7_c = findSubnode(node_7, "c", BH_not_part_of_repository, BPOL_copy, 1, 2);
  mustHaveDirectoryCached(node_7_c, &metadata->backup_history[1]);
  PathNode *node_7_c_1 = findSubnode(node_7_c, "1", BH_not_part_of_repository, BPOL_copy, 1, 0);
  mustHaveSymlinkLCached(node_7_c_1, &metadata->backup_history[1], "/home");
  PathNode *node_7_c_2 = findSubnode(node_7_c, "2", BH_not_part_of_repository, BPOL_copy, 1, 0);
  mustHaveRegularCached(node_7_c_2, &metadata->backup_history[1], 5, (uint8_t *)"dummy", 0);
  PathNode *node_7_d = findSubnode(node_7, "d", BH_not_part_of_repository, BPOL_mirror, 1, 2);
  mustHaveDirectoryCached(node_7_d, &metadata->backup_history[1]);
  PathNode *node_7_d_1 = findSubnode(node_7_d, "1", BH_not_part_of_repository, BPOL_mirror, 1, 0);
  mustHaveRegularCached(node_7_d_1, &metadata->backup_history[1], 18, (uint8_t *)"DUMMY-DUMMY-DUMMY-", 0);
  PathNode *node_7_d_2 = findSubnode(node_7_d, "2", BH_not_part_of_repository, BPOL_mirror, 1, 0);
  mustHaveSymlinkLCached(node_7_d_2, &metadata->backup_history[1], "1");

  PathNode *node_8 = findSubnode(files, "8", BH_directory_to_regular, policy, 1, 3);
  mustHaveRegularStat(node_8, &metadata->current_backup, 518, NULL, 0);
  PathNode *node_8_a = findSubnode(node_8, "a", BH_not_part_of_repository, policy, 1, 1);
  mustHaveDirectoryCached(node_8_a, &metadata->backup_history[1]);
  PathNode *node_8_a_b = findSubnode(node_8_a, "b", BH_not_part_of_repository, BPOL_track, 1, 1);
  mustHaveDirectoryCached(node_8_a_b, &metadata->backup_history[1]);
  PathNode *node_8_a_b_1 = findSubnode(node_8_a_b, "1", BH_not_part_of_repository, BPOL_copy, 1, 0);
  mustHaveRegularCached(node_8_a_b_1, &metadata->backup_history[1], 12, (uint8_t *)"_FILE__FILE_", 0);
  PathNode *node_8_c = findSubnode(node_8, "c", BH_not_part_of_repository, policy, 1, 1);
  mustHaveDirectoryCached(node_8_c, &metadata->backup_history[1]);
  PathNode *node_8_c_d = findSubnode(node_8_c, "d", BH_not_part_of_repository, BPOL_copy, 1, 1);
  mustHaveDirectoryCached(node_8_c_d, &metadata->backup_history[1]);
  PathNode *node_8_c_d_1 = findSubnode(node_8_c_d, "1", BH_not_part_of_repository, BPOL_mirror, 1, 0);
  mustHaveRegularCached(node_8_c_d_1, &metadata->backup_history[1], 1200, bin_c_1_hash, 0);
  PathNode *node_8_e = findSubnode(node_8, "e", BH_not_part_of_repository, policy, 1, 1);
  mustHaveDirectoryCached(node_8_e, &metadata->backup_history[1]);
  PathNode *node_8_e_f = findSubnode(node_8_e, "f", BH_not_part_of_repository, BPOL_mirror, 1, 1);
  mustHaveDirectoryCached(node_8_e_f, &metadata->backup_history[1]);
  PathNode *node_8_e_f_1 = findSubnode(node_8_e_f, "1", BH_not_part_of_repository, BPOL_track, 1, 2);
  mustHaveDirectoryCached(node_8_e_f_1, &metadata->backup_history[1]);
  PathNode *node_8_e_f_1_1 = findSubnode(node_8_e_f_1, "1", BH_not_part_of_repository, BPOL_track, 1, 0);
  mustHaveRegularCached(node_8_e_f_1_1, &metadata->backup_history[1], 11, (uint8_t *)"nano backup", 0);
  PathNode *node_8_e_f_1_2 = findSubnode(node_8_e_f_1, "2", BH_not_part_of_repository, BPOL_track, 1, 0);
  mustHaveRegularCached(node_8_e_f_1_2, &metadata->backup_history[1], 10, (uint8_t *)"NanoBackup", 0);

  PathNode *node_9 = findSubnode(files, "9", BH_directory_to_symlink, policy, 1, 0);
  mustHaveSymlinkLStat(node_9, &metadata->current_backup, "/dev/null");

  /* Finish the backup and perform additional checks. */
  completeBackup(metadata);
  assert_true(countItemsInDir("tmp/repo") == 18);
  mustHaveRegularStat(node_2,   &metadata->current_backup, 518, node_42_hash,                      0);
  mustHaveRegularStat(node_3_b, &metadata->current_backup, 11,  (uint8_t *)"nano-backup",          0);
  mustHaveRegularStat(node_3_1, &metadata->current_backup, 8,   (uint8_t *)"test 123",             0);
  mustHaveRegularStat(node_3_2, &metadata->current_backup, 9,   (uint8_t *)"TEST_TEST",            0);
  mustHaveRegularStat(node_4_b, &metadata->current_backup, 12,  (uint8_t *)"backupbackup",         0);
  mustHaveRegularStat(node_4_1, &metadata->current_backup, 21,  node_45_hash,                      0);
  mustHaveRegularStat(node_4_2, &metadata->current_backup, 20,  (uint8_t *)"====================", 0);
  mustHaveRegularStat(node_5,   &metadata->current_backup, 13,  (uint8_t *)"?????????????",        0);
  mustHaveRegularStat(node_7,   &metadata->current_backup, 0,   (uint8_t *)"K",                    0);
  mustHaveRegularStat(node_8,   &metadata->current_backup, 518, node_42_hash,                      0);
}

/** Tests the metadata written by changeFiletypeChange() and cleans up. */
static void postFiletypeChange(SearchNode *filetype_node,
                               BackupPolicy policy)
{
  /* Initiate the backup. */
  Metadata *metadata = metadataLoad(strWrap("tmp/repo/metadata"));
  assert_true(metadata->total_path_count == cwd_depth() + 21);
  checkHistPoint(metadata, 0, 0, phase_timestamps(backup_counter() - 1), cwd_depth() + 21);
  initiateBackup(metadata, filetype_node);

  /* Check the initiated backup. */
  checkMetadata(metadata, 0, true);
  assert_true(metadata->current_backup.ref_count == cwd_depth() + 2);
  assert_true(metadata->backup_history_length == 1);
  assert_true(metadata->total_path_count == cwd_depth() + 21);
  checkHistPoint(metadata, 0, 0, phase_timestamps(backup_counter() - 1), 19);

  PathNode *files = findFilesNode(metadata, BH_unchanged, 9);

  PathNode *node_1 = findSubnode(files, "1", BH_unchanged, policy, 1, 0);
  mustHaveSymlinkLStat(node_1, &metadata->backup_history[0], "NewSymlink");
  PathNode *node_2 = findSubnode(files, "2", BH_unchanged, policy, 1, 0);
  mustHaveRegularStat(node_2, &metadata->backup_history[0], 518, node_42_hash, 0);

  PathNode *node_3 = findSubnode(files, "3", BH_unchanged, policy, 1, 1);
  mustHaveDirectoryStat(node_3, &metadata->backup_history[0]);
  PathNode *node_3_a = findSubnode(node_3, "a", BH_unchanged, policy, 1, 2);
  mustHaveDirectoryStat(node_3_a, &metadata->backup_history[0]);
  PathNode *node_3_b = findSubnode(node_3_a, "b", BH_unchanged, BPOL_track, 1, 0);
  mustHaveRegularStat(node_3_b, &metadata->backup_history[0], 11, (uint8_t *)"nano-backup", 0);
  PathNode *node_3_c = findSubnode(node_3_a, "c", BH_unchanged, policy, 1, 2);
  mustHaveDirectoryStat(node_3_c, &metadata->backup_history[0]);
  PathNode *node_3_1 = findSubnode(node_3_c, "1", BH_unchanged, BPOL_copy, 1, 0);
  mustHaveRegularStat(node_3_1, &metadata->backup_history[0], 8, (uint8_t *)"test 123", 0);
  PathNode *node_3_2 = findSubnode(node_3_c, "2", BH_unchanged, BPOL_mirror, 1, 0);
  mustHaveRegularStat(node_3_2, &metadata->backup_history[0], 9, (uint8_t *)"TEST_TEST", 0);

  PathNode *node_4 = findSubnode(files, "4", BH_unchanged, policy, 1, 1);
  mustHaveDirectoryStat(node_4, &metadata->backup_history[0]);
  PathNode *node_4_a = findSubnode(node_4, "a", BH_unchanged, policy, 1, 2);
  mustHaveDirectoryStat(node_4_a, &metadata->backup_history[0]);
  PathNode *node_4_b = findSubnode(node_4_a, "b", BH_unchanged, policy, 1, 0);
  mustHaveRegularStat(node_4_b, &metadata->backup_history[0], 12, (uint8_t *)"backupbackup", 0);
  PathNode *node_4_c = findSubnode(node_4_a, "c", BH_unchanged, policy, 1, 2);
  mustHaveDirectoryStat(node_4_c, &metadata->backup_history[0]);
  PathNode *node_4_1 = findSubnode(node_4_c, "1", BH_unchanged, policy, 1, 0);
  mustHaveRegularStat(node_4_1, &metadata->backup_history[0], 21, node_45_hash, 0);
  PathNode *node_4_2 = findSubnode(node_4_c, "2", BH_unchanged, policy, 1, 0);
  mustHaveRegularStat(node_4_2, &metadata->backup_history[0], 20, (uint8_t *)"====================", 0);

  PathNode *node_5 = findSubnode(files, "5", BH_unchanged, policy, 1, 0);
  mustHaveRegularStat(node_5, &metadata->backup_history[0], 13, (uint8_t *)"?????????????", 0);
  PathNode *node_6 = findSubnode(files, "6", BH_unchanged, policy, 1, 0);
  mustHaveSymlinkLStat(node_6, &metadata->backup_history[0], "3");
  PathNode *node_7 = findSubnode(files, "7", BH_unchanged, policy, 1, 0);
  mustHaveRegularStat(node_7, &metadata->backup_history[0], 0, (uint8_t *)"K", 0);
  PathNode *node_8 = findSubnode(files, "8", BH_unchanged, policy, 1, 0);
  mustHaveRegularStat(node_8, &metadata->backup_history[0], 518, node_42_hash, 0);
  PathNode *node_9 = findSubnode(files, "9", BH_unchanged, policy, 1, 0);
  mustHaveSymlinkLStat(node_9, &metadata->backup_history[0], "/dev/null");

  /* Finish the backup and perform additional checks. */
  completeBackup(metadata);
  assert_true(countItemsInDir("tmp/repo") == 18);
}

/** Checks the changes injected by modifyFiletypeChange() for the track
  policy. */
static void trackFiletypeChange(SearchNode *filetype_node)
{
  /* Initiate the backup. */
  Metadata *metadata = metadataLoad(strWrap("tmp/repo/metadata"));
  assert_true(metadata->total_path_count == cwd_depth() + 37);
  checkHistPoint(metadata, 0, 0, phase_timestamps(backup_counter() - 1), cwd_depth() + 4);
  checkHistPoint(metadata, 1, 1, phase_timestamps(backup_counter() - 2), 35);
  initiateBackup(metadata, filetype_node);

  /* Check the initiated backup. */
  checkMetadata(metadata, 0, false);
  assert_true(metadata->current_backup.ref_count == cwd_depth() + 32);
  assert_true(metadata->backup_history_length == 2);
  assert_true(metadata->total_path_count == cwd_depth() + 47);
  checkHistPoint(metadata, 0, 0, phase_timestamps(backup_counter() - 1), 2);
  checkHistPoint(metadata, 1, 1, phase_timestamps(backup_counter() - 2), 35);

  PathNode *files = findFilesNode(metadata, BH_unchanged, 9);

  PathNode *node_1 = findSubnode(files, "1", BH_regular_to_symlink, BPOL_track, 2, 0);
  mustHaveSymlinkLStat(node_1, &metadata->current_backup, "NewSymlink");
  mustHaveRegularCached(node_1, &metadata->backup_history[1], 9, (uint8_t *)"DummyFile", 0);
  PathNode *node_2 = findSubnode(files, "2", BH_symlink_to_regular, BPOL_track, 2, 0);
  mustHaveRegularStat(node_2, &metadata->current_backup, 518, NULL, 0);
  mustHaveSymlinkLCached(node_2, &metadata->backup_history[1], "target");

  PathNode *node_3 = findSubnode(files, "3", BH_regular_to_directory, BPOL_track, 2, 1);
  mustHaveDirectoryStat(node_3, &metadata->current_backup);
  struct stat node_3_stats = cachedStat(node_3->path, sStat);
  node_3_stats.st_gid++;
  mustHaveRegularStats(node_3, &metadata->backup_history[1], node_3_stats, 42, test_c_hash, 0);
  PathNode *node_3_a = findSubnode(node_3, "a", BH_added, BPOL_track, 1, 2);
  mustHaveDirectoryStat(node_3_a, &metadata->current_backup);
  PathNode *node_3_b = findSubnode(node_3_a, "b", BH_added, BPOL_track, 1, 0);
  mustHaveRegularStat(node_3_b, &metadata->current_backup, 11, NULL, 0);
  PathNode *node_3_c = findSubnode(node_3_a, "c", BH_added, BPOL_track, 1, 2);
  mustHaveDirectoryStat(node_3_c, &metadata->current_backup);
  PathNode *node_3_1 = findSubnode(node_3_c, "1", BH_added, BPOL_copy, 1, 0);
  mustHaveRegularStat(node_3_1, &metadata->current_backup, 8, NULL, 0);
  PathNode *node_3_2 = findSubnode(node_3_c, "2", BH_added, BPOL_mirror, 1, 0);
  mustHaveRegularStat(node_3_2, &metadata->current_backup, 9, NULL, 0);

  PathNode *node_4 = findSubnode(files, "4", BH_symlink_to_directory, BPOL_track, 2, 1);
  mustHaveDirectoryStat(node_4, &metadata->current_backup);
  mustHaveSymlinkLCached(node_4, &metadata->backup_history[1], "/dev/nano-backup");
  PathNode *node_4_a = findSubnode(node_4, "a", BH_added, BPOL_track, 1, 2);
  mustHaveDirectoryStat(node_4_a, &metadata->current_backup);
  PathNode *node_4_b = findSubnode(node_4_a, "b", BH_added, BPOL_track, 1, 0);
  mustHaveRegularStat(node_4_b, &metadata->current_backup, 12, NULL, 0);
  PathNode *node_4_c = findSubnode(node_4_a, "c", BH_added, BPOL_track, 1, 2);
  mustHaveDirectoryStat(node_4_c, &metadata->current_backup);
  PathNode *node_4_1 = findSubnode(node_4_c, "1", BH_added, BPOL_track, 1, 0);
  mustHaveRegularStat(node_4_1, &metadata->current_backup, 21, NULL, 0);
  PathNode *node_4_2 = findSubnode(node_4_c, "2", BH_added, BPOL_track, 1, 0);
  mustHaveRegularStat(node_4_2, &metadata->current_backup, 20, NULL, 0);

  PathNode *node_5 = findSubnode(files, "5", BH_directory_to_regular, BPOL_track, 2, 0);
  mustHaveRegularStat(node_5, &metadata->current_backup, 13, NULL, 0);
  struct stat node_5_stats = cachedStat(node_5->path, sStat);
  node_5_stats.st_mode++;
  mustHaveDirectoryStats(node_5, &metadata->backup_history[1], node_5_stats);

  PathNode *node_6 = findSubnode(files, "6", BH_directory_to_symlink, BPOL_track, 2, 3);
  mustHaveSymlinkLStat(node_6, &metadata->current_backup, "3");
  mustHaveDirectoryCached(node_6, &metadata->backup_history[1]);
  PathNode *node_6_a = findSubnode(node_6, "a", BH_removed, BPOL_track, 2, 1);
  mustHaveNonExisting(node_6_a, &metadata->current_backup);
  mustHaveDirectoryCached(node_6_a, &metadata->backup_history[1]);
  PathNode *node_6_a_1 = findSubnode(node_6_a, "1", BH_removed, BPOL_track, 2, 0);
  mustHaveNonExisting(node_6_a_1, &metadata->current_backup);
  mustHaveRegularCached(node_6_a_1, &metadata->backup_history[1], 20, (uint8_t *)"XXXXXXXXXXXXXXXXXXXX", 0);
  PathNode *node_6_2 = findSubnode(node_6, "2", BH_removed, BPOL_track, 2, 0);
  mustHaveNonExisting(node_6_2, &metadata->current_backup);
  mustHaveRegularCached(node_6_2, &metadata->backup_history[1], 6, (uint8_t *)"FOOFOO", 0);
  PathNode *node_6_3 = findSubnode(node_6, "3", BH_removed, BPOL_track, 2, 0);
  mustHaveNonExisting(node_6_3, &metadata->current_backup);
  mustHaveRegularCached(node_6_3, &metadata->backup_history[1], 2123, bin_hash, 0);

  PathNode *node_7 = findSubnode(files, "7", BH_directory_to_regular, BPOL_track, 2, 4);
  mustHaveRegularStat(node_7, &metadata->current_backup, 0, NULL, 0);
  mustHaveDirectoryCached(node_7, &metadata->backup_history[1]);
  PathNode *node_7_a = findSubnode(node_7, "a", BH_unchanged, BPOL_track, 2, 1);
  mustHaveNonExisting(node_7_a, &metadata->backup_history[0]);
  mustHaveDirectoryCached(node_7_a, &metadata->backup_history[1]);
  PathNode *node_7_a_1 = findSubnode(node_7_a, "1", BH_unchanged, BPOL_track, 2, 0);
  mustHaveNonExisting(node_7_a_1, &metadata->backup_history[0]);
  mustHaveRegularCached(node_7_a_1, &metadata->backup_history[1], 63, node_24_hash, 0);
  PathNode *node_7_b = findSubnode(node_7, "b", BH_removed, BPOL_track, 2, 2);
  mustHaveNonExisting(node_7_b, &metadata->current_backup);
  mustHaveDirectoryCached(node_7_b, &metadata->backup_history[1]);
  PathNode *node_7_b_1 = findSubnode(node_7_b, "1", BH_removed, BPOL_track, 2, 0);
  mustHaveNonExisting(node_7_b_1, &metadata->current_backup);
  mustHaveRegularCached(node_7_b_1, &metadata->backup_history[1], 14, (uint8_t *)"nested nested ", 0);
  PathNode *node_7_b_2 = findSubnode(node_7_b, "2", BH_removed, BPOL_track, 2, 0);
  mustHaveNonExisting(node_7_b_2, &metadata->current_backup);
  mustHaveRegularCached(node_7_b_2, &metadata->backup_history[1], 1200, bin_c_1_hash, 0);
  PathNode *node_7_c = findSubnode(node_7, "c", BH_removed, BPOL_copy, 1, 2);
  mustHaveDirectoryCached(node_7_c, &metadata->backup_history[1]);
  PathNode *node_7_c_1 = findSubnode(node_7_c, "1", BH_removed, BPOL_copy, 1, 0);
  mustHaveSymlinkLCached(node_7_c_1, &metadata->backup_history[1], "/home");
  PathNode *node_7_c_2 = findSubnode(node_7_c, "2", BH_removed, BPOL_copy, 1, 0);
  mustHaveRegularCached(node_7_c_2, &metadata->backup_history[1], 5, (uint8_t *)"dummy", 0);
  PathNode *node_7_d = findSubnode(node_7, "d", BH_removed, BPOL_mirror, 1, 2);
  mustHaveDirectoryCached(node_7_d, &metadata->backup_history[1]);
  PathNode *node_7_d_1 = findSubnode(node_7_d, "1", BH_removed, BPOL_mirror, 1, 0);
  mustHaveRegularCached(node_7_d_1, &metadata->backup_history[1], 18, (uint8_t *)"DUMMY-DUMMY-DUMMY-", 0);
  PathNode *node_7_d_2 = findSubnode(node_7_d, "2", BH_removed, BPOL_mirror, 1, 0);
  mustHaveSymlinkLCached(node_7_d_2, &metadata->backup_history[1], "1");

  PathNode *node_8 = findSubnode(files, "8", BH_directory_to_regular, BPOL_track, 2, 3);
  mustHaveRegularStat(node_8, &metadata->current_backup, 518, NULL, 0);
  struct stat node_8_stats = cachedStat(node_8->path, sStat);
  node_8_stats.st_mode++;
  mustHaveDirectoryStats(node_8, &metadata->backup_history[1], node_8_stats);
  PathNode *node_8_a = findSubnode(node_8, "a", BH_removed, BPOL_track, 2, 1);
  mustHaveNonExisting(node_8_a, &metadata->current_backup);
  mustHaveDirectoryCached(node_8_a, &metadata->backup_history[1]);
  PathNode *node_8_a_b = findSubnode(node_8_a, "b", BH_removed, BPOL_track, 2, 1);
  mustHaveNonExisting(node_8_a_b, &metadata->current_backup);
  mustHaveDirectoryCached(node_8_a_b, &metadata->backup_history[1]);
  PathNode *node_8_a_b_1 = findSubnode(node_8_a_b, "1", BH_removed, BPOL_copy, 1, 0);
  mustHaveRegularCached(node_8_a_b_1, &metadata->backup_history[1], 12, (uint8_t *)"_FILE__FILE_", 0);
  PathNode *node_8_c = findSubnode(node_8, "c", BH_removed, BPOL_track, 2, 1);
  mustHaveNonExisting(node_8_c, &metadata->current_backup);
  mustHaveDirectoryCached(node_8_c, &metadata->backup_history[1]);
  PathNode *node_8_c_d = findSubnode(node_8_c, "d", BH_removed, BPOL_copy, 1, 1);
  mustHaveDirectoryCached(node_8_c_d, &metadata->backup_history[1]);
  PathNode *node_8_c_d_1 = findSubnode(node_8_c_d, "1", BH_removed, BPOL_mirror, 1, 0);
  mustHaveRegularCached(node_8_c_d_1, &metadata->backup_history[1], 1200, bin_c_1_hash, 0);
  PathNode *node_8_e = findSubnode(node_8, "e", BH_removed, BPOL_track, 2, 1);
  mustHaveNonExisting(node_8_e, &metadata->current_backup);
  mustHaveDirectoryCached(node_8_e, &metadata->backup_history[1]);
  PathNode *node_8_e_f = findSubnode(node_8_e, "f", BH_removed, BPOL_mirror, 1, 1);
  mustHaveDirectoryCached(node_8_e_f, &metadata->backup_history[1]);
  PathNode *node_8_e_f_1 = findSubnode(node_8_e_f, "1", BH_removed, BPOL_track, 1, 2);
  mustHaveDirectoryCached(node_8_e_f_1, &metadata->backup_history[1]);
  PathNode *node_8_e_f_1_1 = findSubnode(node_8_e_f_1, "1", BH_removed, BPOL_track, 1, 0);
  mustHaveRegularCached(node_8_e_f_1_1, &metadata->backup_history[1], 11, (uint8_t *)"nano backup", 0);
  PathNode *node_8_e_f_1_2 = findSubnode(node_8_e_f_1, "2", BH_removed, BPOL_track, 1, 0);
  mustHaveRegularCached(node_8_e_f_1_2, &metadata->backup_history[1], 10, (uint8_t *)"NanoBackup", 0);

  PathNode *node_9 = findSubnode(files, "9", BH_directory_to_symlink, BPOL_track, 2, 0);
  mustHaveSymlinkLStat(node_9, &metadata->current_backup, "/dev/null");
  mustHaveDirectoryCached(node_9, &metadata->backup_history[1]);

  /* Finish the backup and perform additional checks. */
  completeBackup(metadata);
  assert_true(countItemsInDir("tmp/repo") == 18);
  mustHaveRegularStat(node_2,   &metadata->current_backup, 518, node_42_hash,                      0);
  mustHaveRegularStat(node_3_b, &metadata->current_backup, 11,  (uint8_t *)"nano-backup",          0);
  mustHaveRegularStat(node_3_1, &metadata->current_backup, 8,   (uint8_t *)"test 123",             0);
  mustHaveRegularStat(node_3_2, &metadata->current_backup, 9,   (uint8_t *)"TEST_TEST",            0);
  mustHaveRegularStat(node_4_b, &metadata->current_backup, 12,  (uint8_t *)"backupbackup",         0);
  mustHaveRegularStat(node_4_1, &metadata->current_backup, 21,  node_45_hash,                      0);
  mustHaveRegularStat(node_4_2, &metadata->current_backup, 20,  (uint8_t *)"====================", 0);
  mustHaveRegularStat(node_5,   &metadata->current_backup, 13,  (uint8_t *)"?????????????",        0);
  mustHaveRegularStat(node_7,   &metadata->current_backup, 0,   (uint8_t *)"K",                    0);
  mustHaveRegularStat(node_8,   &metadata->current_backup, 518, node_42_hash,                      0);
}

/** Tests the metadata written by changeFiletypeChange(). It takes the
  following additional argument:

  @param completed_runs The count of subsequent runs this function has
  completed.
*/
static void trackFiletypeChangePost(SearchNode *filetype_node,
                                    size_t completed_runs)
{
  /* Initiate the backup. */
  Metadata *metadata = metadataLoad(strWrap("tmp/repo/metadata"));
  assert_true(metadata->total_path_count == cwd_depth() + 47);
  size_t off = completed_runs > 0;

  if(completed_runs > 0)
  {
    checkHistPoint(metadata, 0, 0, phase_timestamps(backup_counter() - 1), cwd_depth() + 2);
  }
  else
  {
    checkHistPoint(metadata, 0, 0 , phase_timestamps(backup_counter() - 1), cwd_depth() + 32);
  }

  checkHistPoint(metadata, 1 + off, 1 + off, phase_timestamps(backup_counter() - 2 - completed_runs), 2);
  checkHistPoint(metadata, 2 + off, 2 + off, phase_timestamps(backup_counter() - 3 - completed_runs), 35);
  initiateBackup(metadata, filetype_node);

  /* Check the initiated backup. */
  checkMetadata(metadata, 0, true);
  assert_true(metadata->current_backup.ref_count == cwd_depth() + 2);
  assert_true(metadata->backup_history_length == 3 + off);
  assert_true(metadata->total_path_count == cwd_depth() + 47);

  if(completed_runs > 0)
  {
    checkHistPoint(metadata, 0, 0, phase_timestamps(backup_counter() - 1), 0);
  }

  checkHistPoint(metadata, 0 + off, 0 + off, phase_timestamps(backup_counter() - 1 - completed_runs), 30);
  checkHistPoint(metadata, 1 + off, 1 + off, phase_timestamps(backup_counter() - 2 - completed_runs), 2);
  checkHistPoint(metadata, 2 + off, 2 + off, phase_timestamps(backup_counter() - 3 - completed_runs), 35);

  PathNode *files = findFilesNode(metadata, BH_unchanged, 9);

  PathNode *node_1 = findSubnode(files, "1", BH_unchanged, BPOL_track, 2, 0);
  mustHaveSymlinkLStat(node_1, &metadata->backup_history[0 + off], "NewSymlink");
  mustHaveRegularCached(node_1, &metadata->backup_history[2 + off], 9, (uint8_t *)"DummyFile", 0);
  PathNode *node_2 = findSubnode(files, "2", BH_unchanged, BPOL_track, 2, 0);
  mustHaveRegularStat(node_2, &metadata->backup_history[0 + off], 518, node_42_hash, 0);
  mustHaveSymlinkLCached(node_2, &metadata->backup_history[2 + off], "target");

  PathNode *node_3 = findSubnode(files, "3", BH_unchanged, BPOL_track, 2, 1);
  mustHaveDirectoryStat(node_3, &metadata->backup_history[0 + off]);
  struct stat node_3_stats = cachedStat(node_3->path, sStat);
  node_3_stats.st_gid++;
  mustHaveRegularStats(node_3, &metadata->backup_history[2 + off], node_3_stats, 42, test_c_hash, 0);
  PathNode *node_3_a = findSubnode(node_3, "a", BH_unchanged, BPOL_track, 1, 2);
  mustHaveDirectoryStat(node_3_a, &metadata->backup_history[0 + off]);
  PathNode *node_3_b = findSubnode(node_3_a, "b", BH_unchanged, BPOL_track, 1, 0);
  mustHaveRegularStat(node_3_b, &metadata->backup_history[0 + off], 11, (uint8_t *)"nano-backup", 0);
  PathNode *node_3_c = findSubnode(node_3_a, "c", BH_unchanged, BPOL_track, 1, 2);
  mustHaveDirectoryStat(node_3_c, &metadata->backup_history[0 + off]);
  PathNode *node_3_1 = findSubnode(node_3_c, "1", BH_unchanged, BPOL_copy, 1, 0);
  mustHaveRegularStat(node_3_1, &metadata->backup_history[0 + off], 8, (uint8_t *)"test 123", 0);
  PathNode *node_3_2 = findSubnode(node_3_c, "2", BH_unchanged, BPOL_mirror, 1, 0);
  mustHaveRegularStat(node_3_2, &metadata->backup_history[0 + off], 9, (uint8_t *)"TEST_TEST", 0);

  PathNode *node_4 = findSubnode(files, "4", BH_unchanged, BPOL_track, 2, 1);
  mustHaveDirectoryStat(node_4, &metadata->backup_history[0 + off]);
  mustHaveSymlinkLCached(node_4, &metadata->backup_history[2 + off], "/dev/nano-backup");
  PathNode *node_4_a = findSubnode(node_4, "a", BH_unchanged, BPOL_track, 1, 2);
  mustHaveDirectoryStat(node_4_a, &metadata->backup_history[0 + off]);
  PathNode *node_4_b = findSubnode(node_4_a, "b", BH_unchanged, BPOL_track, 1, 0);
  mustHaveRegularStat(node_4_b, &metadata->backup_history[0 + off], 12, (uint8_t *)"backupbackup", 0);
  PathNode *node_4_c = findSubnode(node_4_a, "c", BH_unchanged, BPOL_track, 1, 2);
  mustHaveDirectoryStat(node_4_c, &metadata->backup_history[0 + off]);
  PathNode *node_4_1 = findSubnode(node_4_c, "1", BH_unchanged, BPOL_track, 1, 0);
  mustHaveRegularStat(node_4_1, &metadata->backup_history[0 + off], 21, node_45_hash, 0);
  PathNode *node_4_2 = findSubnode(node_4_c, "2", BH_unchanged, BPOL_track, 1, 0);
  mustHaveRegularStat(node_4_2, &metadata->backup_history[0 + off], 20, (uint8_t *)"====================", 0);

  PathNode *node_5 = findSubnode(files, "5", BH_unchanged, BPOL_track, 2, 0);
  mustHaveRegularStat(node_5, &metadata->backup_history[0 + off], 13, (uint8_t *)"?????????????", 0);
  struct stat node_5_stats = cachedStat(node_5->path, sStat);
  node_5_stats.st_mode++;
  mustHaveDirectoryStats(node_5, &metadata->backup_history[2 + off], node_5_stats);

  PathNode *node_6 = findSubnode(files, "6", BH_unchanged, BPOL_track, 2, 3);
  mustHaveSymlinkLStat(node_6, &metadata->backup_history[0 + off], "3");
  mustHaveDirectoryCached(node_6, &metadata->backup_history[2 + off]);
  PathNode *node_6_a = findSubnode(node_6, "a", BH_unchanged, BPOL_track, 2, 1);
  mustHaveNonExisting(node_6_a, &metadata->backup_history[0 + off]);
  mustHaveDirectoryCached(node_6_a, &metadata->backup_history[2 + off]);
  PathNode *node_6_a_1 = findSubnode(node_6_a, "1", BH_unchanged, BPOL_track, 2, 0);
  mustHaveNonExisting(node_6_a_1, &metadata->backup_history[0 + off]);
  mustHaveRegularCached(node_6_a_1, &metadata->backup_history[2 + off], 20, (uint8_t *)"XXXXXXXXXXXXXXXXXXXX", 0);
  PathNode *node_6_2 = findSubnode(node_6, "2", BH_unchanged, BPOL_track, 2, 0);
  mustHaveNonExisting(node_6_2, &metadata->backup_history[0 + off]);
  mustHaveRegularCached(node_6_2, &metadata->backup_history[2 + off], 6, (uint8_t *)"FOOFOO", 0);
  PathNode *node_6_3 = findSubnode(node_6, "3", BH_unchanged, BPOL_track, 2, 0);
  mustHaveNonExisting(node_6_3, &metadata->backup_history[0 + off]);
  mustHaveRegularCached(node_6_3, &metadata->backup_history[2 + off], 2123, bin_hash, 0);

  PathNode *node_7 = findSubnode(files, "7", BH_unchanged, BPOL_track, 2, 4);
  mustHaveRegularStat(node_7, &metadata->backup_history[0 + off], 0, (uint8_t *)"K", 0);
  mustHaveDirectoryCached(node_7, &metadata->backup_history[2 + off]);
  PathNode *node_7_a = findSubnode(node_7, "a", BH_unchanged, BPOL_track, 2, 1);
  mustHaveNonExisting(node_7_a, &metadata->backup_history[1 + off]);
  mustHaveDirectoryCached(node_7_a, &metadata->backup_history[2 + off]);
  PathNode *node_7_a_1 = findSubnode(node_7_a, "1", BH_unchanged, BPOL_track, 2, 0);
  mustHaveNonExisting(node_7_a_1, &metadata->backup_history[1 + off]);
  mustHaveRegularCached(node_7_a_1, &metadata->backup_history[2 + off], 63, node_24_hash, 0);
  PathNode *node_7_b = findSubnode(node_7, "b", BH_unchanged, BPOL_track, 2, 2);
  mustHaveNonExisting(node_7_b, &metadata->backup_history[0 + off]);
  mustHaveDirectoryCached(node_7_b, &metadata->backup_history[2 + off]);
  PathNode *node_7_b_1 = findSubnode(node_7_b, "1", BH_unchanged, BPOL_track, 2, 0);
  mustHaveNonExisting(node_7_b_1, &metadata->backup_history[0 + off]);
  mustHaveRegularCached(node_7_b_1, &metadata->backup_history[2 + off], 14, (uint8_t *)"nested nested ", 0);
  PathNode *node_7_b_2 = findSubnode(node_7_b, "2", BH_unchanged, BPOL_track, 2, 0);
  mustHaveNonExisting(node_7_b_2, &metadata->backup_history[0 + off]);
  mustHaveRegularCached(node_7_b_2, &metadata->backup_history[2 + off], 1200, bin_c_1_hash, 0);
  PathNode *node_7_c = findSubnode(node_7, "c", BH_removed, BPOL_copy, 1, 2);
  mustHaveDirectoryCached(node_7_c, &metadata->backup_history[2 + off]);
  PathNode *node_7_c_1 = findSubnode(node_7_c, "1", BH_removed, BPOL_copy, 1, 0);
  mustHaveSymlinkLCached(node_7_c_1, &metadata->backup_history[2 + off], "/home");
  PathNode *node_7_c_2 = findSubnode(node_7_c, "2", BH_removed, BPOL_copy, 1, 0);
  mustHaveRegularCached(node_7_c_2, &metadata->backup_history[2 + off], 5, (uint8_t *)"dummy", 0);
  PathNode *node_7_d = findSubnode(node_7, "d", BH_removed, BPOL_mirror, 1, 2);
  mustHaveDirectoryCached(node_7_d, &metadata->backup_history[2 + off]);
  PathNode *node_7_d_1 = findSubnode(node_7_d, "1", BH_removed, BPOL_mirror, 1, 0);
  mustHaveRegularCached(node_7_d_1, &metadata->backup_history[2 + off], 18, (uint8_t *)"DUMMY-DUMMY-DUMMY-", 0);
  PathNode *node_7_d_2 = findSubnode(node_7_d, "2", BH_removed, BPOL_mirror, 1, 0);
  mustHaveSymlinkLCached(node_7_d_2, &metadata->backup_history[2 + off], "1");

  PathNode *node_8 = findSubnode(files, "8", BH_unchanged, BPOL_track, 2, 3);
  mustHaveRegularStat(node_8, &metadata->backup_history[0 + off], 518, node_42_hash, 0);
  struct stat node_8_stats = cachedStat(node_8->path, sStat);
  node_8_stats.st_mode++;
  mustHaveDirectoryStats(node_8, &metadata->backup_history[2 + off], node_8_stats);
  PathNode *node_8_a = findSubnode(node_8, "a", BH_unchanged, BPOL_track, 2, 1);
  mustHaveNonExisting(node_8_a, &metadata->backup_history[0 + off]);
  mustHaveDirectoryCached(node_8_a, &metadata->backup_history[2 + off]);
  PathNode *node_8_a_b = findSubnode(node_8_a, "b", BH_unchanged, BPOL_track, 2, 1);
  mustHaveNonExisting(node_8_a_b, &metadata->backup_history[0 + off]);
  mustHaveDirectoryCached(node_8_a_b, &metadata->backup_history[2 + off]);
  PathNode *node_8_a_b_1 = findSubnode(node_8_a_b, "1", BH_removed, BPOL_copy, 1, 0);
  mustHaveRegularCached(node_8_a_b_1, &metadata->backup_history[2 + off], 12, (uint8_t *)"_FILE__FILE_", 0);
  PathNode *node_8_c = findSubnode(node_8, "c", BH_unchanged, BPOL_track, 2, 1);
  mustHaveNonExisting(node_8_c, &metadata->backup_history[0 + off]);
  mustHaveDirectoryCached(node_8_c, &metadata->backup_history[2 + off]);
  PathNode *node_8_c_d = findSubnode(node_8_c, "d", BH_removed, BPOL_copy, 1, 1);
  mustHaveDirectoryCached(node_8_c_d, &metadata->backup_history[2 + off]);
  PathNode *node_8_c_d_1 = findSubnode(node_8_c_d, "1", BH_removed, BPOL_mirror, 1, 0);
  mustHaveRegularCached(node_8_c_d_1, &metadata->backup_history[2 + off], 1200, bin_c_1_hash, 0);
  PathNode *node_8_e = findSubnode(node_8, "e", BH_unchanged, BPOL_track, 2, 1);
  mustHaveNonExisting(node_8_e, &metadata->backup_history[0 + off]);
  mustHaveDirectoryCached(node_8_e, &metadata->backup_history[2 + off]);
  PathNode *node_8_e_f = findSubnode(node_8_e, "f", BH_removed, BPOL_mirror, 1, 1);
  mustHaveDirectoryCached(node_8_e_f, &metadata->backup_history[2 + off]);
  PathNode *node_8_e_f_1 = findSubnode(node_8_e_f, "1", BH_removed, BPOL_track, 1, 2);
  mustHaveDirectoryCached(node_8_e_f_1, &metadata->backup_history[2 + off]);
  PathNode *node_8_e_f_1_1 = findSubnode(node_8_e_f_1, "1", BH_removed, BPOL_track, 1, 0);
  mustHaveRegularCached(node_8_e_f_1_1, &metadata->backup_history[2 + off], 11, (uint8_t *)"nano backup", 0);
  PathNode *node_8_e_f_1_2 = findSubnode(node_8_e_f_1, "2", BH_removed, BPOL_track, 1, 0);
  mustHaveRegularCached(node_8_e_f_1_2, &metadata->backup_history[2 + off], 10, (uint8_t *)"NanoBackup", 0);

  PathNode *node_9 = findSubnode(files, "9", BH_unchanged, BPOL_track, 2, 0);
  mustHaveSymlinkLStat(node_9, &metadata->backup_history[0 + off], "/dev/null");
  mustHaveDirectoryCached(node_9, &metadata->backup_history[2 + off]);

  /* Finish the backup and perform additional checks. */
  completeBackup(metadata);
  assert_true(countItemsInDir("tmp/repo") == 18);
}

int main(void)
{
  initBackupCommon(1);

  testGroupStart("filetype changes in nodes with no policy");
  SearchNode *none_filetype_node    = searchTreeLoad(strWrap("generated-config-files/filetype-changes-none.txt"));

  initNoneFiletypeChange(none_filetype_node);
  change1NoneFiletypeChange(none_filetype_node);
  change2NoneFiletypeChange(none_filetype_node);
  postNoneFiletypeChange(none_filetype_node);
  restoreNoneFiletypeChange(none_filetype_node);
  testGroupEnd();

  testGroupStart("filetype changes in copied nodes");
  SearchNode *copy_filetype_node    = searchTreeLoad(strWrap("generated-config-files/filetype-changes-copy.txt"));

  initFiletypeChange(copy_filetype_node, BPOL_copy);
  modifyFiletypeChange(copy_filetype_node, BPOL_copy);
  changeFiletypeChange(copy_filetype_node, BPOL_copy);
  postFiletypeChange(copy_filetype_node, BPOL_copy);
  testGroupEnd();

  testGroupStart("filetype changes in mirrored nodes");
  SearchNode *mirror_filetype_node  = searchTreeLoad(strWrap("generated-config-files/filetype-changes-mirror.txt"));

  initFiletypeChange(mirror_filetype_node, BPOL_mirror);
  modifyFiletypeChange(mirror_filetype_node, BPOL_mirror);
  changeFiletypeChange(mirror_filetype_node, BPOL_mirror);
  postFiletypeChange(mirror_filetype_node, BPOL_mirror);
  testGroupEnd();

  testGroupStart("filetype changes in tracked nodes");
  SearchNode *track_filetype_node   = searchTreeLoad(strWrap("generated-config-files/filetype-changes-track.txt"));

  initFiletypeChange(track_filetype_node, BPOL_track);
  modifyFiletypeChange(track_filetype_node, BPOL_track);
  trackFiletypeChange(track_filetype_node);
  trackFiletypeChangePost(track_filetype_node, 0);
  trackFiletypeChangePost(track_filetype_node, 1);
  trackFiletypeChangePost(track_filetype_node, 2);
  trackFiletypeChangePost(track_filetype_node, 3);
  trackFiletypeChangePost(track_filetype_node, 4);
  testGroupEnd();
}
