def test_btrfs_create_snapshot(self, mock_commands):
        # setup mock
        mock_commands.btrfs_subvolume_snapshot.return_value = True
        mock_commands.mount.return_value = True
        mock_commands.umount.return_value = True
        f = lambda: datetime.datetime.fromtimestamp(0.0)
        # do it
        apt_btrfs = AptBtrfsSnapshot(
            fstab=os.path.join(self.testdir, "data", "fstab"))
        with mock.patch.multiple(apt_btrfs,
            _save_last_snapshot_time=nothing,
            _get_last_snapshot_time=f):
            res = apt_btrfs.create_btrfs_root_snapshot()
            # check results
            self.assertTrue(apt_btrfs.commands.mount.called)
            self.assertTrue(apt_btrfs.commands.umount.called)
            self.assertTrue(res)
            self.assertTrue(apt_btrfs.commands.btrfs_subvolume_snapshot.called)
            (args, kwargs) = \
            apt_btrfs.commands.btrfs_subvolume_snapshot.call_args

            self.assertTrue(len(args), 2)
            self.assertTrue(args[0].endswith("@"))
            self.assertTrue("@apt-snapshot-" in args[1])

            # again with a additional prefix for the snapshot
            prefix = "release-upgrade-natty-"
            res = apt_btrfs.create_btrfs_root_snapshot(prefix)
            (args, kwargs) = \
            apt_btrfs.commands.btrfs_subvolume_snapshot.call_args

            self.assertTrue("@apt-snapshot-release-upgrade-natty-" in args[1])
Example #2
0
    def test_btrfs_create_snapshot(self, mock_commands):
        # setup mock
        mock_commands.btrfs_subvolume_snapshot.return_value = True
        mock_commands.mount.return_value = True
        mock_commands.umount.return_value = True
        f = lambda: datetime.datetime.fromtimestamp(0.0)
        # do it
        apt_btrfs = AptBtrfsSnapshot(
            fstab=os.path.join(self.testdir, "data", "fstab"))
        with mock.patch.multiple(apt_btrfs,
                                 _save_last_snapshot_time=nothing,
                                 _get_last_snapshot_time=f):
            res = apt_btrfs.create_btrfs_root_snapshot()
            # check results
            self.assertTrue(apt_btrfs.commands.mount.called)
            self.assertTrue(apt_btrfs.commands.umount.called)
            self.assertTrue(res)
            self.assertTrue(apt_btrfs.commands.btrfs_subvolume_snapshot.called)
            (args, kwargs) = \
            apt_btrfs.commands.btrfs_subvolume_snapshot.call_args

            self.assertTrue(len(args), 2)
            self.assertTrue(args[0].endswith("@"))
            self.assertTrue("@apt-snapshot-" in args[1])

            # again with a additional prefix for the snapshot
            prefix = "release-upgrade-natty-"
            res = apt_btrfs.create_btrfs_root_snapshot(prefix)
            (args, kwargs) = \
            apt_btrfs.commands.btrfs_subvolume_snapshot.call_args

            self.assertTrue("@apt-snapshot-release-upgrade-natty-" in args[1])
 def test_parser_snapshot_to_datetime(self):
     apt_btrfs = AptBtrfsSnapshot(
         fstab=os.path.join(self.testdir, "data", "fstab"))
     e = "@apt-snapshot-2017-12-15_11:26:24"
     t_parsed = apt_btrfs._parse_snapshot_to_datetime(e)
     t_expected = datetime.datetime(2017, 12, 15, 11, 26, 24)
     self.assertEqual(t_parsed, t_expected)
 def test_parser_older_than_to_datetime(self):
     apt_btrfs = AptBtrfsSnapshot(
         fstab=os.path.join(self.testdir, "data", "fstab"),
         sandbox=self.sandbox)
     t = apt_btrfs._parse_older_than_to_datetime("5d")
     e = datetime.datetime.now() - datetime.timedelta(5)
     # Check that t is within a second of e
     self.assertTrue(e - t < datetime.timedelta(0, 1))
 def test_mount_btrfs_root_volume(self, mock_commands):
     apt_btrfs = AptBtrfsSnapshot(
         fstab=os.path.join(self.testdir, "data", "fstab"))
     mock_commands.mount.return_value = True
     mock_commands.umount.return_value = True
     mp = apt_btrfs.mount_btrfs_root_volume()
     self.assertTrue("apt-btrfs-snapshot-mp-" in mp)
     self.assertTrue(apt_btrfs.umount_btrfs_root_volume())
     self.assertFalse(os.path.exists(mp))
 def test_mount_btrfs_root_volume(self, mock_commands):
     apt_btrfs = AptBtrfsSnapshot(
         fstab=os.path.join(self.testdir, "data", "fstab"))
     mock_commands.mount.return_value = True
     mock_commands.umount.return_value = True
     mp = apt_btrfs.mount_btrfs_root_volume()
     self.assertTrue("apt-btrfs-snapshot-mp-" in mp)
     self.assertTrue(apt_btrfs.umount_btrfs_root_volume())
     self.assertFalse(os.path.exists(mp))
 def test_btrfs_delete_snapshot(self, mock_commands):
     # setup mock
     mock_commands.btrfs_delete_snapshot.return_value = True
     mock_commands.mount.return_value = True
     mock_commands.umount.return_value = True
     # do it
     apt_btrfs = AptBtrfsSnapshot(
         fstab=os.path.join(self.testdir, "data", "fstab"))
     res = apt_btrfs.delete_snapshot("lala")
     self.assertTrue(res)
     self.assertTrue(apt_btrfs.commands.mount.called)
     self.assertTrue(apt_btrfs.commands.umount.called)
     self.assertTrue(apt_btrfs.commands.btrfs_delete_snapshot.called)
     (args, kwargs) = apt_btrfs.commands.btrfs_delete_snapshot.call_args
     self.assertTrue(args[0].endswith("/lala"))
 def test_btrfs_delete_snapshot(self, mock_commands):
     # setup mock
     mock_commands.btrfs_delete_snapshot.return_value = True
     mock_commands.mount.return_value = True
     mock_commands.umount.return_value = True
     # do it
     apt_btrfs = AptBtrfsSnapshot(
         fstab=os.path.join(self.testdir, "data", "fstab"))
     res = apt_btrfs.delete_snapshot("lala")
     self.assertTrue(res)
     self.assertTrue(apt_btrfs.commands.mount.called)
     self.assertTrue(apt_btrfs.commands.umount.called)
     self.assertTrue(apt_btrfs.commands.btrfs_delete_snapshot.called)
     (args, kwargs) = apt_btrfs.commands.btrfs_delete_snapshot.call_args
     self.assertTrue(args[0].endswith("/lala"))
 def test_fstab_noatime(self, mock_stdout, mock_stderr):
     mock_stdout.side_effect = StringIO()
     mock_stderr.side_effect = StringIO()
     apt_btrfs = AptBtrfsSnapshot(
         fstab=os.path.join(self.testdir, "data", "fstab.bug833980"))
     # ensure our test is right
     entry = apt_btrfs._get_supported_btrfs_root_fstab_entry()
     self.assertTrue("noatime" in entry.options)
     # ensure we get the right exception
     self.assertRaises(AptBtrfsRootWithNoatimeError,
                       apt_btrfs.get_btrfs_root_snapshots_list, "1d")
     # and the right return codes from the commands
     self.assertEqual(apt_btrfs.clean_btrfs_root_snapshots_older_than("1d"),
                      False)
     self.assertEqual(apt_btrfs.print_btrfs_root_snapshots_older_than("1d"),
                      False)
    def test_convert(self, mock_stdout):
        mock_stdout.side_effect = StringIO()
        subprocess.call(["python", "../convert.py", self.sandbox])
        apt_btrfs = AptBtrfsSnapshot(
            fstab=os.path.join(self.testdir, "data", "fstab"),
            test_mp=self.sandbox)
        apt_btrfs.tree()
        output = extract_stdout(mock_stdout)
        expected = u"""@ (none)
@apt-snapshot-2013-08-15_23:04:32 (+1)
@apt-snapshot-2013-08-14_20:26:45 (^13)
@apt-snapshot-2013-08-13_12:23:41 (+8)
@apt-snapshot-2013-08-13_03:11:29 (+6 ^3)
@apt-snapshot-2013-08-12_02:04:06 (unknown)
×  
"""
        self.assertEqual(output, expected)
        snapshots.setup(self.sandbox)
        # The following change information tests have been meticulously 
        # verified.
        changes = Snapshot("@apt-snapshot-2013-08-15_23:04:32").changes
        self.assertEqual(changes['install'], [(u'handbrake-gtk',
            u'5698svnppa1~precise1')])
        self.assertEqual(changes['auto-install'], [])
        self.assertEqual(changes['upgrade'], [])
        self.assertEqual(changes['remove'], [])
        self.assertEqual(changes['purge'], [])
        changes = Snapshot("@apt-snapshot-2013-08-14_20:26:45").changes
        self.assertEqual(changes['install'], [])
        self.assertEqual(changes['auto-install'], [])
        self.assertEqual(changes['upgrade'], [(u'firefox', u'22.0+build2-0ubuntu0.12.04.2, 23.0+build2-0ubuntu0.12.04.1'), (u'firefox-locale-en', u'22.0+build2-0ubuntu0.12.04.2, 23.0+build2-0ubuntu0.12.04.1'), (u'gimp-help-common', u'2.6.1-1, 1:2.8-0precise16~ppa'), (u'gimp-help-en', u'2.6.1-1, 1:2.8-0precise16~ppa'), (u'gimp-help-fr', u'2.6.1-1, 1:2.8-0precise16~ppa'), (u'jockey-common', u'0.9.7-0ubuntu7.7, 0.9.7-0ubuntu7.9'), (u'jockey-gtk', u'0.9.7-0ubuntu7.7, 0.9.7-0ubuntu7.9'), (u'lsb-base', u'4.0-0ubuntu20.2, 4.0-0ubuntu20.3'), (u'lsb-release', u'4.0-0ubuntu20.2, 4.0-0ubuntu20.3'), (u'mintinstall-icons', u'1.0.5, 1.0.7'), (u'python-problem-report', u'2.0.1-0ubuntu17.3, 2.0.1-0ubuntu17.4'), (u'thunderbird', u'17.0.7+build1-0ubuntu0.12.04.1, 17.0.8+build1-0ubuntu0.12.04.1'), (u'thunderbird-gnome-support', u'17.0.7+build1-0ubuntu0.12.04.1, 17.0.8+build1-0ubuntu0.12.04.1')])
        self.assertEqual(changes['remove'], [])
        self.assertEqual(changes['purge'], [])
        changes = Snapshot("@apt-snapshot-2013-08-13_12:23:41").changes
        self.assertEqual(changes['install'], [(u'0ad', u'0.0.13-0ubuntu1~12.04~wfg1')])
        self.assertEqual(changes['auto-install'], [(u'0ad-data', u'0.0.13-0ubuntu1~12.04~wfg1'), (u'0ad-data-common', u'0.0.13-0ubuntu1~12.04~wfg1'), (u'libboost-filesystem1.46.1', u'1.46.1-7ubuntu3'), (u'libboost-signals1.46.1', u'1.46.1-7ubuntu3'), (u'libboost-system1.46.1', u'1.46.1-7ubuntu3'), (u'libenet1a', u'1.3.3-2ubuntu1'), (u'libnvtt2', u'2.0.8-1+dfsg-2')])
        self.assertEqual(changes['upgrade'], [])
        self.assertEqual(changes['remove'], [])
        self.assertEqual(changes['purge'], [])
        changes = Snapshot("@apt-snapshot-2013-08-13_03:11:29").changes
        self.assertEqual(changes['install'], [])
        self.assertEqual(changes['auto-install'], [(u'libamd2.2.0', u'1:3.4.0-2ubuntu3'), (u'libbabl-0.1-0', u'0.1.11-1precise0~ppa'), (u'libblas3gf', u'1.2.20110419-2ubuntu1'), (u'libgegl-0.2-0', u'0.2.1-1precise0~ppa'), (u'libopenraw1', u'0.0.8-3build1'), (u'libumfpack5.4.0', u'1:3.4.0-2ubuntu3')])
        self.assertEqual(changes['upgrade'], [(u'gimp', u'2.6.12-1ubuntu1.2, 2.8.6-0precise1~ppa'), (u'gimp-data', u'2.6.12-1ubuntu1.2, 2.8.6-0precise1~ppa'), (u'libgimp2.0', u'2.6.12-1ubuntu1.2, 2.8.6-0precise1~ppa')])
        self.assertEqual(changes['remove'], [])
        self.assertEqual(changes['purge'], [])
        changes = Snapshot("@apt-snapshot-2013-08-12_02:04:06").changes
        self.assertEqual(changes, None)
 def test_fstab_noatime(self, mock_stdout, mock_stderr):
     mock_stdout.side_effect = StringIO()
     mock_stderr.side_effect = StringIO()
     apt_btrfs = AptBtrfsSnapshot(
         fstab=os.path.join(self.testdir, "data", "fstab.bug833980"))
     # ensure our test is right
     entry = apt_btrfs._get_supported_btrfs_root_fstab_entry()
     self.assertTrue("noatime" in entry.options)
     # ensure we get the right exception
     self.assertRaises(AptBtrfsRootWithNoatimeError,
                       apt_btrfs.get_btrfs_root_snapshots_list,
                       "1d")
     # and the right return codes from the commands
     self.assertEqual(apt_btrfs.clean_btrfs_root_snapshots_older_than("1d"),
                      False)
     self.assertEqual(apt_btrfs.print_btrfs_root_snapshots_older_than("1d"),
                      False)
 def test_btrfs_create_snapshot(self, mock_commands):
     # setup mock
     mock_commands.btrfs_subvolume_snapshot.return_value = True
     mock_commands.mount.return_value = True
     mock_commands.umount.return_value = True
     # do it
     apt_btrfs = AptBtrfsSnapshot(
         fstab=os.path.join(self.testdir, "data", "fstab"))
     res = apt_btrfs.create_btrfs_root_snapshot()
     # check results
     self.assertTrue(apt_btrfs.commands.mount.called)
     self.assertTrue(apt_btrfs.commands.umount.called)
     self.assertTrue(res)
     self.assertTrue(apt_btrfs.commands.btrfs_subvolume_snapshot.called)
     (args, kwargs) = apt_btrfs.commands.btrfs_subvolume_snapshot.call_args
     self.assertTrue(len(args), 2)
     self.assertTrue(args[0].endswith("@"))
     self.assertTrue("@apt-snapshot-" in args[1])
     # again with a additional prefix for the snapshot
     res = apt_btrfs.create_btrfs_root_snapshot("release-upgrade-natty-")
     (args, kwargs) = apt_btrfs.commands.btrfs_subvolume_snapshot.call_args
     self.assertTrue("@apt-snapshot-release-upgrade-natty-" in args[1])
 def test_btrfs_create_snapshot(self, mock_commands):
     # setup mock
     mock_commands.btrfs_subvolume_snapshot.return_value = True
     mock_commands.mount.return_value = True
     mock_commands.umount.return_value = True
     # do it
     apt_btrfs = AptBtrfsSnapshot(
         fstab=os.path.join(self.testdir, "data", "fstab"))
     res = apt_btrfs.create_btrfs_root_snapshot()
     # check results
     self.assertTrue(apt_btrfs.commands.mount.called)
     self.assertTrue(apt_btrfs.commands.umount.called)
     self.assertTrue(res)
     self.assertTrue(apt_btrfs.commands.btrfs_subvolume_snapshot.called)
     (args, kwargs) = apt_btrfs.commands.btrfs_subvolume_snapshot.call_args
     self.assertTrue(len(args), 2)
     self.assertTrue(args[0].endswith("@"))
     self.assertTrue("@apt-snapshot-" in args[1])
     # again with a additional prefix for the snapshot
     res = apt_btrfs.create_btrfs_root_snapshot("release-upgrade-natty-")
     (args, kwargs) = apt_btrfs.commands.btrfs_subvolume_snapshot.call_args
     self.assertTrue("@apt-snapshot-release-upgrade-natty-" in args[1])
 def test_fstab_detect_snapshot(self, mock_commands):
     #Using python-mock 0.7 style, for precise compatibility
     mock_commands.side_effect = lambda f: f in ('/sbin/btrfs')
     apt_btrfs = AptBtrfsSnapshot(
         fstab=os.path.join(self.testdir, "data", "fstab"))
     self.assertTrue(apt_btrfs.snapshots_supported())
     apt_btrfs = AptBtrfsSnapshot(
         fstab=os.path.join(self.testdir, "data", "fstab.no-btrfs"))
     self.assertFalse(apt_btrfs.snapshots_supported())
     apt_btrfs = AptBtrfsSnapshot(
         fstab=os.path.join(self.testdir, "data", "fstab.bug806065"))
     self.assertFalse(apt_btrfs.snapshots_supported())
     apt_btrfs = AptBtrfsSnapshot(
         fstab=os.path.join(self.testdir, "data", "fstab.bug872145"))
     self.assertTrue(apt_btrfs.snapshots_supported())
 def setUp(self):#, mock_btrfs_subvolume_snapshot, mock_btrfs_delete_snapshot):
     
     self.testdir = os.path.dirname(os.path.abspath(__file__))
     
     # make a copy of a model btrfs subvol tree
     model_root = os.path.join(self.testdir, "data", "model_root")
     self.sandbox = os.path.join(self.testdir, "data", "root3")
     if os.path.exists(self.sandbox):
         shutil.rmtree(self.sandbox)
     shutil.copytree(model_root, self.sandbox, symlinks=True)
     
     # setup snapshot class
     self.apt_btrfs = AptBtrfsSnapshot(
         fstab=os.path.join(self.testdir, "data", "fstab"),
         sandbox=self.sandbox)
 def test_parser_older_than_to_datetime(self):
     apt_btrfs = AptBtrfsSnapshot(
         fstab=os.path.join(self.testdir, "data", "fstab"))
     t = apt_btrfs._parse_older_than_to_datetime("5d")
     self.assertTrue(t <= datetime.datetime.now() - datetime.timedelta(5))
 def test_parser_older_than_to_unixtime(self):
     apt_btrfs = AptBtrfsSnapshot(
         fstab=os.path.join(self.testdir, "data", "fstab"))
     t = apt_btrfs._parse_older_than_to_unixtime("5d")
     self.assertTrue((t < time.time()) - (5 * 60 * 60 * 24))
 def test_fstab_get_uuid(self):
     apt_btrfs = AptBtrfsSnapshot(
         fstab=os.path.join(self.testdir, "data", "fstab"))
     self.assertEqual(apt_btrfs._uuid_for_mountpoint("/"),
                      "UUID=fe63f598-1906-478e-acc7-f74740e78d1f")
 def test_fstab_detect_snapshot(self, mock_commands):
     # Using python-mock 0.7 style, for precise compatibility
     mock_commands.side_effect = lambda f: f == 'btrfs'
     apt_btrfs = AptBtrfsSnapshot(
         fstab=os.path.join(self.testdir, "data", "fstab"))
     self.assertTrue(apt_btrfs.snapshots_supported())
     apt_btrfs = AptBtrfsSnapshot(
         fstab=os.path.join(self.testdir, "data", "fstab.no-btrfs"))
     self.assertFalse(apt_btrfs.snapshots_supported())
     apt_btrfs = AptBtrfsSnapshot(
         fstab=os.path.join(self.testdir, "data", "fstab.bug806065"))
     self.assertFalse(apt_btrfs.snapshots_supported())
     apt_btrfs = AptBtrfsSnapshot(
         fstab=os.path.join(self.testdir, "data", "fstab.bug872145"))
     self.assertTrue(apt_btrfs.snapshots_supported())
 def test_parser_older_than_to_unixtime(self):
     apt_btrfs = AptBtrfsSnapshot(
         fstab=os.path.join(self.testdir, "data", "fstab"))
     t = apt_btrfs._parse_older_than_to_unixtime("5d")
     self.assertTrue((t < time.time()) - (5 * 60 * 60 * 24))
class TestSnapshotting(unittest.TestCase):
    """ A lengthy setUp function copies a model subvolume tree with parent
        links and some package change info. A couple of LowLevelCommand
        functions are overwritten. All that allows test functions to do some
        real work on the model subvolume tree without any risk of messing
        anything up.
    """
    def setUp(self):#, mock_btrfs_subvolume_snapshot, mock_btrfs_delete_snapshot):
        
        self.testdir = os.path.dirname(os.path.abspath(__file__))
        
        # make a copy of a model btrfs subvol tree
        model_root = os.path.join(self.testdir, "data", "model_root")
        self.sandbox = os.path.join(self.testdir, "data", "root3")
        if os.path.exists(self.sandbox):
            shutil.rmtree(self.sandbox)
        shutil.copytree(model_root, self.sandbox, symlinks=True)
        
        # setup snapshot class
        self.apt_btrfs = AptBtrfsSnapshot(
            fstab=os.path.join(self.testdir, "data", "fstab"),
            sandbox=self.sandbox)

    def tearDown(self):
        del self.apt_btrfs
        shutil.rmtree(self.sandbox)
        try: print(self.output)
        except: pass

    def do_and_find_new(self, function, *args, **kwargs):
        old_dirlist = os.listdir(self.sandbox)
        res = function(*args, **kwargs)
        new_dirlist = os.listdir(self.sandbox)
        newdir = None
        for i in new_dirlist:
            if i not in old_dirlist:
                newdir = i
        return res, newdir
    
    def assert_child_parent_linked(self, child, parent):
        # check parent has been fixed in children
        parent_file = os.path.join(self.sandbox, 
            child, PARENT_LINK)
        self.assertEqual(os.readlink(parent_file), 
            os.path.join(PARENT_DOTS, parent))
    
    def load_changes(self, whose):
        changes_file = os.path.join(self.sandbox, whose, CHANGES_FILE)
        self.assertTrue(os.path.exists(changes_file))
        history = pickle.load(open(changes_file, "rb"))
        return history
        
    def test_parser_older_than_to_datetime(self):
        apt_btrfs = AptBtrfsSnapshot(
            fstab=os.path.join(self.testdir, "data", "fstab"),
            sandbox=self.sandbox)
        t = apt_btrfs._parse_older_than_to_datetime("5d")
        e = datetime.datetime.now() - datetime.timedelta(5)
        # Check that t is within a second of e
        self.assertTrue(e - t < datetime.timedelta(0, 1))

    @mock.patch('sys.stdout')
    def test_btrfs_create_snapshot(self, mock_stdout):
        mock_stdout.side_effect = StringIO()
        if os.path.exists('/tmp/apt_last_snapshot'):
            os.remove('/tmp/apt_last_snapshot')
        res, newdir = self.do_and_find_new(self.apt_btrfs.create)
        # check results
        self.assertTrue(res)
        self.assert_child_parent_linked("@", newdir)
        self.assert_child_parent_linked(newdir, 
            SNAP_PREFIX + "2013-08-06_13:26:30")
        
        args = LowLevelCommands.btrfs_subvolume_snapshot.call_args[0]
        self.assertTrue(len(args), 2)
        self.assertTrue(args[0].endswith("@"))
        self.assertTrue(SNAP_PREFIX in args[1])
        
        history = self.load_changes(newdir)
        self.assertEqual(len(history['install']), 10)
        
        # test skipping if recent
        res = self.apt_btrfs.create()
        output = extract_stdout(mock_stdout)
        expected = "A recent snapshot already exists: "
        self.assertTrue(output.startswith(expected))
        
        # test recent but tag -> create anyway
        res, newdir = self.do_and_find_new(self.apt_btrfs.create, "-tag")
        # check results
        self.assertTrue(res)
        self.assertTrue(newdir.endswith("-tag"))
        self.assert_child_parent_linked("@", newdir)
        
        # test disabling by shell variable
        os.environ['APT_NO_SNAPSHOTS'] = '1'
        res = self.apt_btrfs.create()
        output = extract_stdout(mock_stdout, last_line_only=True)
        expected = "Shell variable APT_NO_SNAPSHOTS found, skipping creation"
        self.assertTrue(output.startswith(expected))
        
        # shell var AND tag supplied -> create anyway
        res = self.apt_btrfs.create("tag")
        output = extract_stdout(mock_stdout, last_line_only=True)
        expected = "Shell variable APT_NO_SNAPSHOTS found, but tag supplied, "
        expected += "creating snapshot"
        self.assertTrue(output.startswith(expected))
        del os.environ['APT_NO_SNAPSHOTS']

    def test_btrfs_delete_snapshot(self):
        which = SNAP_PREFIX + "2013-07-31_00:00:04"
        res = self.apt_btrfs.delete(which)
        self.assertTrue(res)
        self.assertFalse(os.path.exists(os.path.join(self.sandbox, which)))
        # check parent has been fixed in children
        self.assert_child_parent_linked(SNAP_PREFIX + "2013-08-01_19:53:16",
            SNAP_PREFIX + "2013-07-26_14:50:53")
        self.assert_child_parent_linked(
            SNAP_PREFIX + "2013-07-31_12:53:16-raring-to-go", 
            SNAP_PREFIX + "2013-07-26_14:50:53")
        # check that the change records have been consolidated
        history = self.load_changes(SNAP_PREFIX + "2013-08-01_19:53:16")
        self.assertEqual(history['install'], [('two', '2')])
        self.assertEqual(history['auto-install'], history['purge'], [])
        self.assertEqual(history['upgrade'], history['remove'], [])      
        history = self.load_changes(
            SNAP_PREFIX + "2013-07-31_12:53:16-raring-to-go")
        self.assertEqual(history['install'], [('one', '1.1'), ('three', '3')])
        self.assertEqual(history['upgrade'], [('zero', '0, 0.1')])
        self.assertEqual(history['auto-install'], [])
        self.assertEqual(history['purge'], history['remove'], [])

    @mock.patch('sys.stdin')
    @mock.patch('sys.stdout')
    def test_btrfs_set_default(self, mock_stdout, mock_stdin):
        mock_stdout.side_effect = StringIO()
        mock_stdin.side_effect = StringIO()
        res, newdir = self.do_and_find_new(self.apt_btrfs.set_default, 
            SNAP_PREFIX + "2013-08-01_19:53:16",
            tag="-tag")
        # check results
        self.assertTrue(res)
        # check for backup's parent and the 
        # record of dpkg changes
        self.assertTrue(newdir.endswith("-tag"))
        
        self.assert_child_parent_linked(newdir, 
            SNAP_PREFIX + "2013-08-06_13:26:30")

        history = self.load_changes(newdir)
        self.assertEqual(len(history['install']), 10)
                   
        self.assertTrue(os.path.exists(os.path.join(self.sandbox, 
            SNAP_PREFIX + "2013-08-01_19:53:16")))
        
        self.assert_child_parent_linked("@",
            SNAP_PREFIX + "2013-08-01_19:53:16")
        
        args = LowLevelCommands.btrfs_subvolume_snapshot.call_args[0]
        self.assertTrue(len(args), 2)
        self.assertTrue(args[1].endswith("@apt-btrfs-staging"))
        self.assertTrue(SNAP_PREFIX + "" in args[0])

    @mock.patch('sys.stdin')
    @mock.patch('sys.stdout')
    def test_btrfs_set_default_tag_prompting(self, mock_stdout, mock_stdin):
        mock_stdout.side_effect = StringIO()
        mock_stdin.side_effect = StringIO()
        mock_stdin.readline.return_value = "tag"
        res, newdir = self.do_and_find_new(self.apt_btrfs.set_default, 
            SNAP_PREFIX + "2013-08-01_19:53:16")
        # check for backup existance (hard) and its tag (easy)
        pos = len(SNAP_PREFIX)
        self.assertEqual(newdir[pos+19:], "-tag")

    @mock.patch('sys.stdout')
    def test_btrfs_set_default_staging_exists(self, mock_stdout):
        mock_stdout.side_effect = StringIO()
        os.mkdir(os.path.join(self.sandbox, "@apt-btrfs-staging"))
        message = "Reserved directory @apt-btrfs-staging exists\n"
        message += "Please remove from btrfs volume root before trying again"
        with self.assertRaisesRegexp(Exception, message):
            res = self.apt_btrfs.set_default(
                SNAP_PREFIX + "2013-08-01_19:53:16", "tag")

    @mock.patch('sys.stdout')
    def test_rollback_one(self, mock_stdout):
        mock_stdout.side_effect = StringIO()
        res, newdir = self.do_and_find_new(self.apt_btrfs.rollback, tag="-tag")
        self.assert_child_parent_linked("@", 
            SNAP_PREFIX + "2013-08-06_13:26:30")
        # check for backup's tag
        pos = len(SNAP_PREFIX)
        self.assertEqual(newdir[pos+19:], "-tag")
    
    @mock.patch('sys.stdout')
    def test_rollback_five(self, mock_stdout):
        mock_stdout.side_effect = StringIO()
        self.apt_btrfs.rollback(5, "-tag")
        self.assert_child_parent_linked("@", 
            SNAP_PREFIX + "2013-07-26_14:50:53")

    @mock.patch('sys.stdout')
    def test_rollback_six(self, mock_stdout):
        mock_stdout.side_effect = StringIO()
        with self.assertRaisesRegexp(Exception, "Can't rollback that far"):
            self.apt_btrfs.rollback(6, "-tag")
        self.assert_child_parent_linked("@", 
            SNAP_PREFIX + "2013-08-06_13:26:30")

    def test_get_status(self):
        date, history = self.apt_btrfs._get_status()
        self.assertEqual(len(history['install']), 10)
        self.assertEqual(len(history['auto-install']), 7)
        self.assertEqual(history['remove'], history['purge'], [])
        self.assertEqual(history['upgrade'], [])     
    
    @mock.patch('sys.stdout')
    def test_tree_view(self, mock_stdout): 
        mock_stdout.side_effect = StringIO()
        self.maxDiff = None
        self.apt_btrfs.tree()
        output = extract_stdout(mock_stdout)
        expected = """@ (+17)
│  
│  @apt-snapshot-2013-08-09_21:09:40 (unknown)
│  @apt-snapshot-2013-08-09_21:08:01 (unknown)
│  │  
│  │  @apt-snapshot-2013-08-09_21:06:32 (unknown)
│  │  ×  
│  │     @apt-snapshot-2013-08-09_21:05:56 (unknown)
│  ├─────┘
│  @apt-snapshot-2013-08-09_21:04:37 (unknown)
│  @apt-snapshot-2013-08-08_18:44:47 (unknown)
├──┘
@apt-snapshot-2013-08-06_13:26:30 (unknown)
@apt-snapshot-2013-08-06_00:29:05 (unknown)
│  
│  @apt-snapshot-2013-08-09_21:06:00 (unknown)
│  │  
│  │  @apt-snapshot-2013-08-07_18:00:42 (unknown)
│  │  ×  
│  │     @apt-snapshot-2013-08-05_04:30:58 (unknown)
│  ├─────┘
│  @apt-snapshot-2013-08-02_00:24:00 (unknown)
├──┘
@apt-snapshot-2013-08-01_19:53:16 (+1 -1)
│  
│  @apt-snapshot-2013-07-31_12:53:16-raring-to-go (+1 ^2)
├──┘
@apt-snapshot-2013-07-31_00:00:04 (+1)
@apt-snapshot-2013-07-26_14:50:53 (unknown)
×  
"""
        self.assertEqual(output, expected)
        
        Snapshot(SNAP_PREFIX + "2013-08-09_21:06:00").parent = None
        # reinitialize snapshots global variables.
        snapshots.setup(self.sandbox)
        self.apt_btrfs.tree()
        output = extract_stdout(mock_stdout)
        expected += """@ (+17)
│  
│  @apt-snapshot-2013-08-09_21:09:40 (unknown)
│  @apt-snapshot-2013-08-09_21:08:01 (unknown)
│  │  
│  │  @apt-snapshot-2013-08-09_21:06:32 (unknown)
│  │  ×  
│  │     @apt-snapshot-2013-08-09_21:06:00 (unknown)
│  │     ×  
│  │        @apt-snapshot-2013-08-09_21:05:56 (unknown)
│  ├────────┘
│  @apt-snapshot-2013-08-09_21:04:37 (unknown)
│  @apt-snapshot-2013-08-08_18:44:47 (unknown)
├──┘
@apt-snapshot-2013-08-06_13:26:30 (unknown)
@apt-snapshot-2013-08-06_00:29:05 (unknown)
│  
│  @apt-snapshot-2013-08-07_18:00:42 (unknown)
│  ×  
│     @apt-snapshot-2013-08-05_04:30:58 (unknown)
│     @apt-snapshot-2013-08-02_00:24:00 (unknown)
├─────┘
@apt-snapshot-2013-08-01_19:53:16 (+1 -1)
│  
│  @apt-snapshot-2013-07-31_12:53:16-raring-to-go (+1 ^2)
├──┘
@apt-snapshot-2013-07-31_00:00:04 (+1)
@apt-snapshot-2013-07-26_14:50:53 (unknown)
×  
"""
        self.assertEqual(output, expected)

    def test_tag(self):
        self.apt_btrfs.tag(SNAP_PREFIX + "2013-07-31_00:00:04", "-tag")
        dirlist = os.listdir(self.sandbox)
        self.assertIn(SNAP_PREFIX + "2013-07-31_00:00:04-tag", dirlist)
        self.assertNotIn(SNAP_PREFIX + "2013-07-31_00:00:04", dirlist)
        
        self.apt_btrfs.tag(SNAP_PREFIX + "2013-07-31_12:53:16-raring-to-go",
            "-tag")
        dirlist = os.listdir(self.sandbox)
        self.assertIn(SNAP_PREFIX + "2013-07-31_12:53:16-tag", dirlist)
        self.assertNotIn(SNAP_PREFIX + "2013-07-31_12:53:16-raring-to-go",
            dirlist)
        # check parent has been fixed in children
        self.assert_child_parent_linked(SNAP_PREFIX + "2013-08-01_19:53:16",
            SNAP_PREFIX + "2013-07-31_00:00:04-tag")
        self.assert_child_parent_linked(SNAP_PREFIX + "2013-07-31_12:53:16-tag",
            SNAP_PREFIX + "2013-07-31_00:00:04-tag")

    def test_clean_apt_cache(self):
        self.apt_btrfs.clean()
        path = os.path.join(self.sandbox, "@/var/cache/apt/archives")
        self.assertTrue(os.path.exists(os.path.join(path, "a.deb")))
        path = os.path.join(self.sandbox, 
            SNAP_PREFIX + "2013-08-07_18:00:42", "var/cache/apt/archives")
        self.assertFalse(os.path.exists(os.path.join(path, "a.deb")))
        self.assertFalse(os.path.exists(os.path.join(path, "b.deb")))
        self.assertTrue(os.path.exists(os.path.join(path, "other_file")))

    def test_delete_older_than(self):
        old_dirlist = os.listdir(self.sandbox)
        self.apt_btrfs.delete_older_than(
            datetime.datetime(2013, 8, 7, 18, 0, 42))
        dirlist = os.listdir(self.sandbox)
        self.assertEqual(len(dirlist), len(old_dirlist) - 4)
        self.assertNotIn(SNAP_PREFIX + "2013-07-26_14:50:53", dirlist)
        self.assertNotIn(SNAP_PREFIX + "2013-08-06_00:29:05", dirlist)
        self.assertNotIn(SNAP_PREFIX + "2013-08-05_04:30:58", dirlist)
        self.assertNotIn(SNAP_PREFIX + "2013-08-02_00:24:00", dirlist)
        
        old_dirlist = os.listdir(self.sandbox)
        self.apt_btrfs.delete_older_than(
            datetime.datetime(2013, 8, 9, 21, 9, 40))
        dirlist = os.listdir(self.sandbox)
        self.assertEqual(len(dirlist), len(old_dirlist) - 8)
        self.assertNotIn(SNAP_PREFIX + "2013-08-09_21:08:01", dirlist)
        self.assertNotIn(SNAP_PREFIX + "2013-08-09_21:06:32", dirlist)
        self.assertNotIn(SNAP_PREFIX + "2013-08-09_21:06:00", dirlist)
        self.assertNotIn(SNAP_PREFIX + "2013-08-09_21:05:56", dirlist)
        
        self.assertNotIn(SNAP_PREFIX + "2013-08-09_21:04:37", dirlist)
        self.assertNotIn(SNAP_PREFIX + "2013-08-08_18:44:47", dirlist)
        self.assertNotIn(SNAP_PREFIX + "2013-08-07_18:00:42", dirlist)
        self.assertNotIn(SNAP_PREFIX + "2013-08-01_19:53:16", dirlist)

    @mock.patch('sys.stdout')
    def test_recent(self, mock_stdout):
        mock_stdout.side_effect = StringIO()
        res = self.apt_btrfs.recent(5, "@")
        self.assertTrue(res)
        output = extract_stdout(mock_stdout)
        expected = """@ and its predecessors. Showing 5 snapshots.

dpkg history for @
- installs (10):
    linux-generic-lts-raring, linux-headers-3.9.0-030900,
    linux-headers-3.9.0-030900-generic, linux-headers-generic-lts-raring,
    linux-image-3.8.0-27-generic, linux-image-3.9.0-030900-generic,
    linux-image-generic-lts-raring, nemo-dropbox, picasa, python-mock
- auto-installs (7):
    lib32asound2, lib32z1, libc6-i386, linux-headers-3.8.0-27,
    linux-headers-3.8.0-27-generic, lynx-cur, python-gpgme

dpkg history for @apt-snapshot-2013-08-06_13:26:30
- No packages operations recorded

dpkg history for @apt-snapshot-2013-08-06_00:29:05
- No packages operations recorded

dpkg history for @apt-snapshot-2013-08-01_19:53:16
- installs (1):
    two
- removes (1):
    one

dpkg history for @apt-snapshot-2013-07-31_00:00:04
- installs (1):
    one
"""
        self.assertEqual(output, expected)
        res = self.apt_btrfs.recent(9, "@apt-snapshot-2013-08-09_21:08:01")
        self.assertTrue(res)
        output = extract_stdout(mock_stdout)
        expected += """@apt-snapshot-2013-08-09_21:08:01 and its predecessors. Showing 9 snapshots.

dpkg history for @apt-snapshot-2013-08-09_21:08:01
- No packages operations recorded

dpkg history for @apt-snapshot-2013-08-09_21:04:37
- No packages operations recorded

dpkg history for @apt-snapshot-2013-08-08_18:44:47
- No packages operations recorded

dpkg history for @apt-snapshot-2013-08-06_13:26:30
- No packages operations recorded

dpkg history for @apt-snapshot-2013-08-06_00:29:05
- No packages operations recorded

dpkg history for @apt-snapshot-2013-08-01_19:53:16
- installs (1):
    two
- removes (1):
    one

dpkg history for @apt-snapshot-2013-07-31_00:00:04
- installs (1):
    one

dpkg history for @apt-snapshot-2013-07-26_14:50:53
- No packages operations recorded
"""
        self.assertEqual(output, expected)

    def test_prune(self):
        message = "Snapshot is not the end of a branch"
        with self.assertRaisesRegexp(Exception, message):
            res = self.apt_btrfs.prune("@apt-snapshot-2013-08-09_21:08:01")
        old_dirlist = os.listdir(self.sandbox)
        
        res = self.apt_btrfs.prune("@apt-snapshot-2013-08-09_21:09:40")
        self.assertTrue(res)
        new_dirlist = os.listdir(self.sandbox)
        self.assertEqual(len(old_dirlist), len(new_dirlist) + 2)
        self.assertNotIn(SNAP_PREFIX + "2013-08-09_21:09:40", new_dirlist)
        self.assertNotIn(SNAP_PREFIX + "2013-08-09_21:08:01", new_dirlist)
        
        res = self.apt_btrfs.prune("@apt-snapshot-2013-08-09_21:05:56")
        self.assertTrue(res)
        new_dirlist = os.listdir(self.sandbox)
        self.assertEqual(len(old_dirlist), len(new_dirlist) + 5)
        self.assertNotIn("@apt-snapshot-2013-08-09_21:05:56", new_dirlist)
        self.assertNotIn("@apt-snapshot-2013-08-09_21:04:37", new_dirlist)
        self.assertNotIn("@apt-snapshot-2013-08-08_18:44:47", new_dirlist)
 def test_fstab_get_uuid(self):
     apt_btrfs = AptBtrfsSnapshot(
         fstab=os.path.join(self.testdir, "data", "fstab"))
     self.assertEqual(apt_btrfs._uuid_for_mountpoint("/"),
                      "UUID=fe63f598-1906-478e-acc7-f74740e78d1f")