public async Task TestGetBackupMetadataAsync_OneFullBackup_AndTwoIncrementalBackup_Present()
        {
            //arrange
            Guid     partitionId = Guid.NewGuid();
            DateTime now         = DateTime.UtcNow;

            BackupMetadata backupMetadata         = new BackupMetadata(partitionId, now, BackupOption.Full);
            BackupMetadata incrementalMetadata    = new BackupMetadata(partitionId, now.AddHours(1), BackupOption.Incremental);
            BackupMetadata incrementalMetadataTwo = new BackupMetadata(partitionId, now.AddHours(2), BackupOption.Incremental);

            var mockBackupStoreObject = new Moq.Mock <ICentralBackupStore>();

            mockBackupStoreObject
            .Setup(store => store.RetrieveScheduledBackupAsync(It.IsAny <Guid>()))
            .Returns(Task.FromResult(incrementalMetadata));

            mockBackupStoreObject
            .Setup(store => store.GetBackupMetadataAsync(null, It.IsAny <Guid>()))
            .Returns(Task.FromResult <IEnumerable <BackupMetadata> >(new[] { backupMetadata, incrementalMetadata, incrementalMetadataTwo }));

            var mockServiceObject = new Moq.Mock <IBackupRestoreServiceOperations>();

            mockServiceObject.Setup(service => service.CentralBackupStore).Returns(mockBackupStoreObject.Object);
            mockServiceObject.Setup(service => service.Context).Returns(Mocks.MockStatefulServiceContextFactory.Default);

            //act
            var result = await BackupRestoreServiceOperations.GetBackupMetadataAsync(mockServiceObject.Object, incrementalMetadataTwo);

            //assert
            Assert.AreEqual(3, result.Count);
            Assert.AreEqual(backupMetadata, result[0]);
            Assert.AreEqual(incrementalMetadata, result[1]);
            Assert.AreEqual(incrementalMetadataTwo, result[2]);
        }
Пример #2
0
 private async Task ApplyChangesAsync(TextEditor textEditor, BackupMetadata backupMetadata)
 {
     TextFile textFile = await FileSystemUtility.ReadFile(backupMetadata.BackupFilePath);
     textEditor.Init(textFile, textEditor.EditingFile, resetOriginalSnapshot: false, isModified: true);
     textEditor.TryChangeEncoding(backupMetadata.Encoding);
     textEditor.TryChangeLineEnding(backupMetadata.LineEnding);
 }
Пример #3
0
        private async Task <ITextEditor> RecoverTextEditorAsync(TextEditorSessionData textEditorData)
        {
            StorageFile sourceFile = await FileSystemUtility.GetFileFromFutureAccessList(ToToken(textEditorData.Id));

            BackupMetadata lastSaved = textEditorData.LastSaved;
            BackupMetadata pending   = textEditorData.Pending;
            ITextEditor    textEditor;

            if (sourceFile == null) // Untitled.txt or file not found
            {
                if (lastSaved != null || pending != null)
                {
                    textEditor = _notepadsCore.OpenNewTextEditor(textEditorData.Id);
                    await ApplyChangesAsync(textEditor, pending ?? lastSaved);
                }
                else
                {
                    textEditor = null;
                }
            }
            else if (lastSaved == null && pending == null) // File without pending changes
            {
                textEditor = await _notepadsCore.OpenNewTextEditor(sourceFile, ignoreFileSizeLimit : true, textEditorData.Id);
            }
            else // File with pending changes
            {
                TextFile textFile = await FileSystemUtility.ReadFile(lastSaved.BackupFilePath, ignoreFileSizeLimit : true);

                textEditor = _notepadsCore.OpenNewTextEditor(
                    textEditorData.Id,
                    textFile.Content,
                    sourceFile,
                    lastSaved.DateModified,
                    lastSaved.Encoding,
                    lastSaved.LineEnding,
                    false);

                await ApplyChangesAsync(textEditor, pending);
            }

            return(textEditor);
        }
        public async Task TestGetBackupMetadataAsync_OneFullBackup_Present()
        {
            //arrange
            Guid           partitionId    = Guid.NewGuid();
            DateTime       now            = DateTime.UtcNow;
            BackupMetadata backupMetadata = new BackupMetadata(partitionId, now, BackupOption.Full);

            var mockBackupStoreObject = new Moq.Mock <ICentralBackupStore>();
            var mockServiceObject     = new Moq.Mock <IBackupRestoreServiceOperations>();

            mockServiceObject.Setup(service => service.CentralBackupStore).Returns(mockBackupStoreObject.Object);
            mockServiceObject.Setup(service => service.Context).Returns(Mocks.MockStatefulServiceContextFactory.Default);

            //act
            var result = await BackupRestoreServiceOperations.GetBackupMetadataAsync(mockServiceObject.Object, backupMetadata);

            //assert
            Assert.AreEqual(1, result.Count);
            Assert.AreEqual(backupMetadata, result[0]);
        }
        async Task BackupAsync()
        {
            this.events.StartingBackup();
            Guid   backupId          = Guid.NewGuid();
            string dbBackupDirectory = Path.Combine(this.backupPath, backupId.ToString());

            BackupMetadata     newBackupMetadata  = new BackupMetadata(backupId, this.dataBackupRestore.DataBackupFormat, DateTime.UtcNow, this.dbStores.Keys.ToList());
            BackupMetadataList backupMetadataList = new BackupMetadataList(new List <BackupMetadata> {
                newBackupMetadata
            });

            try
            {
                Directory.CreateDirectory(dbBackupDirectory);

                // Backup other stores.
                foreach (string store in this.dbStores.Keys)
                {
                    IDbStore dbStore = this.dbStoreProvider.GetDbStore(store);
                    await this.DbStoreBackupAsync(store, dbStore, dbBackupDirectory);
                }

                using (StreamWriter file = File.CreateText(Path.Combine(this.backupPath, BackupMetadataFileName)))
                {
                    JsonSerializer serializer = new JsonSerializer();
                    serializer.Serialize(file, backupMetadataList);
                }

                this.events.BackupComplete();

                // Clean any old backups.
                this.CleanupUnknownBackups(this.backupPath, backupMetadataList);
            }
            catch (Exception exception)
            {
                this.events.BackupFailure($"The backup operation failed with error ${exception}.");

                // Clean up any artifacts of the attempted backup.
                this.CleanupKnownBackups(this.backupPath, backupMetadataList);
            }
        }
Пример #6
0
        private async Task <TextEditor> RecoverTextEditorAsync(TextEditorSessionData textEditorData)
        {
            StorageFile sourceFile = await FileSystemUtility.GetFileFromFutureAccessList(ToToken(textEditorData.Id));

            BackupMetadata lastSaved = textEditorData.LastSaved;
            BackupMetadata pending   = textEditorData.Pending;
            TextEditor     textEditor;

            if (sourceFile == null)
            {
                textEditor = _notepadsCore.OpenNewTextEditor(
                    textEditorData.Id,
                    string.Empty,
                    null,
                    -1,
                    EditorSettingsService.EditorDefaultEncoding,
                    EditorSettingsService.EditorDefaultLineEnding,
                    false);

                await ApplyChangesAsync(textEditor, pending ?? lastSaved);
            }
            else
            {
                TextFile lastSavedContent = await FileSystemUtility.ReadFile(lastSaved.BackupFilePath);

                textEditor = _notepadsCore.OpenNewTextEditor(
                    textEditorData.Id,
                    lastSavedContent.Content,
                    sourceFile,
                    lastSaved.DateModified,
                    lastSaved.Encoding,
                    lastSaved.LineEnding,
                    false);

                await ApplyChangesAsync(textEditor, pending);
            }

            return(textEditor);
        }
Пример #7
0
        private static void VerifyListIsAValidBackupChain(List <BackupMetadata> backupChain)
        {
            bool foundFull, foundDiff, foundLog;

            foundFull = foundDiff = foundLog = false;
            BackupMetadata full       = null;
            BackupMetadata lastBackup = null;

            BackupMetadata currentBackup;

            while ((currentBackup = backupChain.FirstOrDefault()) != null)
            {
                if (currentBackup.BackupType == BackupFileTools.BackupType.Full)
                {
                    Assert.True(!foundFull && !foundDiff && !foundLog);
                    foundFull = true;
                    full      = currentBackup;
                }
                else if (currentBackup.BackupType == BackupFileTools.BackupType.Diff)
                {
                    Assert.True(foundFull && !foundDiff && !foundLog);
                    Assert.Equal(full.CheckpointLsn, currentBackup.DatabaseBackupLsn);
                    Assert.True(currentBackup.FirstLsn >= lastBackup.LastLsn);
                    foundDiff = true;
                }
                else if (currentBackup.BackupType == BackupFileTools.BackupType.Log)
                {
                    Assert.True(foundFull);
                    Assert.True(currentBackup.FirstLsn >= lastBackup.LastLsn);
                    foundLog = true;
                }

                lastBackup = currentBackup;
                backupChain.RemoveAll(b => b.LastLsn == currentBackup.LastLsn);
            }
        }
Пример #8
0
        public async Task SaveSessionAsync()
        {
            if (!IsBackupEnabled)
            {
                LoggingService.LogInfo("Session backup is disabled.");
                return;
            }

            // Serialize saves
            await _semaphoreSlim.WaitAsync();

            Stopwatch stopwatch = Stopwatch.StartNew();

            ITextEditor[] textEditors        = _notepadsCore.GetAllTextEditors();
            ITextEditor   selectedTextEditor = _notepadsCore.GetSelectedTextEditor();

            FileSystemUtility.ClearFutureAccessList();

            NotepadsSessionDataV1 sessionData = new NotepadsSessionDataV1();

            foreach (ITextEditor textEditor in textEditors)
            {
                if (textEditor.EditingFile != null)
                {
                    // Add the opened file to FutureAccessList so we can access it next launch
                    await FileSystemUtility.TryAddToFutureAccessList(ToToken(textEditor.Id), textEditor.EditingFile);
                }

                if (!_sessionData.TryGetValue(textEditor.Id, out TextEditorSessionData textEditorData))
                {
                    textEditorData = new TextEditorSessionData {
                        Id = textEditor.Id
                    };

                    if (textEditor.IsModified)
                    {
                        if (textEditor.EditingFile != null)
                        {
                            // Persist the last save known to the app, which might not be up-to-date (if the file was modified outside the app)
                            BackupMetadata lastSaved = await SaveLastSavedChangesAsync(textEditor);

                            if (lastSaved == null)
                            {
                                continue;
                            }

                            textEditorData.LastSaved = lastSaved;
                        }

                        // Persist pending changes relative to the last save
                        BackupMetadata pending = await SavePendingChangesAsync(textEditor);

                        if (pending == null)
                        {
                            continue;
                        }

                        textEditorData.Pending = pending;
                    }

                    // We will not create new backup files for this text editor unless it has changes
                    _sessionData.TryAdd(textEditor.Id, textEditorData);
                }

                sessionData.TextEditors.Add(textEditorData);

                if (textEditor == selectedTextEditor)
                {
                    sessionData.SelectedTextEditor = textEditor.Id;
                }
            }

            bool sessionDataSaved = false;

            try
            {
                string sessionJsonStr = JsonConvert.SerializeObject(sessionData, _encodingConverter);

                if (!(ApplicationSettingsStore.Read(SessionDataKey) is string currentValue) || !string.Equals(currentValue, sessionJsonStr, StringComparison.OrdinalIgnoreCase))
                {
                    ApplicationSettingsStore.Write(SessionDataKey, sessionJsonStr);
                    sessionDataSaved = true;
                }
            }
            catch (Exception ex)
            {
                LoggingService.LogError($"Failed to save session metadata: {ex.Message}");
                return; // Failed to save the session - do not proceed to delete backup files
            }

            if (sessionDataSaved)
            {
                await DeleteOrphanedBackupFilesAsync(sessionData);
            }

            stopwatch.Stop();

            if (sessionDataSaved)
            {
                LoggingService.LogInfo($"Successfully saved the current session. Total time: {stopwatch.Elapsed.TotalMilliseconds} milliseconds.", consoleOnly: true);
            }

            _semaphoreSlim.Release();
        }
        async Task RestoreAsync()
        {
            string backupMetadataFilePath = Path.Combine(this.backupPath, BackupMetadataFileName);

            if (!File.Exists(backupMetadataFilePath))
            {
                this.events.NoBackupsForRestore();
                return;
            }

            try
            {
                string             fileText           = File.ReadAllText(backupMetadataFilePath);
                BackupMetadataList backupMetadataList = JsonConvert.DeserializeObject <BackupMetadataList>(fileText);
                BackupMetadata     backupMetadata     = backupMetadataList.Backups[0];
                this.events.BackupInformation(backupMetadata.Id, backupMetadata.SerializationFormat, backupMetadata.TimestampUtc, backupMetadata.Stores);

                string latestBackupDirPath = Path.Combine(this.backupPath, backupMetadata.Id.ToString());
                if (Directory.Exists(latestBackupDirPath))
                {
                    this.events.RestoringFromBackup(backupMetadata.Id);
                    foreach (string store in backupMetadata.Stores)
                    {
                        IDbStore dbStore;
                        if (!store.Equals(DefaultStoreBackupName, StringComparison.OrdinalIgnoreCase))
                        {
                            dbStore = this.GetDbStore(store);
                        }
                        else
                        {
                            dbStore = this.GetDbStore();
                        }

                        await this.DbStoreRestoreAsync(store, dbStore, latestBackupDirPath);
                    }

                    this.events.RestoreComplete();
                }
                else
                {
                    this.events.NoBackupsForRestore();
                }
            }
            catch (Exception exception)
            {
                this.events.RestoreFailure($"The restore operation failed with error ${exception}.");

                // Clean up any restored state in the dictionary if the backup fails midway.
                foreach (string store in this.dbStores.Keys.ToList())
                {
                    this.RemoveDbStore(store);
                }

                this.RemoveDbStore();
            }
            finally
            {
                // Delete all other backups as we've either:
                // 1. Restored successfully.
                // 2. Failed during restore which would indicate a bad backup.
                this.CleanupAllBackups(this.backupPath);
            }
        }
Пример #10
0
 /// <inheritdoc />
 Task IBackupRestoreService.BeginRestoreBackup(BackupMetadata backupMetadata, DataLossMode dataLossMode)
 {
     return(BackupRestoreServiceOperations.BeginRestoreBackup(this, backupMetadata, dataLossMode));
 }
Пример #11
0
        public async Task SaveSessionAsync()
        {
            if (!IsBackupEnabled)
            {
                return;
            }

            // Serialize saves
            await _semaphoreSlim.WaitAsync();

            NotepadsSessionDataV1 sessionData = new NotepadsSessionDataV1();
            HashSet <string>      backupPaths = new HashSet <string>(StringComparer.OrdinalIgnoreCase);

            TextEditor[] textEditors        = _notepadsCore.GetAllTextEditors();
            TextEditor   selectedTextEditor = _notepadsCore.GetSelectedTextEditor();

            FileSystemUtility.ClearFutureAccessList();

            foreach (TextEditor textEditor in textEditors)
            {
                TextEditorSessionData textEditorData = new TextEditorSessionData {
                    Id = textEditor.Id
                };

                if (textEditor.EditingFile != null)
                {
                    // Add the opened file to FutureAccessList so we can access it next launch
                    FileSystemUtility.TryAddToFutureAccessList(ToToken(textEditor.Id), textEditor.EditingFile);

                    // Persist the last save known to the app, which might not be up-to-date (if the file was modified outside the app)
                    BackupMetadata lastSaved = await SaveLastSavedChangesAsync(textEditor);

                    if (lastSaved == null)
                    {
                        continue;
                    }

                    textEditorData.LastSaved = lastSaved;
                    backupPaths.Add(lastSaved.BackupFilePath);
                }

                if (textEditor.IsModified)
                {
                    // Persist pending changes relative to the last save
                    BackupMetadata pending = await SavePendingChangesAsync(textEditor);

                    if (pending == null)
                    {
                        continue;
                    }

                    textEditorData.Pending = pending;
                    backupPaths.Add(pending.BackupFilePath);
                }

                if (textEditorData.LastSaved != null || textEditorData.Pending != null)
                {
                    sessionData.TextEditors.Add(textEditorData);

                    if (textEditor == selectedTextEditor)
                    {
                        sessionData.SelectedTextEditor = textEditor.Id;
                    }
                }
            }

            try
            {
                string sessionJson = JsonConvert.SerializeObject(sessionData, _encodingConverter);
                ApplicationData.Current.LocalSettings.Values[SessionDataKey] = sessionJson;
                LoggingService.LogInfo("Successfully saved the current session.");
            }
            catch
            {
                return; // Failed to save the session - do not proceed to delete backup files
            }

            await DeleteOrphanedBackupFilesAsync(backupPaths);

            _semaphoreSlim.Release();
        }
Пример #12
0
        public async Task SaveSessionAsync()
        {
            if (!IsBackupEnabled)
            {
                LoggingService.LogInfo("Session backup is disabled.");
                return;
            }

            // Serialize saves
            await _semaphoreSlim.WaitAsync();

            StorageFolder backupFolder = await SessionUtility.GetBackupFolderAsync();

            LoggingService.LogInfo("Session backup is starting. Backup folder: " + backupFolder.Path);
            Stopwatch stopwatch = Stopwatch.StartNew();

            TextEditor[] textEditors        = _notepadsCore.GetAllTextEditors();
            TextEditor   selectedTextEditor = _notepadsCore.GetSelectedTextEditor();

            FileSystemUtility.ClearFutureAccessList();

            NotepadsSessionDataV1 sessionData = new NotepadsSessionDataV1();

            foreach (TextEditor textEditor in textEditors)
            {
                if (textEditor.EditingFile != null)
                {
                    // Add the opened file to FutureAccessList so we can access it next launch
                    FileSystemUtility.TryAddToFutureAccessList(ToToken(textEditor.Id), textEditor.EditingFile);
                }

                if (!_sessionData.TryGetValue(textEditor.Id, out TextEditorSessionData textEditorData))
                {
                    textEditorData = new TextEditorSessionData {
                        Id = textEditor.Id
                    };

                    if (textEditor.EditingFile != null)
                    {
                        // Persist the last save known to the app, which might not be up-to-date (if the file was modified outside the app)
                        BackupMetadata lastSaved = await SaveLastSavedChangesAsync(textEditor);

                        if (lastSaved == null)
                        {
                            continue;
                        }

                        textEditorData.LastSaved = lastSaved;
                    }

                    if (textEditor.IsModified)
                    {
                        // Persist pending changes relative to the last save
                        BackupMetadata pending = await SavePendingChangesAsync(textEditor);

                        if (pending == null)
                        {
                            continue;
                        }

                        textEditorData.Pending = pending;
                    }

                    // We will not create new backup files for this text editor unless it has changes
                    _sessionData.TryAdd(textEditor.Id, textEditorData);
                }

                if (textEditorData.LastSaved != null || textEditorData.Pending != null)
                {
                    sessionData.TextEditors.Add(textEditorData);

                    if (textEditor == selectedTextEditor)
                    {
                        sessionData.SelectedTextEditor = textEditor.Id;
                    }
                }
            }

            try
            {
                string sessionJson = JsonConvert.SerializeObject(sessionData, _encodingConverter);
                ApplicationData.Current.LocalSettings.Values[SessionDataKey] = sessionJson;
            }
            catch
            {
                return; // Failed to save the session - do not proceed to delete backup files
            }

            await DeleteOrphanedBackupFilesAsync(sessionData);

            stopwatch.Stop();
            LoggingService.LogInfo("Successfully saved the current session. Total time: " + stopwatch.Elapsed.TotalMilliseconds + " milliseconds.");

            _semaphoreSlim.Release();
        }