public async Task LinkCanBeDeletedWhileOpenStreamHasAnotherLinkOpen() { using (var directory = new DisposableDirectory(FileSystem)) { var context = new Context(Logger); await TestStore(context, _clock, async store => { // Put some content. var r1 = await store.PutRandomAsync(context, 10); ResultTestExtensions.ShouldBeSuccess((BoolResult)r1); // Hardlink it somewhere outside the cache. var path = directory.CreateRandomFileName(); var r2 = await store.PlaceFileAsync( context, r1.ContentHash, path, FileAccessMode.ReadOnly, FileReplacementMode.FailIfExists, FileRealizationMode.HardLink, null); r2.IsPlaced().Should().BeTrue(); // Open a stream to the cache link. var r3 = await store.OpenStreamAsync(context, r1.ContentHash, null); r3.ShouldBeSuccess(); using (r3.Stream) { // Verify the external link can be deleted FileSystem.DeleteFile(path); } }); } }
public Task PutContentSucceedsAfterFullnessReleased() { var contentSize = ContentSizeToStartHardPurging(3); return(TestStore(Context, Clock, async store => { var triggeredEviction = false; store.OnLruEnumerationWithTime = hashes => { triggeredEviction = true; return Task.FromResult(hashes); }; using (var pinContext = store.CreatePinContext()) { await PutRandomAndPinAsync(store, contentSize, pinContext); await PutRandomAndPinAsync(store, contentSize, pinContext); PutResult putResult = await store.PutRandomAsync(Context, contentSize); putResult.ShouldBeError(); triggeredEviction.Should().BeTrue(); triggeredEviction = false; await pinContext.DisposeAsync(); } PutResult putAfterReleaseResult = await store.PutRandomAsync(Context, contentSize); ResultTestExtensions.ShouldBeSuccess((BoolResult)putAfterReleaseResult); triggeredEviction.Should().BeTrue(); })); }
public Task PutStreamFullPinnedCacheThrows() { return(TestStore(Context, Clock, async store => { using (var pinContext = store.CreatePinContext()) { var pinRequest = new PinRequest(pinContext); var r = await store.PutRandomAsync(Context, MaxSizeHard / 3); ResultTestExtensions.ShouldBeSuccess((BoolResult)r); Clock.Increment(); Assert.True(await store.ContainsAsync(Context, r.ContentHash, pinRequest)); Clock.Increment(); r = await store.PutRandomAsync(Context, MaxSizeHard / 3); Clock.Increment(); Assert.True(await store.ContainsAsync(Context, r.ContentHash, pinRequest)); Clock.Increment(); var data = ThreadSafeRandom.GetBytes(MaxSizeHard / 2); using (var dataStream = new MemoryStream(data)) { var result = await store.PutStreamAsync(Context, dataStream, ContentHashType, null); Assert.False(result.Succeeded); } await store.SyncAsync(Context); } })); }
private async Task PlaceFileAcrossDrivesAsync( FileRealizationMode allowedFileRealizationMode, Func <PlaceFileResult, bool> checkResult) { // This only works when we have multiple drives. if (FileSystem is MemoryFileSystem) { return; } using (var testDirectory = new DisposableDirectory(FileSystem)) { var context = new Context(Logger); using (var store = Create(testDirectory.Path, _clock)) { var startupResult = await store.StartupAsync(context); startupResult.ShouldBeSuccess(); try { byte[] bytes = ThreadSafeRandom.GetBytes(ValueSize); AbsolutePath pathToContent = testDirectory.CreateRandomFileName(); FileSystem.WriteAllBytes(pathToContent, bytes); // Put the content into the store via copy var putResult = await store.PutFileAsync( context, pathToContent, FileRealizationMode.Copy, ContentHashType, null); ResultTestExtensions.ShouldBeSuccess((BoolResult)putResult); var pathToPlaceDifferentVolume = new AbsolutePath(PathGeneratorUtilities.GetAbsolutePath("D", "foo.txt")); try { var result = await store.PlaceFileAsync( context, putResult.ContentHash, pathToPlaceDifferentVolume, FileAccessMode.ReadOnly, FileReplacementMode.FailIfExists, allowedFileRealizationMode, null); Assert.True(checkResult(result), result.ToString()); } finally { FileSystem.DeleteFile(pathToPlaceDifferentVolume); } } finally { await store.ShutdownAsync(context).ShouldBeSuccess(); } } } }
public async Task PlaceMultipleFilesFromMultipleSessions() { using (var placeDirectory = new DisposableDirectory(FileSystem)) { var pathFromStream = placeDirectory.Path / "file-stream.dat"; var pathFromPath = placeDirectory.Path / "file-path.dat"; var pathFromNonexistent = placeDirectory.Path / "file-nonexistent.dat"; await RunTestAsync(ImplicitPin.None, null, async (context, session) => { var putStreamResult = await session.PutRandomAsync( context, ContentHashType, false, ContentByteCount, Token); ResultTestExtensions.ShouldBeSuccess((BoolResult)putStreamResult); var putFileResult = await session.PutRandomFileAsync( context, FileSystem, ContentHashType, false, ContentByteCount, Token); ResultTestExtensions.ShouldBeSuccess((BoolResult)putFileResult); var hashesWithPaths = new[] { new ContentHashWithPath(putStreamResult.ContentHash, pathFromStream), new ContentHashWithPath(ContentHash.Random(), pathFromNonexistent), new ContentHashWithPath(putFileResult.ContentHash, pathFromPath), }; var results = await session.PlaceFileAsync( context, hashesWithPaths, FileAccessMode.ReadOnly, FileReplacementMode.ReplaceExisting, FileRealizationMode.Any, Token); foreach (var result in results) { var r = await result; switch (r.Index) { case 0: Assert.True(r.Item.Succeeded); break; case 1: Assert.Equal(PlaceFileResult.ResultCode.NotPlacedContentNotFound, r.Item.Code); break; case 2: Assert.True(r.Item.Succeeded); break; default: Assert.False(true); break; } } }); } }
public async Task PlaceFileRequiringNewReplicaCloseToHardLimitDoesNotHang() { var context = new Context(Logger); using (DisposableDirectory testDirectory = new DisposableDirectory(FileSystem)) { #pragma warning disable AsyncFixer04 // A disposable object used in a fire & forget async call Task testTask = TestStore(context, Clock, testDirectory, async store => #pragma warning restore AsyncFixer04 // A disposable object used in a fire & forget async call { // Make a file which will overflow the cache size with just 2 copies. PutResult putResult = await store.PutRandomAsync(context, ContentSizeToStartHardPurging(2)); ResultTestExtensions.ShouldBeSuccess((BoolResult)putResult); ContentHash hash = putResult.ContentHash; // Hardlink the file out 1024 times. Since the limit is 1024 total, and we already have 1 in the CAS, // this will overflow the links and cause the CAS to create a new replica for it. This will cause // the purger to consider that hash for eviction *while making room for that hash*, which is the // trigger for the previous deadlock that this test will now guard against. for (int i = 0; i < 1024; i++) { PlaceFileResult placeResult = await store.PlaceFileAsync( context, hash, testDirectory.Path / $"hardlink{i}.txt", FileAccessMode.ReadOnly, FileReplacementMode.FailIfExists, FileRealizationMode.HardLink, null); // The checks below are just to make sure that the calls completed as expected. // The most important part is that they complete *at all*, which is enforced by // racing against the Task.Delay in the outer scope. if (i < 1023 || SucceedsEvenIfFull) { // The first 1023 links should succeed (bringing it up to the limit of 1024) // And *all* of the calls should succeed if the cache takes new content even when overflowed. Assert.True(placeResult.Succeeded); } else { // If the implementation rejects overflowing content, then the last call should fail. Assert.False(placeResult.Succeeded); Assert.Contains("Failed to reserve space", placeResult.ErrorMessage); } } }); // Race between the test and a 2-minute timer. This can be increased if the test ends up flaky. Task firstCompletedTask = await Task.WhenAny(testTask, Task.Delay(TimeSpan.FromMinutes(2))); // The test should finish first, long before a minute passes, but it won't if it deadlocks. Assert.True(firstCompletedTask == testTask); await firstCompletedTask; } }
public Task PinExistingInStreamSession() { return(RunTestAsync(ImplicitPin.None, null, async(context, session) => { var putResult = await session.PutRandomAsync(context, ContentHashType, false, ContentByteCount, Token); ResultTestExtensions.ShouldBeSuccess((BoolResult)putResult); var result = await session.PinAsync(context, putResult.ContentHash, Token); result.ShouldBeSuccess(); })); }
public Task OpenStreamExistingFromPathSession() { return(RunTestAsync(ImplicitPin.None, null, async(context, session) => { var putResult = await session.PutRandomFileAsync( context, FileSystem, ContentHashType, false, ContentByteCount, Token); ResultTestExtensions.ShouldBeSuccess((BoolResult)putResult); var result = await session.OpenStreamAsync(context, putResult.ContentHash, Token); result.ShouldBeSuccess(); Assert.NotNull(result.Stream); Assert.Equal(ContentByteCount, result.Stream.Length); result.Stream.Dispose(); })); }
public Task PinMultipleContentsFromMultipleSessions() { return(RunTestAsync(ImplicitPin.None, null, async(context, session) => { var putStreamResult = await session.PutRandomAsync( context, ContentHashType, false, ContentByteCount, Token); ResultTestExtensions.ShouldBeSuccess((BoolResult)putStreamResult); var putFileResult = await session.PutRandomFileAsync(context, FileSystem, ContentHashType, false, ContentByteCount, Token); ResultTestExtensions.ShouldBeSuccess((BoolResult)putFileResult); var results = await session.PinAsync(context, new[] { putStreamResult.ContentHash, ContentHash.Random(), putFileResult.ContentHash }, Token); foreach (var result in results) { var r = await result; switch (r.Index) { case 0: r.Item.ShouldBeSuccess(); break; case 1: Assert.Equal(PinResult.ResultCode.ContentNotFound, r.Item.Code); break; case 2: r.Item.ShouldBeSuccess(); break; default: Assert.False(true); break; } } })); }
public async Task PlaceFileExistingFromPathSession() { using (var placeDirectory = new DisposableDirectory(FileSystem)) { var path = placeDirectory.Path / "file.dat"; await RunTestAsync(ImplicitPin.None, null, async (context, session) => { var putResult = await session.PutRandomFileAsync( context, FileSystem, ContentHashType, false, ContentByteCount, Token); ResultTestExtensions.ShouldBeSuccess((BoolResult)putResult); var result = await session.PlaceFileAsync( context, putResult.ContentHash, path, FileAccessMode.ReadOnly, FileReplacementMode.ReplaceExisting, FileRealizationMode.Any, Token); Assert.True(result.IsPlaced()); }); } }
public void ImplicitFailureCastFromResultFailure() => ResultTestExtensions .AssertFailure <int, string>(Result.Failure("One")) .Should() .Be("One");
public void ImplicitSuccessCastFromResultSuccess() => ResultTestExtensions .AssertSuccess <int, string>(Result.Success(1)) .Should() .Be(1);