|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Storage;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.VisualStudio.LanguageServices.UnitTests;
using Roslyn.Test.Utilities;
using Roslyn.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.UnitTests.WorkspaceServices
{
[UseExportProvider]
public abstract class AbstractPersistentStorageTests : IDisposable
{
public enum Size
{
Small,
Medium,
Large,
ExtraLarge,
}
private const int Iterations = 20;
private const int NumThreads = 10;
private const string PersistentFolderPrefix = "PersistentStorageTests_";
private readonly Encoding _encoding = Encoding.UTF8;
private AbstractPersistentStorageService? _storageService;
private readonly DisposableDirectory _persistentFolderRoot;
private readonly TempDirectory _persistentFolder;
// 256k (larger than the 100k that CloudCache uses to decide when to dump to an external file).
// See https://dev.azure.com/devdiv/DevDiv/_git/VS.CloudCache?path=%2Fsrc%2FMicrosoft.VisualStudio.Cache%2FCacheService.cs&version=GBmain&line=35&lineEnd=36&lineStartColumn=1&lineEndColumn=1&lineStyle=plain&_a=contents
private const int ExtraLargeSize = 256 * 1024;
private const int LargeSize = (int)(SQLite.v2.SQLitePersistentStorage.MaxPooledByteArrayLength * 2);
private const int MediumSize = (int)(SQLite.v2.SQLitePersistentStorage.MaxPooledByteArrayLength / 2);
private const string SmallData1 = "Hello ESENT";
private const string SmallData2 = "Goodbye ESENT";
private static readonly string MediumData1 = string.Join(",", Enumerable.Repeat(SmallData1, MediumSize / SmallData1.Length));
private static readonly string MediumData2 = string.Join(",", Enumerable.Repeat(SmallData2, MediumSize / SmallData2.Length));
private static readonly string LargeData1 = string.Join(",", Enumerable.Repeat(SmallData1, LargeSize / SmallData1.Length));
private static readonly string LargeData2 = string.Join(",", Enumerable.Repeat(SmallData2, LargeSize / SmallData2.Length));
private static readonly string ExtraLargeData1 = string.Join(",", Enumerable.Repeat(SmallData1, ExtraLargeSize / SmallData1.Length));
private static readonly string ExtraLargeData2 = string.Join(",", Enumerable.Repeat(SmallData2, ExtraLargeSize / SmallData2.Length));
private static readonly Checksum s_checksum1 = Checksum.Create("1");
private static readonly Checksum s_checksum2 = Checksum.Create("2");
static AbstractPersistentStorageTests()
{
Assert.NotEqual(s_checksum1, s_checksum2);
Assert.True(MediumData1.Length < SQLite.v2.SQLitePersistentStorage.MaxPooledByteArrayLength);
Assert.True(MediumData2.Length < SQLite.v2.SQLitePersistentStorage.MaxPooledByteArrayLength);
Assert.True(LargeData1.Length > SQLite.v2.SQLitePersistentStorage.MaxPooledByteArrayLength);
Assert.True(LargeData2.Length > SQLite.v2.SQLitePersistentStorage.MaxPooledByteArrayLength);
}
protected AbstractPersistentStorageTests()
{
_persistentFolderRoot = new DisposableDirectory(new TempRoot());
_persistentFolder = _persistentFolderRoot.CreateDirectory(PersistentFolderPrefix + Guid.NewGuid());
ThreadPool.GetMinThreads(out var workerThreads, out var completionPortThreads);
ThreadPool.SetMinThreads(Math.Max(workerThreads, NumThreads), completionPortThreads);
}
public void Dispose()
{
// This should cause the service to release the cached connection it maintains for the primary workspace
_storageService?.GetTestAccessor().Shutdown();
_persistentFolderRoot.Dispose();
}
private static string GetData1(Size size)
=> size == Size.Small ? SmallData1 :
size == Size.Medium ? MediumData1 :
size == Size.Large ? LargeData1 : ExtraLargeData1;
private static string GetData2(Size size)
=> size == Size.Small ? SmallData2 :
size == Size.Medium ? MediumData2 :
size == Size.Large ? LargeData2 : ExtraLargeData2;
private static Checksum? GetChecksum1(bool withChecksum)
=> withChecksum ? s_checksum1 : null;
private static Checksum? GetChecksum2(bool withChecksum)
=> withChecksum ? s_checksum2 : null;
[Fact]
public async Task TestNullFilePaths()
{
var solution = CreateOrOpenSolution(nullPaths: true);
var streamName = "stream";
var storage = await GetStorageAsync(solution);
var project = solution.Projects.First();
var document = project.Documents.First();
Assert.False(await storage.WriteStreamAsync(project, streamName, EncodeString("")));
Assert.False(await storage.WriteStreamAsync(document, streamName, EncodeString("")));
Assert.Null(await storage.ReadStreamAsync(project, streamName));
Assert.Null(await storage.ReadStreamAsync(document, streamName));
}
[Theory, CombinatorialData, WorkItem("https://devdiv.visualstudio.com/DevDiv/_queries/edit/1436188")]
public async Task CacheDirectoryInPathWithSingleQuote(Size size, bool withChecksum, [CombinatorialRange(0, Iterations)] int iteration)
{
_ = iteration;
using var folderRoot = new DisposableDirectory(new TempRoot());
var folder = folderRoot.CreateDirectory(PersistentFolderPrefix + "'" + Guid.NewGuid());
var solution = CreateOrOpenSolution(folder);
var streamName1 = "PersistentService_Solution_WriteReadDifferentInstances1";
var streamName2 = "PersistentService_Solution_WriteReadDifferentInstances2";
{
var storage = await GetStorageAsync(solution, folder);
Assert.True(await storage.WriteStreamAsync(streamName1, EncodeString(GetData1(size)), GetChecksum1(withChecksum)));
Assert.True(await storage.WriteStreamAsync(streamName2, EncodeString(GetData2(size)), GetChecksum2(withChecksum)));
}
{
var storage = await GetStorageAsync(solution, folder);
Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(streamName1, GetChecksum1(withChecksum))));
Assert.Equal(GetData2(size), ReadStringToEnd(await storage.ReadStreamAsync(streamName2, GetChecksum2(withChecksum))));
}
}
[Theory, CombinatorialData]
public async Task PersistentService_Solution_WriteReadDifferentInstances(Size size, bool withChecksum, [CombinatorialRange(0, Iterations)] int iteration)
{
_ = iteration;
var solution = CreateOrOpenSolution();
var streamName1 = "PersistentService_Solution_WriteReadDifferentInstances1";
var streamName2 = "PersistentService_Solution_WriteReadDifferentInstances2";
{
var storage = await GetStorageAsync(solution);
Assert.True(await storage.WriteStreamAsync(streamName1, EncodeString(GetData1(size)), GetChecksum1(withChecksum)));
Assert.True(await storage.WriteStreamAsync(streamName2, EncodeString(GetData2(size)), GetChecksum2(withChecksum)));
}
{
var storage = await GetStorageAsync(solution);
Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(streamName1, GetChecksum1(withChecksum))));
Assert.Equal(GetData2(size), ReadStringToEnd(await storage.ReadStreamAsync(streamName2, GetChecksum2(withChecksum))));
}
}
[Theory, CombinatorialData]
public async Task PersistentService_Solution_WriteReadReopenSolution(Size size, bool withChecksum, [CombinatorialRange(0, Iterations)] int iteration)
{
_ = iteration;
var solution = CreateOrOpenSolution();
var streamName1 = "PersistentService_Solution_WriteReadReopenSolution1";
var streamName2 = "PersistentService_Solution_WriteReadReopenSolution2";
{
var storage = await GetStorageAsync(solution);
Assert.True(await storage.WriteStreamAsync(streamName1, EncodeString(GetData1(size)), GetChecksum1(withChecksum)));
Assert.True(await storage.WriteStreamAsync(streamName2, EncodeString(GetData2(size)), GetChecksum2(withChecksum)));
}
solution = CreateOrOpenSolution();
{
var storage = await GetStorageAsync(solution);
Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(streamName1, GetChecksum1(withChecksum))));
Assert.Equal(GetData2(size), ReadStringToEnd(await storage.ReadStreamAsync(streamName2, GetChecksum2(withChecksum))));
}
}
[Theory, CombinatorialData]
public async Task PersistentService_Solution_WriteReadSameInstance(Size size, bool withChecksum, [CombinatorialRange(0, Iterations)] int iteration)
{
_ = iteration;
var solution = CreateOrOpenSolution();
var streamName1 = "PersistentService_Solution_WriteReadSameInstance1";
var streamName2 = "PersistentService_Solution_WriteReadSameInstance2";
var storage = await GetStorageAsync(solution);
Assert.True(await storage.WriteStreamAsync(streamName1, EncodeString(GetData1(size)), GetChecksum1(withChecksum)));
Assert.True(await storage.WriteStreamAsync(streamName2, EncodeString(GetData2(size)), GetChecksum2(withChecksum)));
Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(streamName1, GetChecksum1(withChecksum))));
Assert.Equal(GetData2(size), ReadStringToEnd(await storage.ReadStreamAsync(streamName2, GetChecksum2(withChecksum))));
}
[Theory, CombinatorialData]
public async Task PersistentService_Project_WriteReadSameInstance(Size size, bool withChecksum, [CombinatorialRange(0, Iterations)] int iteration)
{
_ = iteration;
var solution = CreateOrOpenSolution();
var streamName1 = "PersistentService_Project_WriteReadSameInstance1";
var streamName2 = "PersistentService_Project_WriteReadSameInstance2";
var storage = await GetStorageAsync(solution);
var project = solution.Projects.Single();
Assert.True(await storage.WriteStreamAsync(project, streamName1, EncodeString(GetData1(size)), GetChecksum1(withChecksum)));
Assert.True(await storage.WriteStreamAsync(project, streamName2, EncodeString(GetData2(size)), GetChecksum2(withChecksum)));
Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(project, streamName1, GetChecksum1(withChecksum))));
Assert.Equal(GetData2(size), ReadStringToEnd(await storage.ReadStreamAsync(project, streamName2, GetChecksum2(withChecksum))));
}
[Theory, CombinatorialData]
public async Task PersistentService_Document_WriteReadSameInstance(Size size, bool withChecksum, [CombinatorialRange(0, Iterations)] int iteration)
{
_ = iteration;
var solution = CreateOrOpenSolution();
var streamName1 = "PersistentService_Document_WriteReadSameInstance1";
var streamName2 = "PersistentService_Document_WriteReadSameInstance2";
var storage = await GetStorageAsync(solution);
var document = solution.Projects.Single().Documents.Single();
Assert.True(await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(size)), GetChecksum1(withChecksum)));
Assert.True(await storage.WriteStreamAsync(document, streamName2, EncodeString(GetData2(size)), GetChecksum2(withChecksum)));
Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(document, streamName1, GetChecksum1(withChecksum))));
Assert.Equal(GetData2(size), ReadStringToEnd(await storage.ReadStreamAsync(document, streamName2, GetChecksum2(withChecksum))));
}
[Theory, CombinatorialData]
public async Task PersistentService_Solution_SimultaneousWrites([CombinatorialRange(0, Iterations)] int iteration)
{
_ = iteration;
var solution = CreateOrOpenSolution();
var streamName1 = "PersistentService_Solution_SimultaneousWrites1";
var storage = await GetStorageAsync(solution);
DoSimultaneousWrites(s => storage.WriteStreamAsync(streamName1, EncodeString(s)));
var value = int.Parse(ReadStringToEnd(await storage.ReadStreamAsync(streamName1)));
Assert.True(value >= 0);
Assert.True(value < NumThreads);
}
[Theory, CombinatorialData]
public async Task PersistentService_Project_SimultaneousWrites([CombinatorialRange(0, Iterations)] int iteration)
{
_ = iteration;
var solution = CreateOrOpenSolution();
var streamName1 = "PersistentService_Project_SimultaneousWrites1";
var storage = await GetStorageAsync(solution);
DoSimultaneousWrites(s => storage.WriteStreamAsync(solution.Projects.Single(), streamName1, EncodeString(s)));
var value = int.Parse(ReadStringToEnd(await storage.ReadStreamAsync(solution.Projects.Single(), streamName1)));
Assert.True(value >= 0);
Assert.True(value < NumThreads);
}
[Theory, CombinatorialData]
public async Task PersistentService_Document_SimultaneousWrites([CombinatorialRange(0, Iterations)] int iteration)
{
_ = iteration;
var solution = CreateOrOpenSolution();
var streamName1 = "PersistentService_Document_SimultaneousWrites1";
var storage = await GetStorageAsync(solution);
DoSimultaneousWrites(s => storage.WriteStreamAsync(solution.Projects.Single().Documents.Single(), streamName1, EncodeString(s)));
var value = int.Parse(ReadStringToEnd(await storage.ReadStreamAsync(solution.Projects.Single().Documents.Single(), streamName1)));
Assert.True(value >= 0);
Assert.True(value < NumThreads);
}
[Theory, CombinatorialData]
public async Task PersistentService_Solution_SimultaneousReads(Size size, bool withChecksum, [CombinatorialRange(0, Iterations)] int iteration)
{
_ = iteration;
var solution = CreateOrOpenSolution();
var streamName1 = "PersistentService_Solution_SimultaneousReads1";
var storage = await GetStorageAsync(solution);
Assert.True(await storage.WriteStreamAsync(streamName1, EncodeString(GetData1(size)), GetChecksum1(withChecksum)));
DoSimultaneousReads(async () => ReadStringToEnd(await storage.ReadStreamAsync(streamName1, GetChecksum1(withChecksum))), GetData1(size));
}
[Theory, CombinatorialData]
public async Task PersistentService_Project_SimultaneousReads(Size size, bool withChecksum, [CombinatorialRange(0, Iterations)] int iteration)
{
_ = iteration;
var solution = CreateOrOpenSolution();
var streamName1 = "PersistentService_Project_SimultaneousReads1";
var storage = await GetStorageAsync(solution);
Assert.True(await storage.WriteStreamAsync(solution.Projects.Single(), streamName1, EncodeString(GetData1(size)), GetChecksum1(withChecksum)));
DoSimultaneousReads(async () => ReadStringToEnd(await storage.ReadStreamAsync(solution.Projects.Single(), streamName1, GetChecksum1(withChecksum))), GetData1(size));
}
[Theory, CombinatorialData]
public async Task PersistentService_Document_SimultaneousReads(Size size, bool withChecksum, [CombinatorialRange(0, Iterations)] int iteration)
{
_ = iteration;
var solution = CreateOrOpenSolution();
var streamName1 = "PersistentService_Document_SimultaneousReads1";
var storage = await GetStorageAsync(solution);
Assert.True(await storage.WriteStreamAsync(solution.Projects.Single().Documents.Single(), streamName1, EncodeString(GetData1(size)), GetChecksum1(withChecksum)));
DoSimultaneousReads(async () => ReadStringToEnd(await storage.ReadStreamAsync(solution.Projects.Single().Documents.Single(), streamName1, GetChecksum1(withChecksum))), GetData1(size));
}
[Theory, CombinatorialData]
public async Task TestReadChecksumReturnsNullWhenNeverWritten([CombinatorialRange(0, Iterations)] int iteration)
{
_ = iteration;
var solution = CreateOrOpenSolution();
var streamName1 = "TestReadChecksumReturnsNullWhenNeverWritten";
var storage = await GetStorageAsync(solution);
Assert.False(await storage.ChecksumMatchesAsync(streamName1, s_checksum1));
}
[Theory, CombinatorialData]
public async Task TestCanReadWithNullChecksumSomethingWrittenWithNonNullChecksum(Size size, [CombinatorialRange(0, Iterations)] int iteration)
{
_ = iteration;
var solution = CreateOrOpenSolution();
var streamName1 = "TestCanReadWithNullChecksumSomethingWrittenWithNonNullChecksum";
{
var storage = await GetStorageAsync(solution);
Assert.True(await storage.WriteStreamAsync(streamName1, EncodeString(GetData1(size)), s_checksum1));
}
{
var storage = await GetStorageAsync(solution);
Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(streamName1, checksum: null)));
}
}
[Theory, CombinatorialData]
public async Task TestCannotReadWithMismatchedChecksums(Size size, [CombinatorialRange(0, Iterations)] int iteration)
{
_ = iteration;
var solution = CreateOrOpenSolution();
var streamName1 = "TestCannotReadWithMismatchedChecksums";
{
var storage = await GetStorageAsync(solution);
Assert.True(await storage.WriteStreamAsync(streamName1, EncodeString(GetData1(size)), s_checksum1));
}
{
var storage = await GetStorageAsync(solution);
Assert.Null(await storage.ReadStreamAsync(streamName1, s_checksum2));
}
}
[Theory, CombinatorialData]
public async Task TestCannotReadChecksumIfWriteDidNotIncludeChecksum(Size size, [CombinatorialRange(0, Iterations)] int iteration)
{
_ = iteration;
var solution = CreateOrOpenSolution();
var streamName1 = "TestCannotReadChecksumIfWriteDidNotIncludeChecksum";
{
var storage = await GetStorageAsync(solution);
Assert.True(await storage.WriteStreamAsync(streamName1, EncodeString(GetData1(size)), checksum: null));
}
{
var storage = await GetStorageAsync(solution);
Assert.False(await storage.ChecksumMatchesAsync(streamName1, s_checksum1));
}
}
[Theory, CombinatorialData]
public async Task TestReadChecksumProducesWrittenChecksum(Size size, [CombinatorialRange(0, Iterations)] int iteration)
{
_ = iteration;
var solution = CreateOrOpenSolution();
var streamName1 = "TestReadChecksumProducesWrittenChecksum";
{
var storage = await GetStorageAsync(solution);
Assert.True(await storage.WriteStreamAsync(streamName1, EncodeString(GetData1(size)), checksum: s_checksum1));
}
{
var storage = await GetStorageAsync(solution);
Assert.True(await storage.ChecksumMatchesAsync(streamName1, s_checksum1));
}
}
[Theory, CombinatorialData]
public async Task TestReadChecksumProducesLastWrittenChecksum1(Size size, [CombinatorialRange(0, Iterations)] int iteration)
{
_ = iteration;
var solution = CreateOrOpenSolution();
var streamName1 = "TestReadChecksumProducesLastWrittenChecksum1";
{
var storage = await GetStorageAsync(solution);
Assert.True(await storage.WriteStreamAsync(streamName1, EncodeString(GetData1(size)), checksum: s_checksum1));
Assert.True(await storage.WriteStreamAsync(streamName1, EncodeString(GetData1(size)), checksum: null));
}
{
var storage = await GetStorageAsync(solution);
Assert.False(await storage.ChecksumMatchesAsync(streamName1, s_checksum1));
}
}
[Theory, CombinatorialData]
public async Task TestReadChecksumProducesLastWrittenChecksum2(Size size, [CombinatorialRange(0, Iterations)] int iteration)
{
_ = iteration;
var solution = CreateOrOpenSolution();
var streamName1 = "TestReadChecksumProducesLastWrittenChecksum2";
{
var storage = await GetStorageAsync(solution);
Assert.True(await storage.WriteStreamAsync(streamName1, EncodeString(GetData1(size)), checksum: null));
Assert.True(await storage.WriteStreamAsync(streamName1, EncodeString(GetData1(size)), checksum: s_checksum1));
}
{
var storage = await GetStorageAsync(solution);
Assert.True(await storage.ChecksumMatchesAsync(streamName1, s_checksum1));
}
}
[Theory, CombinatorialData]
public async Task TestReadChecksumProducesLastWrittenChecksum3(Size size, [CombinatorialRange(0, Iterations)] int iteration)
{
_ = iteration;
var solution = CreateOrOpenSolution();
var streamName1 = "TestReadChecksumProducesLastWrittenChecksum3";
{
var storage = await GetStorageAsync(solution);
Assert.True(await storage.WriteStreamAsync(streamName1, EncodeString(GetData1(size)), checksum: s_checksum1));
Assert.True(await storage.WriteStreamAsync(streamName1, EncodeString(GetData1(size)), checksum: s_checksum2));
}
{
var storage = await GetStorageAsync(solution);
Assert.True(await storage.ChecksumMatchesAsync(streamName1, s_checksum2));
}
}
[Theory, CombinatorialData]
public async Task TestOpenWithSolutionKeyReadWithDocumentKey(Size size, [CombinatorialRange(0, Iterations)] int iteration)
{
_ = iteration;
var solution = CreateOrOpenSolution();
var document = solution.Projects.Single().Documents.Single();
var streamName1 = "stream";
{
var storage = await GetStorageAsync(solution);
await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(size)), checksum: s_checksum1);
}
{
var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution));
Assert.True(await storage.ChecksumMatchesAsync(DocumentKey.ToDocumentKey(document), streamName1, s_checksum1));
Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(DocumentKey.ToDocumentKey(document), streamName1)));
}
}
[Theory, CombinatorialData]
public async Task TestOpenWithSolutionKeyReadWithDocument(Size size, [CombinatorialRange(0, Iterations)] int iteration)
{
_ = iteration;
var solution = CreateOrOpenSolution();
var document = solution.Projects.Single().Documents.Single();
var streamName1 = "stream";
{
var storage = await GetStorageAsync(solution);
await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(size)), checksum: s_checksum1);
}
{
var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution));
Assert.True(await storage.ChecksumMatchesAsync(document, streamName1, s_checksum1));
Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(document, streamName1)));
}
}
[Theory, CombinatorialData]
public async Task TestOpenWithSolutionReadWithDocumentKey(Size size, [CombinatorialRange(0, Iterations)] int iteration)
{
_ = iteration;
var solution = CreateOrOpenSolution();
var document = solution.Projects.Single().Documents.Single();
var streamName1 = "stream";
{
var storage = await GetStorageAsync(solution);
await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(size)), checksum: s_checksum1);
}
{
var storage = await GetStorageAsync(solution);
Assert.True(await storage.ChecksumMatchesAsync(DocumentKey.ToDocumentKey(document), streamName1, s_checksum1));
Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(DocumentKey.ToDocumentKey(document), streamName1)));
}
}
[Theory, CombinatorialData]
public async Task TestOpenWithSolutionReadWithDocument(Size size, [CombinatorialRange(0, Iterations)] int iteration)
{
_ = iteration;
var solution = CreateOrOpenSolution();
var document = solution.Projects.Single().Documents.Single();
var streamName1 = "stream";
{
var storage = await GetStorageAsync(solution);
await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(size)), checksum: s_checksum1);
}
{
var storage = await GetStorageAsync(solution);
Assert.True(await storage.ChecksumMatchesAsync(document, streamName1, s_checksum1));
Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(document, streamName1)));
}
}
[Theory, CombinatorialData]
public async Task TestOpenWithSolutionReadWithDocumentKeyAndDocument1(Size size, [CombinatorialRange(0, Iterations)] int iteration)
{
_ = iteration;
var solution = CreateOrOpenSolution();
var document = solution.Projects.Single().Documents.Single();
var streamName1 = "stream";
{
var storage = await GetStorageAsync(solution);
await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(size)), checksum: s_checksum1);
}
{
var storage = await GetStorageAsync(solution);
Assert.True(await storage.ChecksumMatchesAsync(DocumentKey.ToDocumentKey(document), streamName1, s_checksum1));
Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(DocumentKey.ToDocumentKey(document), streamName1)));
Assert.True(await storage.ChecksumMatchesAsync(document, streamName1, s_checksum1));
Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(document, streamName1)));
}
}
[Theory, CombinatorialData]
public async Task TestOpenWithSolutionReadWithDocumentKeyAndDocument2(Size size, [CombinatorialRange(0, Iterations)] int iteration)
{
_ = iteration;
var solution = CreateOrOpenSolution();
var document = solution.Projects.Single().Documents.Single();
var streamName1 = "stream";
{
var storage = await GetStorageAsync(solution);
await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(size)), checksum: s_checksum1);
}
{
var storage = await GetStorageAsync(solution);
Assert.True(await storage.ChecksumMatchesAsync(document, streamName1, s_checksum1));
Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(document, streamName1)));
Assert.True(await storage.ChecksumMatchesAsync(DocumentKey.ToDocumentKey(document), streamName1, s_checksum1));
Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(DocumentKey.ToDocumentKey(document), streamName1)));
}
}
[Theory, CombinatorialData]
public async Task TestOpenWithSolutionKeyReadWithDocumentKeyAndDocument1(Size size, [CombinatorialRange(0, Iterations)] int iteration)
{
_ = iteration;
var solution = CreateOrOpenSolution();
var document = solution.Projects.Single().Documents.Single();
var streamName1 = "stream";
{
var storage = await GetStorageAsync(solution);
await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(size)), checksum: s_checksum1);
}
{
var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution));
Assert.True(await storage.ChecksumMatchesAsync(DocumentKey.ToDocumentKey(document), streamName1, s_checksum1));
Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(DocumentKey.ToDocumentKey(document), streamName1)));
Assert.True(await storage.ChecksumMatchesAsync(document, streamName1, s_checksum1));
Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(document, streamName1)));
}
}
[Theory, CombinatorialData]
public async Task TestOpenWithSolutionKeyReadWithDocumentKeyAndDocument2(Size size, [CombinatorialRange(0, Iterations)] int iteration)
{
_ = iteration;
var solution = CreateOrOpenSolution();
var document = solution.Projects.Single().Documents.Single();
var streamName1 = "stream";
{
var storage = await GetStorageAsync(solution);
await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(size)), checksum: s_checksum1);
}
{
var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution));
Assert.True(await storage.ChecksumMatchesAsync(document, streamName1, s_checksum1));
Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(document, streamName1)));
Assert.True(await storage.ChecksumMatchesAsync(DocumentKey.ToDocumentKey(document), streamName1, s_checksum1));
Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(DocumentKey.ToDocumentKey(document), streamName1)));
}
}
[Theory, CombinatorialData]
public async Task TestOpenWithSolutionKeyReadWithDocumentKey_WriteWithSolutionKey(Size size, [CombinatorialRange(0, Iterations)] int iteration)
{
_ = iteration;
var solution = CreateOrOpenSolution();
var document = solution.Projects.Single().Documents.Single();
var streamName1 = "stream";
{
var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution));
await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(size)), checksum: s_checksum1);
}
{
var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution));
Assert.True(await storage.ChecksumMatchesAsync(DocumentKey.ToDocumentKey(document), streamName1, s_checksum1));
Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(DocumentKey.ToDocumentKey(document), streamName1)));
}
}
[Theory, CombinatorialData]
public async Task TestOpenWithSolutionKeyReadWithDocument_WriteWithSolutionKey(Size size, [CombinatorialRange(0, Iterations)] int iteration)
{
_ = iteration;
var solution = CreateOrOpenSolution();
var document = solution.Projects.Single().Documents.Single();
var streamName1 = "stream";
{
var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution));
await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(size)), checksum: s_checksum1);
}
{
var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution));
Assert.True(await storage.ChecksumMatchesAsync(document, streamName1, s_checksum1));
Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(document, streamName1)));
}
}
[Theory, CombinatorialData]
public async Task TestOpenWithSolutionReadWithDocumentKey_WriteWithSolutionKey(Size size, [CombinatorialRange(0, Iterations)] int iteration)
{
_ = iteration;
var solution = CreateOrOpenSolution();
var document = solution.Projects.Single().Documents.Single();
var streamName1 = "stream";
{
var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution));
await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(size)), checksum: s_checksum1);
}
{
var storage = await GetStorageAsync(solution);
Assert.True(await storage.ChecksumMatchesAsync(DocumentKey.ToDocumentKey(document), streamName1, s_checksum1));
Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(DocumentKey.ToDocumentKey(document), streamName1)));
}
}
[Theory, CombinatorialData]
public async Task TestOpenWithSolutionReadWithDocument_WriteWithSolutionKey(Size size, [CombinatorialRange(0, Iterations)] int iteration)
{
_ = iteration;
var solution = CreateOrOpenSolution();
var document = solution.Projects.Single().Documents.Single();
var streamName1 = "stream";
{
var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution));
await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(size)), checksum: s_checksum1);
}
{
var storage = await GetStorageAsync(solution);
Assert.True(await storage.ChecksumMatchesAsync(document, streamName1, s_checksum1));
Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(document, streamName1)));
}
}
[Theory, CombinatorialData]
public async Task TestOpenWithSolutionReadWithDocumentKeyAndDocument1_WriteWithSolutionKey(Size size, [CombinatorialRange(0, Iterations)] int iteration)
{
_ = iteration;
var solution = CreateOrOpenSolution();
var document = solution.Projects.Single().Documents.Single();
var streamName1 = "stream";
{
var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution));
await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(size)), checksum: s_checksum1);
}
{
var storage = await GetStorageAsync(solution);
Assert.True(await storage.ChecksumMatchesAsync(DocumentKey.ToDocumentKey(document), streamName1, s_checksum1));
Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(DocumentKey.ToDocumentKey(document), streamName1)));
Assert.True(await storage.ChecksumMatchesAsync(document, streamName1, s_checksum1));
Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(document, streamName1)));
}
}
[Theory, CombinatorialData]
public async Task TestOpenWithSolutionReadWithDocumentKeyAndDocument2_WriteWithSolutionKey(Size size, [CombinatorialRange(0, Iterations)] int iteration)
{
_ = iteration;
var solution = CreateOrOpenSolution();
var document = solution.Projects.Single().Documents.Single();
var streamName1 = "stream";
{
var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution));
await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(size)), checksum: s_checksum1);
}
{
var storage = await GetStorageAsync(solution);
Assert.True(await storage.ChecksumMatchesAsync(document, streamName1, s_checksum1));
Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(document, streamName1)));
Assert.True(await storage.ChecksumMatchesAsync(DocumentKey.ToDocumentKey(document), streamName1, s_checksum1));
Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(DocumentKey.ToDocumentKey(document), streamName1)));
}
}
[Theory, CombinatorialData]
public async Task TestOpenWithSolutionKeyReadWithDocumentKeyAndDocument1_WriteWithSolutionKey(Size size, [CombinatorialRange(0, Iterations)] int iteration)
{
_ = iteration;
var solution = CreateOrOpenSolution();
var document = solution.Projects.Single().Documents.Single();
var streamName1 = "stream";
{
var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution));
await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(size)), checksum: s_checksum1);
}
{
var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution));
Assert.True(await storage.ChecksumMatchesAsync(DocumentKey.ToDocumentKey(document), streamName1, s_checksum1));
Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(DocumentKey.ToDocumentKey(document), streamName1)));
Assert.True(await storage.ChecksumMatchesAsync(document, streamName1, s_checksum1));
Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(document, streamName1)));
}
}
[Theory, CombinatorialData]
public async Task TestOpenWithSolutionKeyReadWithDocumentKeyAndDocument2_WriteWithSolutionKey(Size size, [CombinatorialRange(0, Iterations)] int iteration)
{
_ = iteration;
var solution = CreateOrOpenSolution();
var document = solution.Projects.Single().Documents.Single();
var streamName1 = "stream";
{
var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution));
await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(size)), checksum: s_checksum1);
}
{
var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution));
Assert.True(await storage.ChecksumMatchesAsync(document, streamName1, s_checksum1));
Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(document, streamName1)));
Assert.True(await storage.ChecksumMatchesAsync(DocumentKey.ToDocumentKey(document), streamName1, s_checksum1));
Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(DocumentKey.ToDocumentKey(document), streamName1)));
}
}
[Fact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1174219")]
public void CacheDirectoryShouldNotBeAtRoot()
{
var workspace = new AdhocWorkspace(FeaturesTestCompositions.Features.GetHostServices());
workspace.AddSolution(SolutionInfo.Create(SolutionId.CreateNewId(), new VersionStamp(), @"D:\git\PCLCrypto\PCLCrypto.sln"));
var configuration = workspace.Services.GetRequiredService<IPersistentStorageConfiguration>();
var location = configuration.TryGetStorageLocation(SolutionKey.ToSolutionKey(workspace.CurrentSolution));
Assert.False(location?.StartsWith("/") ?? false);
}
[Theory, CombinatorialData]
public async Task PersistentService_ReadByteTwice(Size size, bool withChecksum, [CombinatorialRange(0, Iterations)] int iteration)
{
_ = iteration;
var solution = CreateOrOpenSolution();
var streamName1 = "PersistentService_ReadByteTwice";
{
var storage = await GetStorageAsync(solution);
Assert.True(await storage.WriteStreamAsync(streamName1, EncodeString(GetData1(size)), GetChecksum1(withChecksum)));
}
{
var storage = await GetStorageAsync(solution);
using var stream = await storage.ReadStreamAsync(streamName1, GetChecksum1(withChecksum));
Contract.ThrowIfNull(stream);
stream.ReadByte();
stream.ReadByte();
}
}
[Theory, CombinatorialData]
public async Task TestPersistSyntaxTreeIndex([CombinatorialRange(0, Iterations)] int iteration)
{
_ = iteration;
var solution = CreateOrOpenSolution();
var id = DocumentId.CreateNewId(solution.Projects.Single().Id);
solution = solution.AddDocument(id, "file.cs", "class C { void M() }", filePath: Path.Combine(_persistentFolder.Path, "file.cs"));
var document = solution.GetRequiredDocument(id);
{
_ = await GetStorageAsync(solution);
var index = await SyntaxTreeIndex.GetRequiredIndexAsync(document, default);
await index.SaveAsync(document, _storageService!);
var index2 = await SyntaxTreeIndex.LoadAsync(_storageService!, DocumentKey.ToDocumentKey(document), checksum: null, new StringTable(), default);
Assert.NotNull(index2);
}
}
[Theory, CombinatorialData]
public async Task TestPersistTopLevelSyntaxTreeIndex([CombinatorialRange(0, Iterations)] int iteration)
{
_ = iteration;
var solution = CreateOrOpenSolution();
var id = DocumentId.CreateNewId(solution.Projects.Single().Id);
solution = solution.AddDocument(id, "file.cs", "class C { void M() }", filePath: Path.Combine(_persistentFolder.Path, "file.cs"));
var document = solution.GetRequiredDocument(id);
{
_ = await GetStorageAsync(solution);
var index = await TopLevelSyntaxTreeIndex.GetRequiredIndexAsync(document, default);
await index.SaveAsync(document, _storageService!);
var index2 = await TopLevelSyntaxTreeIndex.LoadAsync(_storageService!, DocumentKey.ToDocumentKey(document), checksum: null, new StringTable(), default);
Assert.NotNull(index2);
}
}
private static void DoSimultaneousReads(Func<Task<string>> read, string expectedValue)
{
var barrier = new Barrier(NumThreads);
var countdown = new CountdownEvent(NumThreads);
var exceptions = new List<Exception>();
for (var i = 0; i < NumThreads; i++)
{
Task.Run(async () =>
{
barrier.SignalAndWait();
try
{
Assert.Equal(expectedValue, await read());
}
catch (Exception ex)
{
lock (exceptions)
{
exceptions.Add(ex);
}
}
countdown.Signal();
});
}
countdown.Wait();
Assert.Equal([], exceptions);
}
private static void DoSimultaneousWrites(Func<string, Task> write)
{
var barrier = new Barrier(NumThreads);
var countdown = new CountdownEvent(NumThreads);
var exceptions = new List<Exception>();
for (var i = 0; i < NumThreads; i++)
{
ThreadPool.QueueUserWorkItem(s =>
{
var id = (int)s;
barrier.SignalAndWait();
try
{
write(id + "").Wait();
}
catch (Exception ex)
{
lock (exceptions)
{
exceptions.Add(ex);
}
}
countdown.Signal();
}, i);
}
countdown.Wait();
Assert.Empty(exceptions);
}
protected Solution CreateOrOpenSolution(TempDirectory? persistentFolder = null, bool nullPaths = false)
{
persistentFolder ??= _persistentFolder;
_storageService?.GetTestAccessor().Shutdown();
var solutionFile = persistentFolder.CreateOrOpenFile("Solution1.sln").WriteAllText("");
var info = SolutionInfo.Create(SolutionId.CreateNewId(), VersionStamp.Create(), solutionFile.Path);
var workspace = new AdhocWorkspace(VisualStudioTestCompositions.LanguageServices.GetHostServices());
workspace.AddSolution(info);
var solution = workspace.CurrentSolution;
var projectFile = persistentFolder.CreateOrOpenFile("Project1.csproj").WriteAllText("");
solution = solution.AddProject(ProjectInfo.Create(ProjectId.CreateNewId(), VersionStamp.Create(), "Project1", "Project1", LanguageNames.CSharp,
filePath: nullPaths ? null : projectFile.Path));
var project = solution.Projects.Single();
var documentFile = persistentFolder.CreateOrOpenFile("Document1.cs").WriteAllText("");
solution = solution.AddDocument(DocumentInfo.Create(DocumentId.CreateNewId(project.Id), "Document1",
filePath: nullPaths ? null : documentFile.Path));
// Apply this to the workspace so our Solution is the primary branch ID, which matches our usual behavior
workspace.TryApplyChanges(solution);
return workspace.CurrentSolution;
}
internal async Task<IChecksummedPersistentStorage> GetStorageAsync(
Solution solution,
TempDirectory? persistentFolder = null,
IPersistentStorageFaultInjector? faultInjector = null,
bool throwOnFailure = true)
{
// If we handed out one for a previous test, we need to shut that down first
persistentFolder ??= _persistentFolder;
_storageService?.GetTestAccessor().Shutdown();
var configuration = new MockPersistentStorageConfiguration(solution.Id, persistentFolder.Path, throwOnFailure);
_storageService = (AbstractPersistentStorageService)solution.Workspace.Services.SolutionServices.GetPersistentStorageService();
var storage = await _storageService.GetStorageAsync(
SolutionKey.ToSolutionKey(solution), configuration, faultInjector, CancellationToken.None);
// If we're injecting faults, we expect things to be strange
if (faultInjector == null)
{
Assert.NotEqual(NoOpPersistentStorage.TestAccessor.GetStorageInstance(SolutionKey.ToSolutionKey(solution)), storage);
}
return storage;
}
internal async Task<IChecksummedPersistentStorage> GetStorageFromKeyAsync(
HostWorkspaceServices services, SolutionKey solutionKey, IPersistentStorageFaultInjector? faultInjector = null)
{
var configuration = new MockPersistentStorageConfiguration(solutionKey.Id, _persistentFolder.Path, throwOnFailure: true);
_storageService = (AbstractPersistentStorageService)services.SolutionServices.GetPersistentStorageService();
var storage = await _storageService.GetStorageAsync(
solutionKey, configuration, faultInjector, CancellationToken.None);
// If we're injecting faults, we expect things to be strange
if (faultInjector == null)
{
Assert.NotEqual(NoOpPersistentStorage.TestAccessor.GetStorageInstance(solutionKey), storage);
}
return storage;
}
private class MockPersistentStorageConfiguration : IPersistentStorageConfiguration
{
private readonly SolutionId _solutionId;
private readonly string _storageLocation;
public MockPersistentStorageConfiguration(SolutionId solutionId, string storageLocation, bool throwOnFailure)
{
_solutionId = solutionId;
_storageLocation = storageLocation;
ThrowOnFailure = throwOnFailure;
}
public bool ThrowOnFailure { get; }
public string? TryGetStorageLocation(SolutionKey solutionKey)
=> solutionKey.Id == _solutionId ? _storageLocation : null;
}
protected Stream EncodeString(string text)
{
var bytes = _encoding.GetBytes(text);
var stream = new MemoryStream(bytes);
return stream;
}
private string ReadStringToEnd(Stream? stream)
{
Contract.ThrowIfNull(stream);
using (stream)
{
using var memoryStream = new MemoryStream();
stream.CopyTo(memoryStream);
return _encoding.GetString(memoryStream.ToArray());
}
}
}
}
|