diff --git a/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/BackupUtilities.cs b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/BackupOperation.cs similarity index 88% rename from src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/BackupUtilities.cs rename to src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/BackupOperation.cs index 7ce1c47a..c5e8d67a 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/BackupUtilities.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/BackupOperation.cs @@ -19,7 +19,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery /// /// This class implements backup operations /// - public class BackupUtilities : IBackupUtilities + public class BackupOperation : IBackupOperation { private CDataContainer dataContainer; private ServerConnection serverConnection; @@ -94,7 +94,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery /// /// Ctor /// - public BackupUtilities() + public BackupOperation() { } @@ -110,7 +110,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery { this.dataContainer = dataContainer; this.serverConnection = new ServerConnection(sqlConnection); - this.backupRestoreUtil = new CommonUtilities(this.dataContainer, this.serverConnection); + this.backupRestoreUtil = new CommonUtilities(this.dataContainer, this.serverConnection); } /// @@ -152,11 +152,12 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery public void PerformBackup() { this.backup = new Backup(); + this.backup.Database = this.backupInfo.DatabaseName; + this.backup.Action = this.backupActionType; + this.backup.Incremental = this.isBackupIncremental; this.SetBackupProps(); - backup.Database = this.backupInfo.DatabaseName; - backup.Action = this.backupActionType; - backup.Incremental = this.isBackupIncremental; - if (backup.Action == BackupActionType.Files) + + if (this.backup.Action == BackupActionType.Files) { IDictionaryEnumerator filegroupEnumerator = this.backupInfo.SelectedFileGroup.GetEnumerator(); filegroupEnumerator.Reset(); @@ -169,14 +170,14 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery if (currentKey.IndexOf(",", StringComparison.Ordinal) < 0) { // is a file group - backup.DatabaseFileGroups.Add(currentValue); + this.backup.DatabaseFileGroups.Add(currentValue); } else { // is a file int idx = currentValue.IndexOf(".", StringComparison.Ordinal); currentValue = currentValue.Substring(idx + 1, currentValue.Length - idx - 1); - backup.DatabaseFiles.Add(currentValue); + this.backup.DatabaseFiles.Add(currentValue); } } } @@ -187,7 +188,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery isBackupToUrl = true; } - backup.BackupSetName = this.backupInfo.BackupsetName; + this.backup.BackupSetName = this.backupInfo.BackupsetName; if (false == isBackupToUrl) { @@ -205,19 +206,19 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery if ((this.backupDeviceType == BackupDeviceType.Disk && backupDeviceType == constDeviceTypeFile) || (this.backupDeviceType == BackupDeviceType.Tape && backupDeviceType == constDeviceTypeTape)) { - backup.Devices.AddDevice(destName, DeviceType.LogicalDevice); + this.backup.Devices.AddDevice(destName, DeviceType.LogicalDevice); } break; case (int)DeviceType.File: if (this.backupDeviceType == BackupDeviceType.Disk) { - backup.Devices.AddDevice(destName, DeviceType.File); + this.backup.Devices.AddDevice(destName, DeviceType.File); } break; case (int)DeviceType.Tape: if (this.backupDeviceType == BackupDeviceType.Tape) { - backup.Devices.AddDevice(destName, DeviceType.Tape); + this.backup.Devices.AddDevice(destName, DeviceType.Tape); } break; } @@ -225,15 +226,15 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery } //TODO: This should be changed to get user inputs - backup.FormatMedia = false; - backup.Initialize = false; - backup.SkipTapeHeader = true; - backup.Checksum = false; - backup.ContinueAfterError = false; - backup.LogTruncation = BackupTruncateLogType.Truncate; + this.backup.FormatMedia = false; + this.backup.Initialize = false; + this.backup.SkipTapeHeader = true; + this.backup.Checksum = false; + this.backup.ContinueAfterError = false; + this.backup.LogTruncation = BackupTruncateLogType.Truncate; // Execute backup - backup.SqlBackup(this.dataContainer.Server); + this.backup.SqlBackup(this.dataContainer.Server); } /// @@ -278,20 +279,11 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery /// private bool BackupToUrlSupported() { - return BackupRestoreBase.IsBackupUrlDeviceSupported(this.dataContainer.Server.PingSqlServerVersion(this.dataContainer.ServerName)); //@@ originally, DataContainer.Server.ServerVersion + return BackupRestoreBase.IsBackupUrlDeviceSupported(this.dataContainer.Server.PingSqlServerVersion(this.dataContainer.ServerName)); } #endregion - private string GetDefaultBackupSetName() - { - string backupName = this.backupInfo.DatabaseName + "-" - + this.backupType.ToString() + " " - + this.backupComponent.ToString() + " " - + BackupConstants.Backup; - return backupName; - } - private void SetBackupProps() { try @@ -327,7 +319,6 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery break; default: break; - //throw new Exception("Unexpected error"); } } catch diff --git a/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/CommonUtilities.cs b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/CommonUtilities.cs index 3c97bc2a..aa85258b 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/CommonUtilities.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/CommonUtilities.cs @@ -107,8 +107,8 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery /// public CommonUtilities(CDataContainer dataContainer, ServerConnection sqlConnection) { - this.dataContainer = dataContainer; - this.sqlConnection = sqlConnection; + this.dataContainer = dataContainer; + this.sqlConnection = sqlConnection; this.excludedDatabases = new ArrayList(); this.excludedDatabases.Add("master"); this.excludedDatabases.Add("tempdb"); @@ -202,7 +202,6 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery } } - public bool ServerHasLogicalDevices() { try @@ -253,7 +252,6 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery return new string(result); } - public RecoveryModel GetRecoveryModel(string databaseName) { Enumerator en = null; @@ -274,7 +272,6 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery } return recoveryModel; } - public string GetRecoveryModelAsString(RecoveryModel recoveryModel) { @@ -296,7 +293,6 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery return recoveryModelString; } - public string GetDefaultBackupFolder() { string backupFolder = ""; @@ -570,7 +566,6 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery return result; } - public bool IsDatabaseOnServer(string databaseName) { @@ -699,7 +694,6 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery } } - public void GetBackupSetTypeAndComponent(string strType, ref string backupType, ref string backupComponent) { string type = strType.ToUpperInvariant(); @@ -815,7 +809,6 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery } return result; } - public DataSet GetBackupSetFiles(int backupsetId) { @@ -853,7 +846,6 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery return backupset; } - public ArrayList GetBackupSetPhysicalSources(int backupsetId) { SqlExecutionModes executionMode = this.sqlConnection.SqlExecutionModes; @@ -902,7 +894,6 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery return sources; } - public RestoreActionType GetRestoreTaskFromBackupSetType(BackupsetType type) { RestoreActionType result = RestoreActionType.Database; @@ -952,7 +943,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery } } - public List GetLatestBackupLocations(string DatabaseName) + public List GetLatestBackupLocations(string databaseName) { List latestLocations = new List(); Enumerator en = null; @@ -961,7 +952,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery Request req = new Request(); en = new Enumerator(); - req.Urn = "Server/BackupSet[@DatabaseName='" + Urn.EscapeString(DatabaseName) + "']"; + req.Urn = "Server/BackupSet[@DatabaseName='" + Urn.EscapeString(databaseName) + "']"; req.OrderByList = new OrderBy[1]; req.OrderByList[0] = new OrderBy(); req.OrderByList[0].Field = "BackupFinishDate"; @@ -1015,7 +1006,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery return latestLocations; } - public string GetDefaultDatabaseForLogin(string LoginName) + public string GetDefaultDatabaseForLogin(string loginName) { string defaultDatabase = string.Empty; Enumerator en = new Enumerator(); @@ -1023,7 +1014,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery ds.Locale = System.Globalization.CultureInfo.InvariantCulture; Request req = new Request(); - req.Urn = "Server/Login[@Name='"+Urn.EscapeString(LoginName)+"']"; + req.Urn = "Server/Login[@Name='"+Urn.EscapeString(loginName)+"']"; req.Fields = new string[1]; req.Fields[0] = "DefaultDatabase"; ds = en.Process(this.sqlConnection, req); diff --git a/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/DisasterRecoveryService.cs b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/DisasterRecoveryService.cs index acb60501..15a4fd4d 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/DisasterRecoveryService.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/DisasterRecoveryService.cs @@ -23,23 +23,12 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery { private static readonly Lazy instance = new Lazy(() => new DisasterRecoveryService()); private static ConnectionService connectionService = null; - private IBackupUtilities backupUtilities; - private ManualResetEvent backupCompletedEvent = new ManualResetEvent(initialState: false); /// /// Default, parameterless constructor. /// internal DisasterRecoveryService() { - this.backupUtilities = new BackupUtilities(); - } - - /// - /// For testing purpose only - /// - internal DisasterRecoveryService(IBackupUtilities backupUtilities) - { - this.backupUtilities = backupUtilities; } /// @@ -100,18 +89,14 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery { DatabaseTaskHelper helper = AdminService.CreateDatabaseTaskHelper(connInfo, databaseExists: true); SqlConnection sqlConn = GetSqlConnection(connInfo); - if (sqlConn != null) + if ((sqlConn != null) && !connInfo.IsSqlDW && !connInfo.IsAzure) { - DisasterRecoveryService.Instance.InitializeBackup(helper.DataContainer, sqlConn); - if (!connInfo.IsSqlDW) - { - BackupConfigInfo backupConfigInfo = DisasterRecoveryService.Instance.GetBackupConfigInfo(sqlConn.Database); - backupConfigInfo.DatabaseInfo = AdminService.GetDatabaseInfo(connInfo); - response.BackupConfigInfo = backupConfigInfo; - } + BackupConfigInfo backupConfigInfo = DisasterRecoveryService.Instance.GetBackupConfigInfo(helper.DataContainer, sqlConn, sqlConn.Database); + backupConfigInfo.DatabaseInfo = AdminService.GetDatabaseInfo(connInfo); + response.BackupConfigInfo = backupConfigInfo; } } - + await requestContext.SendResult(response); } @@ -131,11 +116,9 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery { DatabaseTaskHelper helper = AdminService.CreateDatabaseTaskHelper(connInfo, databaseExists: true); SqlConnection sqlConn = GetSqlConnection(connInfo); - if (sqlConn != null) + if ((sqlConn != null) && !connInfo.IsSqlDW && !connInfo.IsAzure) { - // initialize backup - DisasterRecoveryService.Instance.InitializeBackup(helper.DataContainer, sqlConn); - DisasterRecoveryService.Instance.SetBackupInput(backupParams.BackupInfo); + BackupOperation backupOperation = DisasterRecoveryService.Instance.SetBackupInput(helper.DataContainer, sqlConn, backupParams.BackupInfo); // create task metadata TaskMetadata metadata = new TaskMetadata(); @@ -143,9 +126,10 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery metadata.DatabaseName = connInfo.ConnectionDetails.DatabaseName; metadata.Name = SR.Backup_TaskName; metadata.IsCancelable = true; - + metadata.Data = backupOperation; + // create backup task and perform - SqlTask sqlTask = SqlTaskManager.Instance.CreateTask(metadata, Instance.BackupTask); + SqlTask sqlTask = SqlTaskManager.Instance.CreateTask(metadata, Instance.BackupTaskAsync); sqlTask.Run(); } } @@ -179,48 +163,72 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery return null; } - internal void InitializeBackup(CDataContainer dataContainer, SqlConnection sqlConnection) + internal BackupConfigInfo GetBackupConfigInfo(CDataContainer dataContainer, SqlConnection sqlConnection, string databaseName) { - this.backupUtilities.Initialize(dataContainer, sqlConnection); + BackupOperation backupOperation = new BackupOperation(); + backupOperation.Initialize(dataContainer, sqlConnection); + return backupOperation.GetBackupConfigInfo(databaseName); } - internal void SetBackupInput(BackupInfo input) - { - this.backupUtilities.SetBackupInput(input); - } - - internal void PerformBackup() + internal BackupOperation SetBackupInput(CDataContainer dataContainer, SqlConnection sqlConnection, BackupInfo input) { - this.backupUtilities.PerformBackup(); + BackupOperation backupOperation = new BackupOperation(); + backupOperation.Initialize(dataContainer, sqlConnection); + backupOperation.SetBackupInput(input); + return backupOperation; } - internal BackupConfigInfo GetBackupConfigInfo(string databaseName) + /// + /// For testing purpose only + /// + internal void PerformBackup(BackupOperation backupOperation) { - return this.backupUtilities.GetBackupConfigInfo(databaseName); + backupOperation.PerformBackup(); } - + /// /// Create a backup task for execution and cancellation /// /// /// - internal async Task BackupTask(SqlTask sqlTask) + internal async Task BackupTaskAsync(SqlTask sqlTask) { sqlTask.AddMessage(SR.Task_InProgress, SqlTaskStatus.InProgress, true); - Task performTask = this.PerformTask(); - Task cancelTask = this.CancelTask(sqlTask); - Task completedTask = await Task.WhenAny(performTask, cancelTask); - if (completedTask == performTask) + IBackupOperation backupOperation = sqlTask.TaskMetadata.Data as IBackupOperation; + TaskResult taskResult = null; + + if (backupOperation != null) { - this.backupCompletedEvent.Set(); + AutoResetEvent backupCompletedEvent = new AutoResetEvent(initialState: false); + Task performTask = PerformTaskAsync(backupOperation); + Task cancelTask = CancelTaskAsync(backupOperation, sqlTask.TokenSource.Token, backupCompletedEvent); + Task completedTask = await Task.WhenAny(performTask, cancelTask); + + // Release the cancelTask + if (completedTask == performTask) + { + backupCompletedEvent.Set(); + } + + sqlTask.AddMessage(completedTask.Result.TaskStatus == SqlTaskStatus.Failed ? completedTask.Result.ErrorMessage : SR.Task_Completed, + completedTask.Result.TaskStatus); + taskResult = completedTask.Result; + } + else + { + taskResult = new TaskResult(); + taskResult.TaskStatus = SqlTaskStatus.Failed; } - sqlTask.AddMessage(completedTask.Result.TaskStatus == SqlTaskStatus.Failed ? completedTask.Result.ErrorMessage : SR.Task_Completed, - completedTask.Result.TaskStatus); - return completedTask.Result; + return taskResult; } - private async Task PerformTask() + /// + /// Async task to execute backup + /// + /// + /// + private async Task PerformTaskAsync(IBackupOperation backupOperation) { // Create a task to perform backup return await Task.Factory.StartNew(() => @@ -228,7 +236,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery TaskResult result = new TaskResult(); try { - this.backupUtilities.PerformBackup(); + backupOperation.PerformBackup(); result.TaskStatus = SqlTaskStatus.Succeeded; } catch (Exception ex) @@ -244,14 +252,19 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery }); } - private async Task CancelTask(SqlTask sqlTask) + /// + /// Async task to cancel backup + /// + /// + /// + /// + /// + private async Task CancelTaskAsync(IBackupOperation backupOperation, CancellationToken token, AutoResetEvent backupCompletedEvent) { // Create a task for backup cancellation request return await Task.Factory.StartNew(() => { TaskResult result = new TaskResult(); - - CancellationToken token = sqlTask.TokenSource.Token; WaitHandle[] waitHandles = new WaitHandle[2] { backupCompletedEvent, @@ -259,19 +272,17 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery }; WaitHandle.WaitAny(waitHandles); - if (token.IsCancellationRequested) + try { - try - { - this.backupUtilities.CancelBackup(); - result.TaskStatus = SqlTaskStatus.Canceled; - } - catch (Exception ex) - { - result.TaskStatus = SqlTaskStatus.Failed; - result.ErrorMessage = ex.Message; - } + backupOperation.CancelBackup(); + result.TaskStatus = SqlTaskStatus.Canceled; } + catch (Exception ex) + { + result.TaskStatus = SqlTaskStatus.Failed; + result.ErrorMessage = ex.Message; + } + return result; }); } diff --git a/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/IBackupUtilities.cs b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/IBackupOperation.cs similarity index 93% rename from src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/IBackupUtilities.cs rename to src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/IBackupOperation.cs index fafdac9e..b2f72862 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/IBackupUtilities.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/DisasterRecovery/IBackupOperation.cs @@ -2,6 +2,7 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +using Microsoft.SqlServer.Management.Smo; using Microsoft.SqlTools.ServiceLayer.Admin; using Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts; using System.Data.SqlClient; @@ -11,7 +12,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery /// /// Interface for backup operations /// - public interface IBackupUtilities + public interface IBackupOperation { /// /// Initialize @@ -32,7 +33,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery /// /// void SetBackupInput(BackupInfo input); - + /// /// Execute backup /// diff --git a/src/Microsoft.SqlTools.ServiceLayer/TaskServices/TaskMetadata.cs b/src/Microsoft.SqlTools.ServiceLayer/TaskServices/TaskMetadata.cs index 2678c12e..3481a6ca 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/TaskServices/TaskMetadata.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/TaskServices/TaskMetadata.cs @@ -37,5 +37,10 @@ namespace Microsoft.SqlTools.ServiceLayer.TaskServices /// Database name this task is created for /// public string DatabaseName { get; set; } + + /// + /// Data required to perform the task + /// + public object Data { get; set; } } } diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/DisasterRecovery/BackupTests.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/DisasterRecovery/BackupTests.cs index bcc11910..df209371 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/DisasterRecovery/BackupTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/DisasterRecovery/BackupTests.cs @@ -57,10 +57,9 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DisasterRecovery // Initialize backup service DatabaseTaskHelper helper = AdminService.CreateDatabaseTaskHelper(liveConnection.ConnectionInfo, databaseExists: true); SqlConnection sqlConn = DisasterRecoveryService.GetSqlConnection(liveConnection.ConnectionInfo); - DisasterRecoveryService.Instance.InitializeBackup(helper.DataContainer, sqlConn); - + // Get default backup path - BackupConfigInfo backupConfigInfo = DisasterRecoveryService.Instance.GetBackupConfigInfo(sqlConn.Database); + BackupConfigInfo backupConfigInfo = DisasterRecoveryService.Instance.GetBackupConfigInfo(helper.DataContainer, sqlConn, sqlConn.Database); string backupPath = backupConfigInfo.DefaultBackupFolder + "\\" + databaseName + ".bak"; var backupInfo = new BackupInfo(); @@ -81,8 +80,8 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DisasterRecovery }; // Backup the database - DisasterRecoveryService.Instance.SetBackupInput(backupParams.BackupInfo); - DisasterRecoveryService.Instance.PerformBackup(); + BackupOperation backupOperation = DisasterRecoveryService.Instance.SetBackupInput(helper.DataContainer, sqlConn, backupParams.BackupInfo); + DisasterRecoveryService.Instance.PerformBackup(backupOperation); // Remove the backup file if (File.Exists(backupPath)) diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/DisasterRecovery/BackupUtilitiesStub.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/DisasterRecovery/BackupOperationStub.cs similarity index 91% rename from test/Microsoft.SqlTools.ServiceLayer.UnitTests/DisasterRecovery/BackupUtilitiesStub.cs rename to test/Microsoft.SqlTools.ServiceLayer.UnitTests/DisasterRecovery/BackupOperationStub.cs index ea9b3e6f..3936d297 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/DisasterRecovery/BackupUtilitiesStub.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/DisasterRecovery/BackupOperationStub.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +using Microsoft.SqlServer.Management.Smo; using Microsoft.SqlTools.ServiceLayer.Admin; using Microsoft.SqlTools.ServiceLayer.DisasterRecovery; using Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts; @@ -12,9 +13,9 @@ using System.Threading; namespace Microsoft.SqlTools.ServiceLayer.UnitTests.DisasterRecovery { /// - /// Stub class that implements IBackupUtilities + /// Stub class that implements IBackupOperation /// - public class BackupUtilitiesStub : IBackupUtilities + public class BackupOperationStub : IBackupOperation { /// /// Initialize diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/DisasterRecovery/BackupTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/DisasterRecovery/BackupTests.cs index 85b7f77a..75a249b4 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/DisasterRecovery/BackupTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/DisasterRecovery/BackupTests.cs @@ -21,14 +21,20 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.DisasterRecovery IsCancelable = true }; + /// + /// Create and run a backup task + /// + /// [Fact] - public async Task VerifyCreateAndRunningBackupTask() + public async Task VerifyRunningBackupTask() { using (SqlTaskManager manager = new SqlTaskManager()) { - var mockUtility = new Mock(); - DisasterRecoveryService service = new DisasterRecoveryService(mockUtility.Object); - SqlTask sqlTask = manager.CreateTask(this.taskMetaData, service.BackupTask); + DisasterRecoveryService service = new DisasterRecoveryService(); + var mockBackupOperation = new Mock(); + this.taskMetaData.Data = mockBackupOperation.Object; + + SqlTask sqlTask = manager.CreateTask(this.taskMetaData, service.BackupTaskAsync); Assert.NotNull(sqlTask); Task taskToVerify = sqlTask.RunAsync().ContinueWith(Task => { @@ -38,15 +44,52 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.DisasterRecovery await taskToVerify; } } - + + /// + /// Create and run multiple backup tasks + /// + /// [Fact] - public async Task CancelBackupTask() + public async Task VerifyRunningMultipleBackupTasks() + { + using (SqlTaskManager manager = new SqlTaskManager()) + { + DisasterRecoveryService service = new DisasterRecoveryService(); + var mockUtility = new Mock(); + this.taskMetaData.Data = mockUtility.Object; + + SqlTask sqlTask = manager.CreateTask(this.taskMetaData, service.BackupTaskAsync); + SqlTask sqlTask2 = manager.CreateTask(this.taskMetaData, service.BackupTaskAsync); + Assert.NotNull(sqlTask); + Assert.NotNull(sqlTask2); + + Task taskToVerify = sqlTask.RunAsync().ContinueWith(Task => + { + Assert.Equal(SqlTaskStatus.Succeeded, sqlTask.TaskStatus); + }); + + Task taskToVerify2 = sqlTask2.RunAsync().ContinueWith(Task => + { + Assert.Equal(SqlTaskStatus.Succeeded, sqlTask2.TaskStatus); + }); + + await Task.WhenAll(taskToVerify, taskToVerify2); + } + } + + /// + /// Cancel a backup task + /// + /// + [Fact] + public async Task VerifyCancelBackupTask() { using (SqlTaskManager manager = new SqlTaskManager()) { - IBackupUtilities backupUtility = new BackupUtilitiesStub(); - DisasterRecoveryService service = new DisasterRecoveryService(backupUtility); - SqlTask sqlTask = manager.CreateTask(this.taskMetaData, service.BackupTask); + IBackupOperation backupOperation = new BackupOperationStub(); + DisasterRecoveryService service = new DisasterRecoveryService(); + this.taskMetaData.Data = backupOperation; + SqlTask sqlTask = manager.CreateTask(this.taskMetaData, service.BackupTaskAsync); Assert.NotNull(sqlTask); Task taskToVerify = sqlTask.RunAsync().ContinueWith(Task => { @@ -59,5 +102,77 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.DisasterRecovery await taskToVerify; } } + + /// + /// Cancel multiple backup tasks + /// + /// + [Fact] + public async Task VerifyCancelMultipleBackupTasks() + { + using (SqlTaskManager manager = new SqlTaskManager()) + { + IBackupOperation backupOperation = new BackupOperationStub(); + DisasterRecoveryService service = new DisasterRecoveryService(); + this.taskMetaData.Data = backupOperation; + SqlTask sqlTask = manager.CreateTask(this.taskMetaData, service.BackupTaskAsync); + SqlTask sqlTask2 = manager.CreateTask(this.taskMetaData, service.BackupTaskAsync); + Assert.NotNull(sqlTask); + Assert.NotNull(sqlTask2); + + Task taskToVerify = sqlTask.RunAsync().ContinueWith(Task => + { + Assert.Equal(SqlTaskStatus.Canceled, sqlTask.TaskStatus); + Assert.Equal(sqlTask.IsCancelRequested, true); + manager.Reset(); + }); + + Task taskToVerify2 = sqlTask2.RunAsync().ContinueWith(Task => + { + Assert.Equal(SqlTaskStatus.Canceled, sqlTask2.TaskStatus); + Assert.Equal(sqlTask2.IsCancelRequested, true); + manager.Reset(); + }); + + manager.CancelTask(sqlTask.TaskId); + manager.CancelTask(sqlTask2.TaskId); + + await Task.WhenAll(taskToVerify, taskToVerify2); + } + } + + /// + /// Create two backup tasks and cancel one task + /// + /// + [Fact] + public async Task VerifyCombinationRunAndCancelBackupTasks() + { + using (SqlTaskManager manager = new SqlTaskManager()) + { + IBackupOperation backupOperation = new BackupOperationStub(); + DisasterRecoveryService service = new DisasterRecoveryService(); + this.taskMetaData.Data = backupOperation; + SqlTask sqlTask = manager.CreateTask(this.taskMetaData, service.BackupTaskAsync); + SqlTask sqlTask2 = manager.CreateTask(this.taskMetaData, service.BackupTaskAsync); + Assert.NotNull(sqlTask); + Assert.NotNull(sqlTask2); + + Task taskToVerify = sqlTask.RunAsync().ContinueWith(Task => + { + Assert.Equal(SqlTaskStatus.Canceled, sqlTask.TaskStatus); + Assert.Equal(sqlTask.IsCancelRequested, true); + manager.Reset(); + }); + + Task taskToVerify2 = sqlTask2.RunAsync().ContinueWith(Task => + { + Assert.Equal(SqlTaskStatus.Succeeded, sqlTask2.TaskStatus); + }); + + manager.CancelTask(sqlTask.TaskId); + await Task.WhenAll(taskToVerify, taskToVerify2); + } + } } }