# Copyright 2012 Lars Wirzenius # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import os import shutil import tempfile import unittest import larch class JournalTests(unittest.TestCase): def setUp(self): self.tempdir = tempfile.mkdtemp() self.fs = larch.LocalFS() self.j = larch.Journal(True, self.fs, self.tempdir) def tearDown(self): shutil.rmtree(self.tempdir) def join(self, *args): return os.path.join(self.tempdir, *args) def test_constructs_new_filename(self): self.assertEqual(self.j._new(self.join('foo')), self.join('new', 'foo')) def test_constructs_deleted_filename(self): self.assertEqual(self.j._deleted(self.join('foo')), self.join('delete', 'foo')) def test_does_not_know_random_directory_initially(self): self.assertFalse(self.j.exists(self.join('foo'))) def test_creates_directory(self): dirname = self.join('foo/bar') self.j.makedirs(dirname) self.assertTrue(self.j.exists(dirname)) def test_rollback_undoes_directory_creation(self): dirname = self.join('foo/bar') self.j.makedirs(dirname) self.j.rollback() self.assertFalse(self.j.exists(dirname)) def test_rollback_keeps_committed_directory(self): dirname = self.join('foo/bar') self.j.makedirs(dirname) self.j.commit() self.j.rollback() self.assertTrue(self.j.exists(dirname)) def test_rollback_works_without_changes(self): self.assertEqual(self.j.rollback(), None) def test_creates_new_file(self): filename = self.join('foo/bar') self.j.overwrite_file(filename, 'bar') self.assertEqual(self.j.cat(filename), 'bar') def test_rollback_undoes_new_file(self): filename = self.join('foo/bar') self.j.overwrite_file(filename, 'bar') self.j.rollback() self.assertFalse(self.j.exists(filename)) def test_commits_new_file(self): filename = self.join('foo/bar') self.j.overwrite_file(filename, 'bar') self.j.commit() self.j.rollback() self.assertEqual(self.j.cat(filename), 'bar') def test_creates_new_file_after_commit(self): filename = self.join('foo/bar') self.j.overwrite_file(filename, 'bar') self.j.commit() self.j.overwrite_file(filename, 'yo') self.assertEqual(self.j.cat(filename), 'yo') def test_cat_does_not_find_deleted_file(self): filename = self.join('foo/bar') self.j.overwrite_file(filename, 'bar') self.j.commit() self.j.remove(filename) self.assertRaises(OSError, self.j.cat, filename) def test_rollback_brings_back_old_file(self): filename = self.join('foo/bar') self.j.overwrite_file(filename, 'bar') self.j.commit() self.j.overwrite_file(filename, 'yo') self.j.rollback() self.assertEqual(self.j.cat(filename), 'bar') def test_removes_uncommitted_file(self): filename = self.join('foo/bar') self.j.overwrite_file(filename, 'bar') self.j.remove(filename) self.assertFalse(self.j.exists(filename)) def test_rollback_undoes_removal_of_uncommitted_file(self): filename = self.join('foo/bar') self.j.overwrite_file(filename, 'bar') self.j.remove(filename) self.j.rollback() self.assertFalse(self.j.exists(filename)) def test_commits_file_removal(self): filename = self.join('foo/bar') self.j.overwrite_file(filename, 'bar') self.j.remove(filename) self.j.commit() self.j.rollback() self.assertFalse(self.j.exists(filename)) def test_removes_committed_file(self): filename = self.join('foo/bar') self.j.overwrite_file(filename, 'bar') self.j.commit() self.j.remove(filename) self.assertFalse(self.j.exists(filename)) def test_removing_committed_file_twice_causes_oserror(self): filename = self.join('foo/bar') self.j.overwrite_file(filename, 'bar') self.j.commit() self.j.remove(filename) self.assertRaises(OSError, self.j.remove, filename) def test_rollback_brings_back_committed_file(self): filename = self.join('foo/bar') self.j.overwrite_file(filename, 'bar') self.j.commit() self.j.remove(filename) self.j.rollback() self.assertEqual(self.j.cat(filename), 'bar') def test_commits_removal_of_committed_file(self): filename = self.join('foo/bar') self.j.overwrite_file(filename, 'bar') self.j.commit() self.j.remove(filename) self.j.commit() self.j.rollback() self.assertFalse(self.j.exists(filename)) def test_commits_metadata(self): metadata = self.join('metadata') self.j.overwrite_file(metadata, 'yuck') self.j.commit() self.assertEqual(self.fs.cat(self.join(metadata)), 'yuck') def test_unflagged_commit_means_new_instance_rollbacks(self): filename = self.join('foo/bar') self.j.overwrite_file(filename, 'bar') j2 = larch.Journal(True, self.fs, self.tempdir) self.assertFalse(j2.exists(filename)) def test_partial_commit_finished_by_new_instance(self): filename = self.join('foo/bar') metadata = self.join('metadata') self.j.overwrite_file(filename, 'bar') self.j.overwrite_file(metadata, '') self.j.commit(skip=[filename]) j2 = larch.Journal(True, self.fs, self.tempdir) self.assertTrue(j2.exists(filename)) class ReadOnlyJournalTests(unittest.TestCase): def setUp(self): self.tempdir = tempfile.mkdtemp() self.fs = larch.LocalFS() self.rw = larch.Journal(True, self.fs, self.tempdir) self.ro = larch.Journal(False, self.fs, self.tempdir) def tearDown(self): shutil.rmtree(self.tempdir) def join(self, *args): return os.path.join(self.tempdir, *args) def test_does_not_know_random_directory_initially(self): self.assertFalse(self.ro.exists(self.join('foo'))) def test_creating_directory_raises_error(self): self.assertRaises(larch.ReadOnlyMode, self.ro.makedirs, 'foo') def test_calling_rollback_raises_error(self): self.assertRaises(larch.ReadOnlyMode, self.ro.rollback) def test_readonly_mode_does_not_check_for_directory_creation(self): dirname = self.join('foo/bar') self.rw.makedirs(dirname) self.assertFalse(self.ro.exists(dirname)) def test_write_file_raisees_error(self): self.assertRaises(larch.ReadOnlyMode, self.ro.overwrite_file, 'foo', 'bar') def test_readonly_mode_does_not_check_for_new_file(self): filename = self.join('foo') self.rw.overwrite_file(filename, 'bar') self.assertFalse(self.ro.exists(filename)) def test_readonly_mode_does_not_check_for_modified_file(self): filename = self.join('foo') self.rw.overwrite_file(filename, 'first') self.rw.commit() self.assertEqual(self.ro.cat(filename), 'first') self.rw.overwrite_file(filename, 'second') self.assertEqual(self.ro.cat(filename), 'first') def test_readonly_mode_does_not_know_file_is_deleted_in_journal(self): filename = self.join('foo/bar') self.rw.overwrite_file(filename, 'bar') self.rw.commit() self.rw.remove(filename) self.assertEqual(self.ro.cat(filename), 'bar') def tests_lists_no_files_initially(self): dirname = self.join('foo') self.assertEqual(list(self.ro.list_files(dirname)), []) def test_lists_files_correctly_when_no_changes(self): dirname = self.join('foo') filename = self.join('foo/bar') self.rw.overwrite_file(filename, 'bar') self.rw.commit() self.assertEqual(list(self.ro.list_files(dirname)), [filename]) def test_lists_added_file_correctly(self): dirname = self.join('foo') filename = self.join('foo/bar') self.rw.overwrite_file(filename, 'bar') self.assertEqual(list(self.rw.list_files(dirname)), [filename]) self.assertEqual(list(self.ro.list_files(dirname)), []) def test_lists_added_file_correctly_when_dir_existed_already(self): dirname = self.join('foo') filename = self.join('foo/bar') filename2 = self.join('foo/foobar') self.rw.overwrite_file(filename, 'bar') self.rw.commit() self.rw.overwrite_file(filename2, 'yoyo') self.assertEqual(sorted(list(self.rw.list_files(dirname))), sorted([filename, filename2])) self.assertEqual(list(self.ro.list_files(dirname)), [filename]) def test_lists_removed_file_correctly(self): dirname = self.join('foo') filename = self.join('foo/bar') self.rw.overwrite_file(filename, 'bar') self.rw.commit() self.rw.remove(filename) self.assertEqual(list(self.rw.list_files(dirname)), []) self.assertEqual(list(self.ro.list_files(dirname)), [filename])