public void TestDirectoryPreserveSMBPropertiesResume() { if (!CrossPlatformHelpers.IsWindows) { return; } CloudFileNtfsAttributes[] SMBFileAttributes = { CloudFileNtfsAttributes.ReadOnly, CloudFileNtfsAttributes.Hidden, #if DEBUG CloudFileNtfsAttributes.System, CloudFileNtfsAttributes.Archive, CloudFileNtfsAttributes.Normal, CloudFileNtfsAttributes.Offline, CloudFileNtfsAttributes.NotContentIndexed, CloudFileNtfsAttributes.NoScrubData, CloudFileNtfsAttributes.ReadOnly | CloudFileNtfsAttributes.Hidden, CloudFileNtfsAttributes.System | CloudFileNtfsAttributes.Archive, CloudFileNtfsAttributes.Offline | CloudFileNtfsAttributes.NotContentIndexed | CloudFileNtfsAttributes.NoScrubData, CloudFileNtfsAttributes.ReadOnly | CloudFileNtfsAttributes.Hidden | CloudFileNtfsAttributes.System | CloudFileNtfsAttributes.Archive | CloudFileNtfsAttributes.NotContentIndexed | CloudFileNtfsAttributes.NoScrubData #endif }; for (int i = 0; i < SMBFileAttributes.Length; ++i) { CancellationTokenSource tokenSource = new CancellationTokenSource(); TransferItem transferItem = null; bool IsStreamJournal = random.Next(0, 2) == 0; using (Stream journalStream = new MemoryStream()) { Test.Info("Testing setting attributes {0} to directories", SMBFileAttributes[i]); // Prepare data DMLibDataInfo sourceDataInfo = new DMLibDataInfo(""); GenerateDirNodeWithAttributes(sourceDataInfo.RootNode, 2, SMBFileAttributes[i]); DirectoryTransferContext dirTransferContext = null; if (IsStreamJournal) { dirTransferContext = new DirectoryTransferContext(journalStream); } else { dirTransferContext = new DirectoryTransferContext(); } var progressChecker = new ProgressChecker(14, 14 * 1024); dirTransferContext.ProgressHandler = progressChecker.GetProgressHandler(); var options = new TestExecutionOptions <DMLibDataInfo>(); options.IsDirectoryTransfer = true; options.TransferItemModifier = (fileNode, item) => { item.TransferContext = dirTransferContext; dynamic transferOptions = DefaultTransferDirectoryOptions; transferOptions.Recursive = true; transferOptions.PreserveSMBAttributes = true; item.Options = transferOptions; item.CancellationToken = tokenSource.Token; transferItem = item; }; TransferCheckpoint checkpoint = null; options.AfterAllItemAdded = () => { // Wait until there are data transferred progressChecker.DataTransferred.WaitOne(); if (!IsStreamJournal) { checkpoint = dirTransferContext.LastCheckpoint; } // Cancel the transfer and store the second checkpoint tokenSource.Cancel(); }; #if DEBUG TestHookCallbacks.GetFileAttributesCallback = (path) => { if (path.EndsWith("\\") || path.Substring(0, path.Length - 2).EndsWith(DMLibTestBase.DirName)) { if (SMBFileAttributes[i] == CloudFileNtfsAttributes.Normal) { return(FileAttributes.Directory); } else { return(Helper.ToFileAttributes(SMBFileAttributes[i] | CloudFileNtfsAttributes.Directory)); } } else { return(Helper.ToFileAttributes(SMBFileAttributes[i])); } }; TestHookCallbacks.SetFileAttributesCallback = (path, attributes) => { if (path.EndsWith("\\") || path.Substring(0, path.Length - 2).EndsWith(DMLibTestBase.DirName)) { if (path.Substring(0, path.Length - 7).EndsWith("destroot") || path.Substring(0, path.Length - 6).EndsWith("destroot")) { Test.Assert(attributes == FileAttributes.Directory, "Directory attributes should be expected {0}", attributes); } else { if (SMBFileAttributes[i] == CloudFileNtfsAttributes.Normal) { Test.Assert(attributes == FileAttributes.Directory, "Directory attributes should be expected {0}", attributes); } else { Test.Assert(attributes == Helper.ToFileAttributes(SMBFileAttributes[i] | CloudFileNtfsAttributes.Directory), "Directory attributes should be expected {0}", attributes); } } } else { Test.Assert(attributes == Helper.ToFileAttributes(SMBFileAttributes[i]), "File attributes should be expected {0}", attributes); } }; #endif try { // Execute test case var result = this.ExecuteTestCase(sourceDataInfo, options); Test.Assert(result.Exceptions.Count == 1, "Verify job is cancelled"); Exception exception = result.Exceptions[0]; Helper.VerifyCancelException(exception); TransferItem resumeItem = transferItem.Clone(); DirectoryTransferContext resumeContext = null; journalStream.Position = 0; if (IsStreamJournal) { resumeContext = new DirectoryTransferContext(journalStream); } else { resumeContext = new DirectoryTransferContext(DMLibTestHelper.RandomReloadCheckpoint(checkpoint)); } resumeItem.TransferContext = resumeContext; result = this.RunTransferItems(new List <TransferItem>() { resumeItem }, new TestExecutionOptions <DMLibDataInfo>()); VerificationHelper.VerifyTransferSucceed(result, sourceDataInfo); options = new TestExecutionOptions <DMLibDataInfo>(); options.IsDirectoryTransfer = true; TransferItem item = new TransferItem() { SourceObject = SourceAdaptor.GetTransferObject(string.Empty, sourceDataInfo.RootNode), DestObject = DestAdaptor.GetTransferObject(string.Empty, sourceDataInfo.RootNode), SourceType = DMLibTestContext.SourceType, DestType = DMLibTestContext.DestType, CopyMethod = DMLibTestContext.CopyMethod.ToCopyMethod(), IsDirectoryTransfer = true, Options = DefaultTransferDirectoryOptions }; dynamic transferOptions = DefaultTransferDirectoryOptions; transferOptions.Recursive = true; transferOptions.PreserveSMBAttributes = true; item.Options = transferOptions; item.TransferContext = new DirectoryTransferContext(); item.TransferContext.ShouldOverwriteCallbackAsync = async(s, d) => { return(false); }; List <TransferItem> items = new List <TransferItem>(); items.Add(item); result = this.RunTransferItems(items, options); VerificationHelper.VerifyTransferSucceed(result, sourceDataInfo); if (DMLibTestContext.DestType == DMLibDataType.Local) { Helper.CompareSMBProperties(sourceDataInfo.RootNode, result.DataInfo.RootNode, false); } else { Helper.CompareSMBProperties(sourceDataInfo.RootNode, result.DataInfo.RootNode, true); } } finally { #if DEBUG TestHookCallbacks.GetFileAttributesCallback = null; TestHookCallbacks.SetFileAttributesCallback = null; #endif } } } }
public void TestDirectoryPreserveSMBPermissionsResume() { if (!CrossPlatformHelpers.IsWindows) { return; } PreserveSMBPermissions preserveSMBPermissions = PreserveSMBPermissions.Owner | PreserveSMBPermissions.Group | PreserveSMBPermissions.DACL; string sampleSDDL = "O:S-1-5-21-2146773085-903363285-719344707-1375029G:S-1-5-21-2146773085-903363285-719344707-513D:(A;ID;FA;;;BA)(A;OICIIOID;GA;;;BA)(A;ID;FA;;;SY)(A;OICIIOID;GA;;;SY)(A;ID;0x1301bf;;;AU)(A;OICIIOID;SDGXGWGR;;;AU)(A;ID;0x1200a9;;;BU)(A;OICIIOID;GXGR;;;BU)"; CancellationTokenSource tokenSource = new CancellationTokenSource(); TransferItem transferItem = null; bool IsStreamJournal = random.Next(0, 2) == 0; using (Stream journalStream = new MemoryStream()) { // Prepare data DMLibDataInfo sourceDataInfo = new DMLibDataInfo(""); GenerateDirNodeWithPermissions(sourceDataInfo.RootNode, 2, sampleSDDL); DirectoryTransferContext dirTransferContext = null; if (IsStreamJournal) { dirTransferContext = new DirectoryTransferContext(journalStream); } else { dirTransferContext = new DirectoryTransferContext(); } var progressChecker = new ProgressChecker(14, 14 * 1024); dirTransferContext.ProgressHandler = progressChecker.GetProgressHandler(); var options = new TestExecutionOptions <DMLibDataInfo>(); options.IsDirectoryTransfer = true; options.TransferItemModifier = (fileNode, item) => { item.TransferContext = dirTransferContext; dynamic transferOptions = DefaultTransferDirectoryOptions; transferOptions.Recursive = true; transferOptions.PreserveSMBPermissions = preserveSMBPermissions; item.Options = transferOptions; item.CancellationToken = tokenSource.Token; transferItem = item; }; TransferCheckpoint checkpoint = null; options.AfterAllItemAdded = () => { // Wait until there are data transferred progressChecker.DataTransferred.WaitOne(); if (!IsStreamJournal) { checkpoint = dirTransferContext.LastCheckpoint; } // Cancel the transfer and store the second checkpoint tokenSource.Cancel(); }; #if DEBUG TestHookCallbacks.UnderTesting = true; TestHookCallbacks.GetFilePermissionsCallback = (path, SMBPermissionType) => { Test.Assert(SMBPermissionType == preserveSMBPermissions, "The SMB permission type should be expected."); return(sampleSDDL); }; TestHookCallbacks.SetFilePermissionsCallback = (path, portableSDDL, SMBPermissionType) => { Test.Assert(SMBPermissionType == preserveSMBPermissions, "The SMB permission type should be expected."); Test.Assert(portableSDDL.StartsWith(sampleSDDL), "The SDDL value should be expected."); }; #endif try { // Execute test case var result = this.ExecuteTestCase(sourceDataInfo, options); Test.Assert(result.Exceptions.Count == 1, "Verify job is cancelled"); Exception exception = result.Exceptions[0]; Helper.VerifyCancelException(exception); TransferItem resumeItem = transferItem.Clone(); DirectoryTransferContext resumeContext = null; journalStream.Position = 0; if (IsStreamJournal) { resumeContext = new DirectoryTransferContext(journalStream); } else { resumeContext = new DirectoryTransferContext(DMLibTestHelper.RandomReloadCheckpoint(checkpoint)); } resumeItem.TransferContext = resumeContext; result = this.RunTransferItems(new List <TransferItem>() { resumeItem }, new TestExecutionOptions <DMLibDataInfo>()); VerificationHelper.VerifyTransferSucceed(result, sourceDataInfo); if (DMLibTestContext.DestType == DMLibDataType.CloudFile) { Helper.CompareSMBPermissions(sourceDataInfo.RootNode, result.DataInfo.RootNode, preserveSMBPermissions); } } finally { #if DEBUG TestHookCallbacks.UnderTesting = false; TestHookCallbacks.GetFilePermissionsCallback = null; TestHookCallbacks.SetFilePermissionsCallback = null; #endif } } }
private void TestDirectorySetAttribute_Restart( int bigFileSizeInKB, int smallFileSizeInKB, int bigFileNum, int smallFileNum, Action <DirNode> bigFileDirAddFileAction, Action <DirNode> smallFileDirAddFileAction, SetAttributesCallbackAsync setAttributesCallback = null, Action <DMLibDataInfo> sourceDataInfoDecorator = null) { int totalFileNum = bigFileNum + smallFileNum; long totalSizeInBytes = ((bigFileSizeInKB * bigFileNum) + (smallFileSizeInKB * smallFileNum)) * 1024; DMLibDataInfo sourceDataInfo = new DMLibDataInfo(string.Empty); DirNode bigFileDirNode = new DirNode("big"); DirNode smallFileDirNode = new DirNode("small"); sourceDataInfo.RootNode.AddDirNode(bigFileDirNode); sourceDataInfo.RootNode.AddDirNode(smallFileDirNode); bigFileDirAddFileAction(bigFileDirNode); smallFileDirAddFileAction(smallFileDirNode); CancellationTokenSource tokenSource = new CancellationTokenSource(); TransferItem transferItem = null; var options = new TestExecutionOptions <DMLibDataInfo> { LimitSpeed = true, IsDirectoryTransfer = true }; using (Stream journalStream = new MemoryStream()) { bool isStreamJournal = random.Next(0, 2) == 0; var transferContext = isStreamJournal ? new DirectoryTransferContext(journalStream) : new DirectoryTransferContext(); transferContext.SetAttributesCallbackAsync = setAttributesCallback; var progressChecker = new ProgressChecker(totalFileNum, totalSizeInBytes, totalFileNum, null, 0, totalSizeInBytes); transferContext.ProgressHandler = progressChecker.GetProgressHandler(); var eventChecker = new TransferEventChecker(); eventChecker.Apply(transferContext); transferContext.FileFailed += (sender, e) => { Helper.VerifyCancelException(e.Exception); }; options.TransferItemModifier = (fileName, item) => { dynamic dirOptions = DefaultTransferDirectoryOptions; dirOptions.Recursive = true; item.Options = dirOptions; item.CancellationToken = tokenSource.Token; item.TransferContext = transferContext; transferItem = item; }; TransferCheckpoint firstCheckpoint = null, secondCheckpoint = null; options.AfterAllItemAdded = () => { // Wait until there are data transferred progressChecker.DataTransferred.WaitOne(); if (!isStreamJournal) { // Store the first checkpoint firstCheckpoint = transferContext.LastCheckpoint; } Thread.Sleep(1000); // Cancel the transfer and store the second checkpoint tokenSource.Cancel(); }; // Cancel and store checkpoint for resume var result = this.ExecuteTestCase(sourceDataInfo, options); if (progressChecker.FailedFilesNumber <= 0) { Test.Error("Verify file number in progress. Failed: {0}", progressChecker.FailedFilesNumber); } TransferCheckpoint firstResumeCheckpoint = null, secondResumeCheckpoint = null; if (!isStreamJournal) { secondCheckpoint = transferContext.LastCheckpoint; Test.Info("Resume with the second checkpoint first."); firstResumeCheckpoint = secondCheckpoint; secondResumeCheckpoint = firstCheckpoint; } // resume with firstResumeCheckpoint TransferItem resumeItem = transferItem.Clone(); progressChecker.Reset(); TransferContext resumeContext = null; if (isStreamJournal) { resumeContext = new DirectoryTransferContext(journalStream) { ProgressHandler = progressChecker.GetProgressHandler() }; } else { resumeContext = new DirectoryTransferContext(DMLibTestHelper.RandomReloadCheckpoint(firstResumeCheckpoint)) { ProgressHandler = progressChecker.GetProgressHandler() }; } resumeContext.SetAttributesCallbackAsync = setAttributesCallback; eventChecker.Reset(); eventChecker.Apply(resumeContext); resumeItem.TransferContext = resumeContext; result = this.RunTransferItems(new List <TransferItem>() { resumeItem }, new TestExecutionOptions <DMLibDataInfo>()); sourceDataInfoDecorator?.Invoke(sourceDataInfo); VerificationHelper.VerifyFinalProgress(progressChecker, totalFileNum, 0, 0); VerificationHelper.VerifySingleTransferStatus(result, totalFileNum, 0, 0, totalSizeInBytes); VerificationHelper.VerifyTransferSucceed(result, sourceDataInfo); if (!isStreamJournal) { // resume with secondResumeCheckpoint resumeItem = transferItem.Clone(); progressChecker.Reset(); resumeContext = new DirectoryTransferContext(DMLibTestHelper.RandomReloadCheckpoint(secondResumeCheckpoint)) { ProgressHandler = progressChecker.GetProgressHandler(), // Need this overwrite callback since some files is already transferred to destination ShouldOverwriteCallbackAsync = DMLibInputHelper.GetDefaultOverwiteCallbackY(), SetAttributesCallbackAsync = setAttributesCallback }; eventChecker.Reset(); eventChecker.Apply(resumeContext); resumeItem.TransferContext = resumeContext; result = this.RunTransferItems(new List <TransferItem>() { resumeItem }, new TestExecutionOptions <DMLibDataInfo>()); VerificationHelper.VerifyFinalProgress(progressChecker, totalFileNum, 0, 0); VerificationHelper.VerifySingleTransferStatus(result, totalFileNum, 0, 0, totalSizeInBytes); VerificationHelper.VerifyTransferSucceed(result, sourceDataInfo); } } }
private void TestDirectoryCheckContentMD5StreamResume(bool checkMD5) { long fileSize = 5 * 1024; int fileCountMulti = 32; long totalSize = fileSize * 4 * fileCountMulti; string wrongMD5 = "wrongMD5"; string wrongMD5File = "wrongMD5File"; string correctMD5File = "correctMD5File"; // Prepare data for transfer items with checkMD5 DMLibDataInfo sourceDataInfo = new DMLibDataInfo(string.Empty); DirNode checkMD5Folder = new DirNode(checkMD5 ? "checkMD5" : "notCheckMD5"); for (int i = 0; i < fileCountMulti; ++i) { var wrongMD5FileNode = new FileNode($"{wrongMD5File}_{i}") { SizeInByte = fileSize, MD5 = wrongMD5 }; checkMD5Folder.AddFileNode(wrongMD5FileNode); DMLibDataHelper.AddOneFileInBytes(checkMD5Folder, $"{correctMD5File}_{i}", fileSize); } sourceDataInfo.RootNode.AddDirNode(checkMD5Folder); SourceAdaptor.GenerateData(sourceDataInfo); DestAdaptor.Cleanup(); TransferEventChecker eventChecker = new TransferEventChecker(); CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); using (var resumeStream = new MemoryStream()) { TransferContext context = new DirectoryTransferContext(resumeStream); eventChecker.Apply(context); ProgressChecker progressChecker = new ProgressChecker(2 * fileCountMulti, totalSize, checkMD5 ? fileCountMulti : 2 * fileCountMulti, null, 0, totalSize); context.ProgressHandler = progressChecker.GetProgressHandler(); List <Exception> transferExceptions = new List <Exception>(); TransferItem checkMD5Item = new TransferItem() { SourceObject = SourceAdaptor.GetTransferObject(sourceDataInfo.RootPath, checkMD5Folder), DestObject = DestAdaptor.GetTransferObject(sourceDataInfo.RootPath, checkMD5Folder), IsDirectoryTransfer = true, SourceType = DMLibTestContext.SourceType, DestType = DMLibTestContext.DestType, CopyMethod = DMLibTestContext.CopyMethod.ToCopyMethod(), TransferContext = context, Options = new DownloadDirectoryOptions() { DisableContentMD5Validation = !checkMD5, Recursive = true, }, CancellationToken = cancellationTokenSource.Token, }; var executionOption = new TestExecutionOptions <DMLibDataInfo>(); executionOption.AfterAllItemAdded = () => { // Wait until there are data transferred if (!progressChecker.DataTransferred.WaitOne(30000)) { Test.Error("No progress in 30s."); } // Cancel the transfer and store the second checkpoint cancellationTokenSource.Cancel(); }; executionOption.LimitSpeed = true; var testResult = this.RunTransferItems(new List <TransferItem>() { checkMD5Item }, executionOption); if (null != testResult.Exceptions) { foreach (var exception in testResult.Exceptions) { Test.Info("Got exception during transferring. {0}", exception); } } eventChecker = new TransferEventChecker(); resumeStream.Position = 0; context = new DirectoryTransferContext(resumeStream); eventChecker.Apply(context); bool failureReported = false; context.FileFailed += (sender, args) => { if (args.Exception != null) { failureReported = args.Exception.Message.Contains(wrongMD5File); } transferExceptions.Add(args.Exception); }; progressChecker.Reset(); context.ProgressHandler = progressChecker.GetProgressHandler(); checkMD5Item = checkMD5Item.Clone(); checkMD5Item.TransferContext = context; testResult = this.RunTransferItems(new List <TransferItem>() { checkMD5Item }, new TestExecutionOptions <DMLibDataInfo>()); DMLibDataInfo expectedDataInfo = sourceDataInfo.Clone(); DMLibDataInfo actualDataInfo = testResult.DataInfo; for (int i = 0; i < fileCountMulti; ++i) { expectedDataInfo.RootNode.GetDirNode(checkMD5Folder.Name).DeleteFileNode($"{wrongMD5File}_{i}"); actualDataInfo.RootNode.GetDirNode(checkMD5Folder.Name).DeleteFileNode($"{wrongMD5File}_{i}"); } Test.Assert(DMLibDataHelper.Equals(expectedDataInfo, actualDataInfo), "Verify transfer result."); Test.Assert(checkMD5 ? failureReported : !failureReported, "Verify md5 check failure is expected."); VerificationHelper.VerifyFinalProgress(progressChecker, checkMD5 ? fileCountMulti : 2 * fileCountMulti, 0, checkMD5 ? fileCountMulti : 0); if (checkMD5) { if (testResult.Exceptions.Count != 0 || transferExceptions.Count != fileCountMulti) { Test.Error("Expect one exception but actually no exception is thrown."); } else { for (int i = 0; i < fileCountMulti; ++i) { VerificationHelper.VerifyExceptionErrorMessage(transferExceptions[i], new string[] { "The MD5 hash calculated from the downloaded data does not match the MD5 hash stored in the property of source" }); } } } else { Test.Assert(testResult.Exceptions.Count == 0, "Should no exception thrown out when disabling check md5"); } } }
public void TestDirectoryCheckContentMD5Resume() { long fileSize = 5 * 1024; int fileCountMulti = 32; long totalSize = fileSize * 4 * fileCountMulti; string wrongMD5 = "wrongMD5"; string checkWrongMD5File = "checkWrongMD5File"; string checkCorrectMD5File = "checkCorrectMD5File"; string notCheckWrongMD5File = "notCheckWrongMD5File"; string notCheckCorrectMD5File = "notCheckCorrectMD5File"; // Prepare data for transfer items with checkMD5 DMLibDataInfo sourceDataInfo = new DMLibDataInfo(string.Empty); DirNode checkMD5Folder = new DirNode("checkMD5"); for (int i = 0; i < fileCountMulti; ++i) { var wrongMD5FileNode = new FileNode($"{checkWrongMD5File}_{i}") { SizeInByte = fileSize, MD5 = wrongMD5 }; checkMD5Folder.AddFileNode(wrongMD5FileNode); DMLibDataHelper.AddOneFileInBytes(checkMD5Folder, $"{checkCorrectMD5File}_{i}", fileSize); } sourceDataInfo.RootNode.AddDirNode(checkMD5Folder); // Prepare data for transfer items with disabling MD5 check DirNode notCheckMD5Folder = new DirNode("notCheckMD5"); for (int i = 0; i < fileCountMulti; ++i) { var wrongMD5FileNode = new FileNode($"{notCheckWrongMD5File}_{i}") { SizeInByte = fileSize, MD5 = wrongMD5 }; notCheckMD5Folder.AddFileNode(wrongMD5FileNode); DMLibDataHelper.AddOneFileInBytes(notCheckMD5Folder, $"{notCheckCorrectMD5File}_{i}", fileSize); } sourceDataInfo.RootNode.AddDirNode(notCheckMD5Folder); SourceAdaptor.GenerateData(sourceDataInfo); TransferEventChecker eventChecker = new TransferEventChecker(); CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); TransferContext context = new DirectoryTransferContext(); eventChecker.Apply(context); ProgressChecker progressChecker = new ProgressChecker(4 * fileCountMulti, totalSize, 3 * fileCountMulti, null, 0, totalSize); context.ProgressHandler = progressChecker.GetProgressHandler(); List <Exception> transferExceptions = new List <Exception>(); TransferItem checkMD5Item = new TransferItem() { SourceObject = SourceAdaptor.GetTransferObject(sourceDataInfo.RootPath, checkMD5Folder), DestObject = DestAdaptor.GetTransferObject(sourceDataInfo.RootPath, checkMD5Folder), IsDirectoryTransfer = true, SourceType = DMLibTestContext.SourceType, DestType = DMLibTestContext.DestType, IsServiceCopy = DMLibTestContext.IsAsync, TransferContext = context, Options = new DownloadDirectoryOptions() { DisableContentMD5Validation = false, Recursive = true, }, CancellationToken = cancellationTokenSource.Token, }; TransferItem notCheckMD5Item = new TransferItem() { SourceObject = SourceAdaptor.GetTransferObject(sourceDataInfo.RootPath, notCheckMD5Folder), DestObject = DestAdaptor.GetTransferObject(sourceDataInfo.RootPath, notCheckMD5Folder), IsDirectoryTransfer = true, SourceType = DMLibTestContext.SourceType, DestType = DMLibTestContext.DestType, IsServiceCopy = DMLibTestContext.IsAsync, TransferContext = context, Options = new DownloadDirectoryOptions() { DisableContentMD5Validation = true, Recursive = true, }, CancellationToken = cancellationTokenSource.Token }; var executionOption = new TestExecutionOptions <DMLibDataInfo>(); executionOption.AfterAllItemAdded = () => { // Wait until there are data transferred progressChecker.DataTransferred.WaitOne(); // Cancel the transfer and store the second checkpoint cancellationTokenSource.Cancel(); }; executionOption.LimitSpeed = true; var testResult = this.RunTransferItems(new List <TransferItem>() { checkMD5Item, notCheckMD5Item }, executionOption); eventChecker = new TransferEventChecker(); context = new DirectoryTransferContext(DMLibTestHelper.RandomReloadCheckpoint(context.LastCheckpoint)); eventChecker.Apply(context); bool failureReported = false; context.FileFailed += (sender, args) => { if (args.Exception != null) { failureReported = args.Exception.Message.Contains(checkWrongMD5File); } transferExceptions.Add(args.Exception); }; progressChecker.Reset(); context.ProgressHandler = progressChecker.GetProgressHandler(); checkMD5Item = checkMD5Item.Clone(); notCheckMD5Item = notCheckMD5Item.Clone(); checkMD5Item.TransferContext = context; notCheckMD5Item.TransferContext = context; testResult = this.RunTransferItems(new List <TransferItem>() { checkMD5Item, notCheckMD5Item }, new TestExecutionOptions <DMLibDataInfo>()); DMLibDataInfo expectedDataInfo = sourceDataInfo.Clone(); DMLibDataInfo actualDataInfo = testResult.DataInfo; for (int i = 0; i < fileCountMulti; ++i) { expectedDataInfo.RootNode.GetDirNode(checkMD5Folder.Name).DeleteFileNode($"{checkWrongMD5File}_{i}"); expectedDataInfo.RootNode.GetDirNode(notCheckMD5Folder.Name).DeleteFileNode($"{notCheckWrongMD5File}_{i}"); actualDataInfo.RootNode.GetDirNode(checkMD5Folder.Name).DeleteFileNode($"{checkWrongMD5File}_{i}"); actualDataInfo.RootNode.GetDirNode(notCheckMD5Folder.Name).DeleteFileNode($"{notCheckWrongMD5File}_{i}"); } Test.Assert(DMLibDataHelper.Equals(expectedDataInfo, actualDataInfo), "Verify transfer result."); Test.Assert(failureReported, "Verify md5 check failure is reported."); VerificationHelper.VerifyFinalProgress(progressChecker, 3 * fileCountMulti, 0, fileCountMulti); if (testResult.Exceptions.Count != 0 || transferExceptions.Count != fileCountMulti) { Test.Error("Expect one exception but actually no exception is thrown."); } else { for (int i = 0; i < fileCountMulti; ++i) { VerificationHelper.VerifyExceptionErrorMessage(transferExceptions[i], new string[] { "The MD5 hash calculated from the downloaded data does not match the MD5 hash stored in the property of source" }); } } }