コード例 #1
0
        public void HandleModifiedFile_NoLocalBook_DoesNothing()
        {
            // Setup //
            // Simulate that a book appeared remotely. We should eventually get a created notice.
            // Sometimes, for reasons we don't fully understand, we get a modified notice
            // first. Or the book might be modified again before we fetch it. In any case,
            // we don't need modify messages until we fetch a local copy.
            const string bookFolderName = "My book";
            var          bookBuilder    = new BookFolderBuilder()
                                          .WithRootFolder(_collectionFolder.FolderPath)
                                          .WithTitle(bookFolderName)
                                          .Build(); // Writes the book to disk based on the above specified values
            string bookFolderPath = bookBuilder.BuiltBookFolderPath;

            var status = _collection.PutBook(bookFolderPath);

            // pretending we had nothing local before the change.
            RobustIO.DeleteDirectory(bookFolderPath, true);

            // Anticipate verification
            var prevMessages = _tcLog.Messages.Count;

            _mockTcManager.Setup(m => m.RaiseBookStatusChanged(It.IsAny <BookStatusChangeEventArgs>()))
            .Throws(new ArgumentException("RaiseBookStatus should not be called"));

            // System Under Test //
            _collection.HandleModifiedFile(new BookRepoChangeEventArgs()
            {
                BookFileName = $"{bookFolderName}.bloom"
            });

            // Verification
            Assert.That(_tcLog.Messages.Count, Is.EqualTo(prevMessages));
        }
コード例 #2
0
        public void HandleDeletedFile_BookDeletedButNoTombstone_DoesNothing()
        {
            // Simulate that a book is reported as deleted in the repo (and it is), but there is no tombstone
            const string bookFolderName = "My other book";
            var          bookBuilder    = new BookFolderBuilder()
                                          .WithRootFolder(_collectionFolder.FolderPath)
                                          .WithTitle(bookFolderName)
                                          .Build();
            string bookFolderPath = bookBuilder.BuiltBookFolderPath;

            var status = _collection.PutBook(bookFolderPath);

            // Delete but do NOT make tombstone
            _collection.DeleteBookFromRepo(bookFolderPath, false);

            var prevMessages    = _tcLog.Messages.Count;
            var prevInvocations = _mockTcManager.Invocations.Count;

            // System Under Test: a valid notification, but one we want to ignore //
            _collection.HandleDeletedRepoFile($"{bookFolderName}.bloom");

            // Verification
            Assert.That(_mockTcManager.Invocations.Count, Is.EqualTo(prevInvocations));
            Assert.That(_tcLog.Messages.Count, Is.EqualTo(prevMessages));
            Assert.That(Directory.Exists(bookFolderPath), Is.True, "The local book should not have been deleted");
        }
コード例 #3
0
        public void HandleDeletedFile_BookSelected_LogsProblem()
        {
            // Simulate that a book which is currently selected was just deleted in the repo
            const string bookFolderName = "My book";
            var          bookBuilder    = new BookFolderBuilder()
                                          .WithRootFolder(_collectionFolder.FolderPath)
                                          .WithTitle(bookFolderName)
                                          .Build();
            string bookFolderPath = bookBuilder.BuiltBookFolderPath;

            var status = _collection.PutBook(bookFolderPath);

            // But, although we have all the status to indicate it is in the repo, it's gone!
            _collection.DeleteBookFromRepo(bookFolderPath);

            // But it can't go away locally...it's the selected book!
            var book = new Mock <Bloom.Book.Book>();

            book.Setup(m => m.FolderPath).Returns(bookFolderPath);
            var selection = new Mock <BookSelection>();

            selection.Setup(m => m.CurrentSelection).Returns(book.Object);
            _mockTcManager.Setup(m => m.BookSelection).Returns(selection.Object);

            var prevMessages    = _tcLog.Messages.Count;
            var prevInvocations = _mockTcManager.Invocations.Count;

            // System Under Test //
            _collection.HandleDeletedRepoFile($"{bookFolderName}.bloom");

            // Verification
            Assert.That(_tcLog.Messages.Count, Is.EqualTo(prevMessages + 1));
            Assert.That(_tcLog.Messages[prevMessages].MessageType, Is.EqualTo(MessageAndMilestoneType.Error));
            Assert.That(Directory.Exists(bookFolderPath), Is.True, "The local book should not have been deleted");
        }
コード例 #4
0
        public void HandleDeletedFile_ConflictBookDeleted_LogsProblem()
        {
            // Simulate that a book which is checked out here was just deleted in the repo
            const string bookFolderName = "My book";
            var          bookBuilder    = new BookFolderBuilder()
                                          .WithRootFolder(_collectionFolder.FolderPath)
                                          .WithTitle(bookFolderName)
                                          .Build();
            string bookFolderPath = bookBuilder.BuiltBookFolderPath;

            var status = _collection.PutBook(bookFolderPath);

            _collection.AttemptLock(bookFolderName);
            // But, in spite of that, it's gone!
            _collection.DeleteBookFromRepo(bookFolderPath);

            var prevMessages    = _tcLog.Messages.Count;
            var prevInvocations = _mockTcManager.Invocations.Count;

            // System Under Test //
            _collection.HandleDeletedRepoFile($"{bookFolderName}.bloom");

            // Verification
            Assert.That(_mockTcManager.Invocations.Count, Is.EqualTo(prevInvocations));
            Assert.That(_tcLog.Messages.Count, Is.EqualTo(prevMessages + 1));
            Assert.That(_tcLog.Messages[prevMessages].MessageType, Is.EqualTo(MessageAndMilestoneType.ErrorNoReload));
            Assert.That(Directory.Exists(bookFolderPath), Is.True, "The local book should not have been deleted");
        }
コード例 #5
0
        public void HandleModifiedFile_NoConflictBookCheckedOutRemotely_RaisesCheckedOutByOtherButNoNewStuffMessage()
        {
            // Setup //
            // Simulate (sort of) that a book was just overwritten with the following new contents,
            // including that book.status indicates a remote checkout
            const string bookFolderName = "My other book";
            var          bookBuilder    = new BookFolderBuilder()
                                          .WithRootFolder(_collectionFolder.FolderPath)
                                          .WithTitle(bookFolderName)
                                          .Build();
            string bookFolderPath = bookBuilder.BuiltBookFolderPath;

            _collection.PutBook(bookFolderPath);
            _collection.AttemptLock("My other book", "*****@*****.**");
            // Enhance: to make it more realistic, we could write a not-checked-out-here local status,
            // but it's not necessary for producing the effects we want to test here.
            var prevMessages = _tcLog.Messages.Count;

            // System Under Test //
            _collection.HandleModifiedFile(new BookRepoChangeEventArgs()
            {
                BookFileName = $"{bookFolderName}.bloom"
            });

            // Verification
            var eventArgs = (BookStatusChangeEventArgs)_mockTcManager.Invocations.Last().Arguments[0];

            Assert.That(eventArgs.CheckedOutByWhom, Is.EqualTo(CheckedOutBy.Other));

            Assert.That(_tcLog.Messages.Count, Is.EqualTo(prevMessages));             // checksums didn't change
        }
コード例 #6
0
        public void HandleDeletedFile_NoConflictBook_DeletesAndRaisesCheckedOutByDeletedButNoMessage(string checkedOutTo)
        {
            // Simulate that a book was just deleted in the repo
            const string bookFolderName = "My book";
            var          bookBuilder    = new BookFolderBuilder()
                                          .WithRootFolder(_collectionFolder.FolderPath)
                                          .WithTitle(bookFolderName)
                                          .Build();
            string bookFolderPath = bookBuilder.BuiltBookFolderPath;

            var status = _collection.PutBook(bookFolderPath);

            if (checkedOutTo != null)
            {
                _collection.AttemptLock(bookFolderName, checkedOutTo);
            }
            _collection.DeleteBookFromRepo(bookFolderPath);

            var prevMessages    = _tcLog.Messages.Count;
            var prevInvocations = _mockTcManager.Invocations.Count;

            // System Under Test //
            _collection.HandleDeletedRepoFile($"{bookFolderName}.bloom");

            // Verification
            // (This is a bit fragile, as it depends on how many times the method calls ANY function in the
            // mock TC manager, and in what order. Currently we call it once to ask for a book selection,
            // and then, (the call we're interested in) to raise book status changed.
            Assert.That(_mockTcManager.Invocations.Count, Is.EqualTo(prevInvocations + 2));
            var eventArgs = (BookStatusChangeEventArgs)_mockTcManager.Invocations[prevInvocations + 1].Arguments[0];

            Assert.That(eventArgs.CheckedOutByWhom, Is.EqualTo(CheckedOutBy.Deleted));
            Assert.That(_tcLog.Messages.Count, Is.EqualTo(prevMessages));
            Assert.That(Directory.Exists(bookFolderPath), Is.False, "The local book should have been deleted");
        }
コード例 #7
0
        public void HandleModifiedFile_NoConflictBookChangedNotCheckedOut_RaisesCheckedOutByNoneAndNewStuffMessage()
        {
            // Simulate (sort of) that a book was just overwritten with the following new contents,
            // including that book.status does not indicate it's checked out
            const string bookFolderName = "My book";
            var          bookBuilder    = new BookFolderBuilder()
                                          .WithRootFolder(_collectionFolder.FolderPath)
                                          .WithTitle(bookFolderName)
                                          .WithHtm("This is pretending to be new content from remote")
                                          .Build();

            string bookFolderPath = bookBuilder.BuiltBookFolderPath;
            var    status         = _collection.PutBook(bookFolderPath);

            RobustFile.WriteAllText(bookBuilder.BuiltBookHtmPath, "This is pretending to be old content");
            // pretending this is what it was before the change.
            _collection.WriteLocalStatus(bookFolderName, status.WithChecksum(Bloom.TeamCollection.TeamCollection.MakeChecksum(bookFolderPath)));
            var prevMessages = _tcLog.Messages.Count;

            // System Under Test //
            _collection.HandleModifiedFile(new BookRepoChangeEventArgs()
            {
                BookFileName = $"{bookFolderName}.bloom"
            });

            // Verification
            var eventArgs = (BookStatusChangeEventArgs)_mockTcManager.Invocations[0].Arguments[0];

            Assert.That(eventArgs.CheckedOutByWhom, Is.EqualTo(CheckedOutBy.None));

            Assert.That(_tcLog.Messages[prevMessages].MessageType, Is.EqualTo(MessageAndMilestoneType.NewStuff));
        }
コード例 #8
0
        public void HandleModifiedFile_NoConflictBookNotChangedCheckedOutRemoved_RaisesCheckedOutByNoneButNoNewStuffMessage()
        {
            // Setup //
            // Simulate (sort of) that a book was just overwritten with the following new contents,
            // including that book.status does not indicate it's checked out
            const string bookFolderName = "My book";
            var          bookBuilder    = new BookFolderBuilder()
                                          .WithRootFolder(_collectionFolder.FolderPath)
                                          .WithTitle(bookFolderName)
                                          .Build(); // Writes the book to disk based on the above specified values
            string bookFolderPath = bookBuilder.BuiltBookFolderPath;

            var status = _collection.PutBook(bookFolderPath);

            // pretending this is what it was before the change.
            _collection.WriteLocalStatus(bookFolderName, status.WithLockedBy("*****@*****.**"));
            var prevMessages = _tcLog.Messages.Count;

            // System Under Test //
            _collection.HandleModifiedFile(new BookRepoChangeEventArgs()
            {
                BookFileName = $"{bookFolderName}.bloom"
            });

            // Verification
            var eventArgs = (BookStatusChangeEventArgs)_mockTcManager.Invocations[0].Arguments[0];

            Assert.That(eventArgs.CheckedOutByWhom, Is.EqualTo(CheckedOutBy.None));

            Assert.That(_tcLog.Messages.Count, Is.EqualTo(prevMessages));
        }
コード例 #9
0
        public void HandleBookRename_CaseChangeOnly_WorksRight()
        {
            // Setup //
            const string originalBookName = "A new book";
            var          bookBuilder      = new BookFolderBuilder()
                                            .WithRootFolder(_collectionFolder.FolderPath)
                                            .WithTitle(originalBookName)
                                            .WithHtm("<html><body>This is just a dummy</body></html>")
                                            .Build();
            string bookFolderPath = bookBuilder.BuiltBookFolderPath;
            string htmlPath       = bookBuilder.BuiltBookHtmPath;

            TeamCollectionManager.ForceCurrentUserForTests("*****@*****.**");
            _collection.PutBook(bookFolderPath);

            var locked = _collection.AttemptLock(originalBookName);

            Assert.That(locked, Is.True, "successfully checked out book to [email protected]");

            // SUT: rename changes status in local collection folder, but not in shared repo folder
            const string newBookName       = "A New Book";
            var          newBookFolderPath = Path.Combine(_collectionFolder.FolderPath, newBookName);

            File.Move(htmlPath, Path.Combine(bookFolderPath, newBookName + ".htm"));
            // renaming directory doesn't work when names are 'the same'
            var tempPath = Path.Combine(_collectionFolder.FolderPath, "tempxxyy");

            Directory.Move(bookFolderPath, tempPath);
            Directory.Move(tempPath, newBookFolderPath);

            _collection.HandleBookRename(originalBookName, newBookName);

            _collection.PutBook(newBookFolderPath, true);

            var newRepoPath = Path.Combine(_sharedFolder.FolderPath, "Books", newBookName + ".bloom");

            // It should not have been deleted! This is a regression test for BL-10156.
            // The danger is that Windows considers the old and new names the same, so after
            // we move the file to the new name, if we go to delete the old name, we get rid of the new one.
            Assert.That(File.Exists(newRepoPath));

            // Did it get renamed?
            var matchingFiles = Directory.EnumerateFiles(Path.Combine(_sharedFolder.FolderPath, "Books"), newBookName + ".bloom").ToArray();

            Assert.That(matchingFiles[0], Is.EqualTo(Path.Combine(_sharedFolder.FolderPath, "Books", newBookName + ".bloom")));

            var newStatus  = _collection.GetLocalStatus(newBookName);
            var repoStatus = _collection.GetStatus(newBookName);

            Assert.That(newStatus, Is.Not.Null, "local status of renamed book is not null");
            Assert.That(repoStatus, Is.Not.Null, "repo status of renamed book is not null");
            Assert.That(newStatus.checksum, Is.EqualTo(repoStatus.checksum), "checksums of local and remote match after rename");
            Assert.That(newStatus.lockedBy, Is.EqualTo(null), "lockedBy of local and remote match after rename");
            Assert.That(newStatus.oldName, Is.Null, "local status has original name cleared after commit");
        }
コード例 #10
0
        // Make a very trivial fake book. Not nearly good enough to make a Book object from,
        // but enough for most purposes of testing TeamCollection.
        public static string MakeFakeBook(string collectionFolder, string name, string content, string folderNameIfDifferent = null)
        {
            var bookBuilder = new BookFolderBuilder()
                              .WithRootFolder(collectionFolder)
                              .WithBookFolderName(folderNameIfDifferent)
                              .WithTitle(name)
                              .WithHtm("<html><body>" + content + "</body></html>")
                              .Build();

            return(bookBuilder.BuiltBookFolderPath);
        }
コード例 #11
0
        public void DeleteBookFromRepo_CreatesTombstone()
        {
            const string bookFolderName = "Delete away";

            var bookBuilder = new BookFolderBuilder()
                              .WithRootFolder(_collectionFolder.FolderPath)
                              .WithTitle(bookFolderName)
                              .Build();
            string bookFolderPath = bookBuilder.BuiltBookFolderPath;

            var status = _collection.PutBook(bookFolderPath);

            _collection.DeleteBookFromRepo(bookFolderPath);
            Assert.That(_collection.KnownToHaveBeenDeleted("Delete away"), Is.True);
        }
コード例 #12
0
        public void HandleModifiedFile_CheckedOutToMe_ContentChangedRemotely_RaisesCheckedOutByNoneAndErrorMessage()
        {
            // Setup //
            // Simulate a book that was checked out and modified by me, but then we get a remote change
            // notification.
            const string bookFolderName = "My conflicting change book";
            var          bookBuilder    = new BookFolderBuilder()
                                          .WithRootFolder(_collectionFolder.FolderPath)
                                          .WithTitle(bookFolderName)
                                          .WithHtm("We will be simulating a remote change to this.")
                                          .Build();

            string bookFolderPath = bookBuilder.BuiltBookFolderPath;
            string bookPath       = bookBuilder.BuiltBookHtmPath;

            _collection.PutBook(bookFolderPath);
            var pathToBookFileInRepo = _collection.GetPathToBookFileInRepo(bookFolderName);
            // Save the data we will eventually write back to the .bloom file to simulate the remote change.
            var remoteContent = RobustFile.ReadAllBytes(pathToBookFileInRepo);

            _collection.AttemptLock(bookFolderName);
            RobustFile.WriteAllText(bookPath, "Pretend this was the state when we checked it out.");
            _collection.PutBook(bookFolderPath);

            RobustFile.WriteAllText(bookPath, "This is a further change locally, not checked in anywhere");

            // But now it's been changed remotely to the other state. (Ignore the fact that it was a previous local state;
            // that was just a trick to get a valid alternative state.)
            RobustFile.WriteAllBytes(pathToBookFileInRepo, remoteContent);

            var prevMessages = _tcLog.Messages.Count;

            // System Under Test //
            _collection.HandleModifiedFile(new BookRepoChangeEventArgs()
            {
                BookFileName = $"{bookFolderName}.bloom"
            });

            // Verification
            var eventArgs = (BookStatusChangeEventArgs)_mockTcManager.Invocations[0].Arguments[0];

            Assert.That(eventArgs.CheckedOutByWhom, Is.EqualTo(CheckedOutBy.None));

            Assert.That(_tcLog.Messages[prevMessages].MessageType, Is.EqualTo(MessageAndMilestoneType.Error));
            Assert.That(_tcLog.Messages[prevMessages].L10NId, Is.EqualTo("TeamCollection.EditedFileChangedRemotely"));
        }
コード例 #13
0
        public void HandleModifiedFile_CheckedOutToMe_RemotelyToOther_RaisesCheckedOutByOtherAndErrorMessage()
        {
            // Setup //
            // Simulate a book was just overwritten with contents indicating a remote checkout,
            // while locally it is checked out to me.
            const string bookFolderName = "My conflict book";
            var          bookBuilder    = new BookFolderBuilder()
                                          .WithRootFolder(_collectionFolder.FolderPath)
                                          .WithTitle(bookFolderName)
                                          .Build();
            string bookFolderPath = bookBuilder.BuiltBookFolderPath;

            TeamCollectionManager.ForceCurrentUserForTests("*****@*****.**");

            _collection.PutBook(bookFolderPath);
            // Temporarily, it looks locked by Nancy in both places.
            _collection.AttemptLock("My conflict book", "*****@*****.**");
            var status = _collection.GetStatus("My conflict book").WithLockedBy(TeamCollectionManager.CurrentUser);

            // Now it is locally checked out to me. (The state changes are in the opposite order to what
            // we're trying to simulate, because we don't have an easy way to change remote checkout status without
            // changing local status to match at the same time.)
            _collection.WriteLocalStatus("My conflict book", status);
            var prevMessages = _tcLog.Messages.Count;

            // System Under Test...basically HandleModifiedFile, but this is a convenient place to
            // make sure we take the right path through this calling method.
            _collection.QueuePendingBookChange(
                new BookRepoChangeEventArgs()
            {
                BookFileName = $"{bookFolderName}.bloom"
            });
            _collection.HandleRemoteBookChangesOnIdle(null, new EventArgs());

            // Verification
            var eventArgs = (BookStatusChangeEventArgs)_mockTcManager.Invocations[2].Arguments[0];

            Assert.That(eventArgs.CheckedOutByWhom, Is.EqualTo(CheckedOutBy.Other));

            Assert.That(_tcLog.Messages[prevMessages].MessageType, Is.EqualTo(MessageAndMilestoneType.Error));
            Assert.That(_tcLog.Messages[prevMessages].L10NId, Is.EqualTo("TeamCollection.ConflictingCheckout"));
            TeamCollectionManager.ForceCurrentUserForTests(null);
        }
コード例 #14
0
        public void HandleNewBook_CreatesNewStuffMessage()
        {
            var bookBuilder = new BookFolderBuilder()
                              .WithRootFolder(_collectionFolder.FolderPath)
                              .WithTitle("My new book")
                              .Build();                              // Writes the book to disk based on the above specified values
            string bookFolderPath = bookBuilder.BuiltBookFolderPath; // Gets the location the book was written to

            _collection.PutBook(bookFolderPath);
            SIL.IO.RobustIO.DeleteDirectoryAndContents(bookFolderPath);
            var prevMessages = _tcLog.Messages.Count;

            // SUT: We're mainly testing HandleNewbook, but it's convenient to check that this method calls it properly.
            _collection.QueuePendingBookChange(new NewBookEventArgs()
            {
                BookFileName = "My new book.bloom"
            });
            _collection.HandleRemoteBookChangesOnIdle(null, new EventArgs());


            Assert.That(_tcLog.Messages[prevMessages].MessageType, Is.EqualTo(MessageAndMilestoneType.NewStuff));
        }
コード例 #15
0
        public void HandleBookRename_CheckedOutToMe_FixesStatusProperly()
        {
            // Setup //
            const string originalBookName = "Hello. Goodbye!";
            var          bookBuilder      = new BookFolderBuilder()
                                            .WithRootFolder(_collectionFolder.FolderPath)
                                            .WithTitle(originalBookName)
                                            .WithHtm("<html><body>This is just a dummy</body></html>")
                                            .Build();
            string bookFolderPath = bookBuilder.BuiltBookFolderPath;
            string htmlPath       = bookBuilder.BuiltBookHtmPath;

            TeamCollectionManager.ForceCurrentUserForTests("*****@*****.**");
            _collection.PutBook(bookFolderPath);

            var locked = _collection.AttemptLock(originalBookName);

            Assert.That(locked, Is.True, "successfully checked out book to [email protected]");

            // SUT: rename changes status in local collection folder, but not in shared repo folder
            const string newBookName       = "Testing is Fun. Sometimes";
            var          newBookFolderPath = Path.Combine(_collectionFolder.FolderPath, newBookName);

            File.Move(htmlPath, Path.Combine(bookFolderPath, newBookName + ".htm"));
            Directory.Move(bookFolderPath, newBookFolderPath);

            _collection.HandleBookRename(originalBookName, newBookName);
            var newStatus  = _collection.GetLocalStatus(newBookName);
            var repoStatus = _collection.GetStatus(newBookName);

            Assert.That(newStatus, Is.Not.Null, "local status of renamed book is not null");
            Assert.That(repoStatus, Is.Not.Null, "repo status of renamed book is not null");
            Assert.That(newStatus.checksum, Is.EqualTo(repoStatus.checksum), "checksums of local and remote match after rename");
            Assert.That(newStatus.lockedBy, Is.EqualTo(repoStatus.lockedBy), "lockedBy of local and remote match after rename");
            Assert.That(newStatus.lockedWhen, Is.EqualTo(repoStatus.lockedWhen), "lockedWhen of local and remote match after rename");
            Assert.That(newStatus.lockedWhere, Is.EqualTo(repoStatus.lockedWhere), "lockedWhere of local and remote match after rename");
            Assert.That(newStatus.oldName, Is.EqualTo(originalBookName), "local status has original name in oldName field after rename");
            Assert.That(repoStatus.oldName, Is.Null, "repo status still has null oldName field after rename");
        }
コード例 #16
0
        public void HandleDeletedFile_BookNotDeleted_DoesNothing()
        {
            // Simulate that a book was reported as deleted in the repo, but actually, it's still there
            const string bookFolderName = "My book";
            var          bookBuilder    = new BookFolderBuilder()
                                          .WithRootFolder(_collectionFolder.FolderPath)
                                          .WithTitle(bookFolderName)
                                          .Build();
            string bookFolderPath = bookBuilder.BuiltBookFolderPath;

            var status = _collection.PutBook(bookFolderPath);

            var prevMessages    = _tcLog.Messages.Count;
            var prevInvocations = _mockTcManager.Invocations.Count;

            // System Under Test: a spurious notification //
            _collection.HandleDeletedRepoFile($"{bookFolderName}.bloom");

            // Verification
            Assert.That(_mockTcManager.Invocations.Count, Is.EqualTo(prevInvocations));
            Assert.That(_tcLog.Messages.Count, Is.EqualTo(prevMessages));
            Assert.That(Directory.Exists(bookFolderPath), Is.True, "The local book should not have been deleted");
        }