public void ShouldPassThroughAllProvidedIntermediatesInOrder() { // Arrange using var arena = new TestArena(); var resumeStrategy = Substitute.For <IResumeStrategy>(); resumeStrategy.CanResume( Arg.Any <IFileResource>(), Arg.Any <IFileResource>(), Arg.Any <Stream>(), Arg.Any <Stream>()) .Returns(false); var(source, target) = (arena.SourceFileSystem, arena.TargetFileSystem); var relPath = GetRandomString(); var allData = GetRandomBytes(100, 200); // small buffer, likely to be read in one pass arena.CreateResource( arena.SourcePath, relPath, allData); var intermediateCalls = new List <string>(); var endCalls = new List <string>(); var intermediate1 = new GenericPassThrough( (data, count) => intermediateCalls.Add("first"), () => endCalls.Add("first") ); var intermediate2 = new GenericPassThrough( (data, count) => intermediateCalls.Add("second"), () => endCalls.Add("second") ); var intermediate3 = new GenericPassThrough( (data, count) => intermediateCalls.Add("third"), () => endCalls.Add("third") ); var expected = new[] { "first", "second", "third" }; var sut = Create( resumeStrategy, new IPassThrough[] { intermediate1, intermediate2, intermediate3 }, // this test is about notifiers, so we'll // just pretend that all sources are to be required // at the target filters: CreatePermissiveFilter()); // Act sut.Synchronize(source, target); // Assert Expect(intermediateCalls) .To.Equal(expected); Expect(endCalls) .To.Equal(expected); }
public void ShouldResumeWhenResumeStrategySaysYes() { // Arrange using var arena = new TestArena(); var resumeStrategy = Substitute.For <IResumeStrategy>(); resumeStrategy.CanResume( Arg.Any <IFileResource>(), Arg.Any <IFileResource>(), Arg.Any <Stream>(), Arg.Any <Stream>() ) .Returns(true); var(source, target) = (arena.SourceFileSystem, arena.TargetFileSystem); var relPath = GetRandomString(); var allData = GetRandomBytes(1024); var partialSize = GetRandomInt(384, 768); var partialData = allData.Take(partialSize) .ToArray(); var expected = allData.Skip(partialSize) .ToArray(); var targetFilePath = arena.CreateResource( arena.TargetPath, relPath, partialData); arena.CreateResource( arena.SourcePath, relPath, allData); var captured = new List <byte>(); var ended = false; var intermediate = new GenericPassThrough( (data, count) => captured.AddRange(data.Take(count)), () => ended = true ); var sut = Create( resumeStrategy, new IPassThrough[] { intermediate } ); // Act sut.Synchronize(source, target); // Assert var targetData = File.ReadAllBytes(targetFilePath); Expect(targetData) .To.Equal(allData); var transferred = captured.ToArray(); Expect(transferred) .To.Equal(expected, "unexpected transferred data"); Expect(ended) .To.Be.True(); }
public void ShouldRetryOnError() { // Arrange using var arena = new TestArena(); var options = Substitute.For <IOptions>(); options.Retries.Returns(3); using var resetter = new AutoResetter <int>(() => { var original = StreamSource.MaxBuffer; StreamSource.MaxBuffer = 1; return(original); }, original => StreamSource.MaxBuffer = original); var haveErrored = false; var errorPassThrough = new GenericPassThrough( (data, size) => { if (!haveErrored) { haveErrored = true; throw new DirectoryNotFoundException("foo"); } }, () => { } ); // keep the size small since we're forcing a flush at every byte var sourceFile = arena.CreateSourceFile(data: GetRandomBytes(10, 20)); var expected = arena.TargetPathFor(sourceFile); var sut = Create( intermediatePipes: new IPassThrough[] { errorPassThrough }, options: options); // Act sut.Synchronize(arena.SourceFileSystem, arena.TargetFileSystem); // Assert Expect(expected) .To.Exist(); Expect(File.ReadAllBytes(expected)) .To.Equal(sourceFile.Data); }
public void ShouldNotifyNotifiablePipesWithPossibleResumeSizes() { // Arrange using var arena = new TestArena(); var missingData = GetRandomBytes(100); arena.CreateSourceResource( "missing", missingData); var partialFileAllData = "hello world".AsBytes(); var partialTargetData = "hello".AsBytes(); arena.CreateSourceResource("partial", partialFileAllData); arena.CreateTargetResource("partial", partialTargetData); var existingData = GetRandomBytes(100); arena.CreateSourceResource("existing", existingData); arena.CreateTargetResource("existing", existingData); var intermediate1 = new GenericPassThrough( (data, count) => { }, () => { }); var notifiable = new NotifiableGenericPassThrough( (data, count) => { }, () => { }); var options = Substitute.For <IOptions>(); options.ResumeCheckBytes.Returns(512); var sut = Create( new SimpleResumeStrategy(options, Substitute.For <IMessageWriter>()), new IPassThrough[] { notifiable, intermediate1 } .Randomize() .ToArray() ); // Act sut.Synchronize( arena.SourceFileSystem, arena.TargetFileSystem); // Assert var partialResultData = arena.TargetFileSystem.ReadAllBytes( "partial"); Expect(partialResultData) .To.Equal(partialFileAllData, () => $@"Expected { partialFileAllData.Length } bytes, but got { partialResultData.Length }: '{ partialFileAllData.AsString() }' vs '{ partialResultData.AsString() }'"); Expect(arena.TargetFileSystem.ReadAllBytes("existing")) .To.Equal(existingData); Expect(arena.TargetFileSystem.ReadAllBytes("missing")) .To.Equal(missingData); Expect(notifiable.BatchStartedNotifications) .To.Contain.Only(1) .Item(); // should only have one initial notification var initial = notifiable.BatchStartedNotifications.Single(); Expect(initial.resources) .To.Contain .Only(2) .Items(); // should only have partial and missing in sync queue Expect(initial.resources) .To.Contain.Exactly(1) .Matched.By( resource => resource.RelativePath == "missing" ); Expect(initial.resources) .To.Contain.Exactly(1) .Matched.By( resource => resource.RelativePath == "partial" ); var batchComplete = notifiable.BatchCompletedNotifications.Single(); Expect(batchComplete.resources) .To.Contain .Only(2) .Items(); // should only have partial and missing in sync queue Expect(batchComplete.resources) .To.Contain.Exactly(1) .Matched.By( resource => resource.RelativePath == "missing" ); Expect(batchComplete.resources) .To.Contain.Exactly(1) .Matched.By( resource => resource.RelativePath == "partial" ); Expect(notifiable.ResourceNotifications) .To.Contain.Exactly(2) .Items(); var missingResource = notifiable.ResourceNotifications.First(); Expect(missingResource.source.RelativePath) .To.Equal("missing"); Expect(missingResource.source.Size) .To.Equal(missingData.Length); Expect(missingResource.target) .To.Be.Null(); var partialResource = notifiable.ResourceNotifications.Second(); Expect(partialResource.source.RelativePath) .To.Equal("partial"); Expect(partialResource.source.Size) .To.Equal(partialFileAllData.Length); Expect(partialResource.target.RelativePath) .To.Equal("partial"); Expect(partialResource.target.Size) .To.Equal(partialTargetData.Length); Expect(notifiable.CompletedNotifications) .To.Contain.Exactly(2) .Items(); missingResource = notifiable.CompletedNotifications.First(); Expect(missingResource.source.RelativePath) .To.Equal("missing"); Expect(missingResource.source.Size) .To.Equal(missingData.Length); Expect(missingResource.target) .Not.To.Be.Null(); Expect(missingResource.target.RelativePath) .To.Equal("missing"); Expect(missingResource.target.Size) .To.Equal(missingData.Length); partialResource = notifiable.CompletedNotifications.Second(); Expect(partialResource.source.RelativePath) .To.Equal("partial"); Expect(partialResource.source.Size) .To.Equal(partialFileAllData.Length); Expect(partialResource.target.RelativePath) .To.Equal("partial"); Expect(partialResource.target.Size) .To.Equal(partialTargetData.Length); }