diff --git a/src/EditorFeatures/TestUtilities/Remote/InProcRemostHostClient.cs b/src/EditorFeatures/TestUtilities/Remote/InProcRemostHostClient.cs index 85dac513c710e78f9d6f9c50b75550a66efb8025..647516bdc9ddc707f109465eaa6c58e760cb7c02 100644 --- a/src/EditorFeatures/TestUtilities/Remote/InProcRemostHostClient.cs +++ b/src/EditorFeatures/TestUtilities/Remote/InProcRemostHostClient.cs @@ -53,6 +53,8 @@ public static async Task CreateAsync(Workspace workspace, Canc _rpc.Disconnected += OnRpcDisconnected; } + public AssetStorage AssetStorage => _inprocServices.AssetStorage; + protected override async Task CreateServiceSessionAsync(string serviceName, PinnedRemotableDataScope snapshot, object callbackTarget, CancellationToken cancellationToken) { // get stream from service hub to communicate snapshot/asset related information @@ -80,14 +82,46 @@ private void OnRpcDisconnected(object sender, JsonRpcDisconnectedEventArgs e) Disconnected(); } + public class ServiceProvider : IServiceProvider + { + private static readonly TraceSource s_traceSource = new TraceSource("inprocRemoteClient"); + + private readonly AssetStorage _storage; + + public ServiceProvider() + { + _storage = new AssetStorage(enableCleanup: false); + } + + public AssetStorage AssetStorage => _storage; + + public object GetService(Type serviceType) + { + if (typeof(TraceSource) == serviceType) + { + return s_traceSource; + } + + if (typeof(AssetStorage) == serviceType) + { + return _storage; + } + + throw ExceptionUtilities.UnexpectedValue(serviceType); + } + } + private class InProcRemoteServices { - private static readonly IServiceProvider s_serviceProvider = new ServiceProvider(); + private readonly ServiceProvider _serviceProvider; public InProcRemoteServices() { + _serviceProvider = new ServiceProvider(); } + public AssetStorage AssetStorage => _serviceProvider.AssetStorage; + public Task RequestServiceAsync(string serviceName, CancellationToken cancellationToken) { switch (serviceName) @@ -95,48 +129,28 @@ public Task RequestServiceAsync(string serviceName, CancellationToken ca case WellKnownRemoteHostServices.RemoteHostService: { var tuple = FullDuplexStream.CreateStreams(); - return Task.FromResult(new WrappedStream(new RemoteHostService(tuple.Item1, s_serviceProvider), tuple.Item2)); + return Task.FromResult(new WrappedStream(new RemoteHostService(tuple.Item1, _serviceProvider), tuple.Item2)); } case WellKnownServiceHubServices.CodeAnalysisService: { var tuple = FullDuplexStream.CreateStreams(); - return Task.FromResult(new WrappedStream(new CodeAnalysisService(tuple.Item1, s_serviceProvider), tuple.Item2)); + return Task.FromResult(new WrappedStream(new CodeAnalysisService(tuple.Item1, _serviceProvider), tuple.Item2)); } case WellKnownServiceHubServices.SnapshotService: { var tuple = FullDuplexStream.CreateStreams(); - return Task.FromResult(new WrappedStream(new SnapshotService(tuple.Item1, s_serviceProvider), tuple.Item2)); + return Task.FromResult(new WrappedStream(new SnapshotService(tuple.Item1, _serviceProvider), tuple.Item2)); } case WellKnownServiceHubServices.RemoteSymbolSearchUpdateEngine: { var tuple = FullDuplexStream.CreateStreams(); - return Task.FromResult(new WrappedStream(new RemoteSymbolSearchUpdateEngine(tuple.Item1, s_serviceProvider), tuple.Item2)); + return Task.FromResult(new WrappedStream(new RemoteSymbolSearchUpdateEngine(tuple.Item1, _serviceProvider), tuple.Item2)); } } throw ExceptionUtilities.UnexpectedValue(serviceName); } - private class ServiceProvider : IServiceProvider - { - private static readonly TraceSource s_traceSource = new TraceSource("inprocRemoteClient"); - - public object GetService(Type serviceType) - { - if (typeof(TraceSource) == serviceType) - { - return s_traceSource; - } - - if (typeof(AssetStorage) == serviceType) - { - return AssetStorage.Default; - } - - throw ExceptionUtilities.UnexpectedValue(serviceType); - } - } - private class WrappedStream : Stream { private readonly IDisposable _service; diff --git a/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.RemoteHostClientService.cs b/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.RemoteHostClientService.cs index 8baa1c2e4aae8d209c875d7c557006fe579f9aab..27f05746dc9c99ff6792d85460266227ece1e630 100644 --- a/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.RemoteHostClientService.cs +++ b/src/VisualStudio/Core/Def/Implementation/Remote/RemoteHostClientServiceFactory.RemoteHostClientService.cs @@ -160,7 +160,7 @@ private void AddGlobalAssets(CancellationToken cancellationToken) using (Logger.LogBlock(FunctionId.RemoteHostClientService_AddGlobalAssetsAsync, cancellationToken)) { var snapshotService = _workspace.Services.GetService(); - var assetBuilder = new CustomAssetBuilder(_workspace.CurrentSolution); + var assetBuilder = new CustomAssetBuilder(_workspace); foreach (var reference in _analyzerService.GetHostAnalyzerReferences()) { diff --git a/src/VisualStudio/Core/Test.Next/Services/AssetServiceTests.cs b/src/VisualStudio/Core/Test.Next/Services/AssetServiceTests.cs new file mode 100644 index 0000000000000000000000000000000000000000..14906ebc5726f2505c2d4a40160bebb8f8240332 --- /dev/null +++ b/src/VisualStudio/Core/Test.Next/Services/AssetServiceTests.cs @@ -0,0 +1,62 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Remote; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Roslyn.VisualStudio.Next.UnitTests.Remote +{ + public class AssetServiceTests + { + [Fact, Trait(Traits.Feature, Traits.Features.RemoteHost)] + public async Task TestAssets() + { + var sessionId = 0; + var checksum = new Checksum(Guid.NewGuid().ToByteArray()); + var data = new object(); + + var storage = new AssetStorage(enableCleanup: false); + var source = new MyAssetSource(storage, sessionId, checksum, data); + + var service = new AssetService(sessionId, storage); + var stored = await service.GetAssetAsync(checksum, CancellationToken.None); + Assert.Equal(data, stored); + + var stored2 = await service.GetAssetsAsync(new[] { checksum }, CancellationToken.None); + Assert.Equal(1, stored2.Count); + + Assert.Equal(checksum, stored2[0].Item1); + Assert.Equal(data, stored2[0].Item2); + } + + private class MyAssetSource : AssetSource + { + private readonly Checksum _checksum; + private readonly object _data; + + public MyAssetSource(AssetStorage assetStorage, int sessionId, Checksum checksum, object data) : + base(assetStorage, sessionId) + { + _checksum = checksum; + _data = data; + } + + public override Task> RequestAssetsAsync(int serviceId, ISet checksums, CancellationToken cancellationToken) + { + if (checksums.Contains(_checksum)) + { + return Task.FromResult>(new List<(Checksum, object)>() { ValueTuple.Create(_checksum, _data) }); + } + + // fail + Assert.True(false); + return null; + } + } + } +} diff --git a/src/VisualStudio/Core/Test.Next/Services/AssetStorageTests.cs b/src/VisualStudio/Core/Test.Next/Services/AssetStorageTests.cs new file mode 100644 index 0000000000000000000000000000000000000000..66fc41014b29a60fee3480b3e7d49ee1b52d018b --- /dev/null +++ b/src/VisualStudio/Core/Test.Next/Services/AssetStorageTests.cs @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Remote; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Roslyn.VisualStudio.Next.UnitTests.Remote +{ + public class AssetStorageTests + { + [Fact, Trait(Traits.Feature, Traits.Features.RemoteHost)] + public void TestCreation() + { + var sessionId = 0; + + var storage = new AssetStorage(enableCleanup: false); + var source = new MyAssetSource(storage, sessionId); + + var stored = storage.TryGetAssetSource(sessionId); + Assert.Equal(source, stored); + + storage.UnregisterAssetSource(sessionId); + + var none = storage.TryGetAssetSource(sessionId); + Assert.Null(none); + } + + [Fact, Trait(Traits.Feature, Traits.Features.RemoteHost)] + public void TestGetAssets() + { + var sessionId = 0; + + var storage = new AssetStorage(enableCleanup: false); + var source = new MyAssetSource(storage, sessionId); + + var checksum = new Checksum(Guid.NewGuid().ToByteArray()); + var data = new object(); + + Assert.True(storage.TryAddAsset(checksum, data)); + + object stored; + Assert.True(storage.TryGetAsset(checksum, out stored)); + } + + private class MyAssetSource : AssetSource + { + public MyAssetSource(AssetStorage assetStorage, int sessionId) : + base(assetStorage, sessionId) + { + } + + public override Task> RequestAssetsAsync(int serviceId, ISet checksums, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + } + } +} diff --git a/src/VisualStudio/Core/Test.Next/Services/ChecksumUtils.cs b/src/VisualStudio/Core/Test.Next/Services/ChecksumUtils.cs new file mode 100644 index 0000000000000000000000000000000000000000..f04d916b035f61bb5e39a9d26128c695ad200844 --- /dev/null +++ b/src/VisualStudio/Core/Test.Next/Services/ChecksumUtils.cs @@ -0,0 +1,70 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Threading; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Serialization; +using Xunit; + +namespace Roslyn.VisualStudio.Next.UnitTests.Remote +{ + internal static class ChecksumUtils + { + public static Dictionary GetAssetMap(this Solution solution) + { + var map = new Dictionary(); + + SolutionStateChecksums solutionChecksums; + Assert.True(solution.State.TryGetStateChecksums(out solutionChecksums)); + + solutionChecksums.Find(solution.State, Flatten(solutionChecksums), map, CancellationToken.None); + + foreach (var project in solution.Projects) + { + ProjectStateChecksums projectChecksums; + Assert.True(project.State.TryGetStateChecksums(out projectChecksums)); + + projectChecksums.Find(project.State, Flatten(projectChecksums), map, CancellationToken.None); + + foreach (var document in project.Documents) + { + DocumentStateChecksums documentChecksums; + Assert.True(document.State.TryGetStateChecksums(out documentChecksums)); + + documentChecksums.Find(document.State, Flatten(documentChecksums), map, CancellationToken.None); + + // fix up due to source text can't be obtained synchronously in product code + map[documentChecksums.Text] = document.State.GetTextSynchronously(CancellationToken.None); + } + } + + return map; + } + + private static HashSet Flatten(ChecksumWithChildren checksums) + { + var set = new HashSet(); + set.Add(checksums.Checksum); + + foreach (var child in checksums.Children) + { + var checksum = child as Checksum; + if (checksum != null) + { + set.Add(checksum); + } + + var collection = child as ChecksumCollection; + if (collection != null) + { + foreach (var item in collection) + { + set.Add(item); + } + } + } + + return set; + } + } +} diff --git a/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs b/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs new file mode 100644 index 0000000000000000000000000000000000000000..8f44bb67ce3966bcac2bac19effaaa5c1f22ae4c --- /dev/null +++ b/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; +using Microsoft.CodeAnalysis.Remote; +using Microsoft.CodeAnalysis.Serialization; +using Roslyn.Test.Utilities; +using Roslyn.Test.Utilities.Remote; +using Xunit; + +namespace Roslyn.VisualStudio.Next.UnitTests.Remote +{ + public class ServiceHubServicesTests + { + [Fact, Trait(Traits.Feature, Traits.Features.RemoteHost)] + public void TestRemoteHostCreation() + { + var remoteHostService = CreateService(); + Assert.NotNull(remoteHostService); + } + + [Fact, Trait(Traits.Feature, Traits.Features.RemoteHost)] + public void TestRemoteHostConnect() + { + var remoteHostService = CreateService(); + + var input = "Test"; + var output = remoteHostService.Connect(input); + + Assert.Equal(input, output); + } + + [Fact, Trait(Traits.Feature, Traits.Features.RemoteHost)] + public async Task TestRemoteHostSynchronize() + { + var code = @"class Test { void Method() { } }"; + + using (var workspace = await TestWorkspace.CreateCSharpAsync(code)) + { + var solution = workspace.CurrentSolution; + + var client = (InProcRemoteHostClient)(await InProcRemoteHostClient.CreateAsync(workspace, CancellationToken.None)); + using (var session = await client.CreateServiceSessionAsync(WellKnownRemoteHostServices.RemoteHostService, solution, CancellationToken.None)) + { + await session.InvokeAsync(WellKnownRemoteHostServices.RemoteHostService_SynchronizeAsync); + } + + var storage = client.AssetStorage; + + var map = solution.GetAssetMap(); + + object data; + foreach (var kv in map) + { + Assert.True(storage.TryGetAsset(kv.Key, out data)); + } + } + } + + private static RemoteHostService CreateService() + { + var stream = new MemoryStream(); + return new RemoteHostService(stream, new InProcRemoteHostClient.ServiceProvider()); + } + } +} diff --git a/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs b/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs new file mode 100644 index 0000000000000000000000000000000000000000..b8d15e3d6722af521941b14a6ae0316cca040600 --- /dev/null +++ b/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs @@ -0,0 +1,66 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; +using Microsoft.CodeAnalysis.Remote; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Roslyn.VisualStudio.Next.UnitTests.Remote +{ + public class SolutionServiceTests + { + [Fact, Trait(Traits.Feature, Traits.Features.RemoteHost)] + public async Task TestCreation() + { + var code = @"class Test { void Method() { } }"; + + using (var workspace = await TestWorkspace.CreateCSharpAsync(code)) + { + var solution = workspace.CurrentSolution; + var solutionChecksum = await solution.State.GetChecksumAsync(CancellationToken.None); + + var map = solution.GetAssetMap(); + + var sessionId = 0; + var storage = new AssetStorage(enableCleanup: false); + var source = new MyAssetSource(storage, sessionId, map); + var service = new SolutionService(new AssetService(sessionId, storage)); + + var synched = await service.GetSolutionAsync(solutionChecksum, CancellationToken.None); + + Assert.Equal(solutionChecksum, await synched.State.GetChecksumAsync(CancellationToken.None)); + } + } + + private class MyAssetSource : AssetSource + { + private readonly Dictionary _map; + + public MyAssetSource(AssetStorage assetStorage, int sessionId, Dictionary map) : + base(assetStorage, sessionId) + { + _map = map; + } + + public override Task> RequestAssetsAsync(int serviceId, ISet checksums, CancellationToken cancellationToken) + { + var list = new List<(Checksum, object)>(); + + foreach (var checksum in checksums) + { + object data; + Assert.True(_map.TryGetValue(checksum, out data)); + + list.Add(ValueTuple.Create(checksum, data)); + } + + return Task.FromResult>(list); + } + } + } +} diff --git a/src/VisualStudio/Core/Test.Next/ServiceHub/CalculateDiagnosticsTests.cs b/src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs similarity index 75% rename from src/VisualStudio/Core/Test.Next/ServiceHub/CalculateDiagnosticsTests.cs rename to src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs index 52d65a6dba41e246d90f6dfa9ceff5cda8424e8a..e86f9ce2c606b1b0f56859140fc85398ded05a6f 100644 --- a/src/VisualStudio/Core/Test.Next/ServiceHub/CalculateDiagnosticsTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Reflection; @@ -12,12 +13,14 @@ using Microsoft.CodeAnalysis.CSharp.Diagnostics.TypeStyle; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; +using Microsoft.CodeAnalysis.Execution; using Microsoft.CodeAnalysis.Shared.Options; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.VisualBasic.UseNullPropagation; using Microsoft.CodeAnalysis.Workspaces.Diagnostics; using Microsoft.VisualStudio.LanguageServices.Implementation.Diagnostics; using Microsoft.VisualStudio.LanguageServices.Remote; +using Moq; using Roslyn.Test.Utilities; using Roslyn.Utilities; using Roslyn.VisualStudio.Next.UnitTests.Mocks; @@ -25,7 +28,7 @@ namespace Roslyn.VisualStudio.Next.UnitTests.Remote { - public class CalculateDiagnosticsTests + public class VisualStudioDiagnosticAnalyzerExecutorTests { [Fact, Trait(Traits.Feature, Traits.Features.RemoteHost)] public async Task TestCSharpAnalyzerOptions() @@ -119,6 +122,51 @@ public async Task TestCancellation() } } + [Fact, Trait(Traits.Feature, Traits.Features.RemoteHost)] + public async Task TestHostAnalyzers() + { + var code = @"class Test +{ + void Method() + { + var t = new Test(); + } +}"; + + using (var workspace = await CreateWorkspaceAsync(LanguageNames.CSharp, code)) + { + var analyzerType = typeof(CSharpUseExplicitTypeDiagnosticAnalyzer); + var analyzerReference = new AnalyzerFileReference(analyzerType.Assembly.Location, new TestAnalyzerAssemblyLoader()); + var mockAnalyzerService = CreateMockDiagnosticAnalyzerService(new[] { analyzerReference }); + + // add host analyzer as global assets + var snapshotService = workspace.Services.GetService(); + var assetBuilder = new CustomAssetBuilder(workspace); + + foreach (var reference in mockAnalyzerService.GetHostAnalyzerReferences()) + { + var asset = assetBuilder.Build(reference, CancellationToken.None); + snapshotService.AddGlobalAsset(reference, asset, CancellationToken.None); + } + + // set option + workspace.Options = workspace.Options.WithChangedOption(CSharpCodeStyleOptions.UseImplicitTypeWhereApparent, new CodeStyleOption(false, NotificationOption.Suggestion)); + + // run analysis + var project = workspace.CurrentSolution.Projects.First(); + + var executor = new VisualStudioDiagnosticAnalyzerExecutor(mockAnalyzerService, new MyUpdateSource(workspace)); + var analyzerDriver = (await project.GetCompilationAsync()).WithAnalyzers(analyzerReference.GetAnalyzers(project.Language).Where(a => a.GetType() == analyzerType).ToImmutableArray()); + var result = await executor.AnalyzeAsync(analyzerDriver, project, CancellationToken.None); + + var analyzerResult = result.AnalysisResult[analyzerDriver.Analyzers[0]]; + + // check result + var diagnostics = analyzerResult.SemanticLocals[analyzerResult.DocumentIds.First()]; + Assert.Equal(IDEDiagnosticIds.UseExplicitTypeDiagnosticId, diagnostics[0].Id); + } + } + private static async Task AnalyzeAsync(TestWorkspace workspace, ProjectId projectId, Type analyzerType, CancellationToken cancellationToken = default(CancellationToken)) { var diagnosticService = workspace.ExportProvider.GetExportedValue(); @@ -146,6 +194,13 @@ private async Task CreateWorkspaceAsync(string language, string c return workspace; } + private IDiagnosticAnalyzerService CreateMockDiagnosticAnalyzerService(IEnumerable references) + { + var mock = new Mock(MockBehavior.Strict); + mock.Setup(a => a.GetHostAnalyzerReferences()).Returns(references); + return mock.Object; + } + [DiagnosticAnalyzer(LanguageNames.CSharp)] private class MyAnalyzer : DiagnosticAnalyzer { diff --git a/src/VisualStudio/Core/Test.Next/VisualStudioTest.Next.csproj b/src/VisualStudio/Core/Test.Next/VisualStudioTest.Next.csproj index 6256a3d023966dc2edd22f01c4a6fc193c9b4d00..62b82ce47cae29d4d75c06a4499ae4cdcbd31f72 100644 --- a/src/VisualStudio/Core/Test.Next/VisualStudioTest.Next.csproj +++ b/src/VisualStudio/Core/Test.Next/VisualStudioTest.Next.csproj @@ -181,9 +181,7 @@ - - - + @@ -191,7 +189,12 @@ - + + + + + + \ No newline at end of file diff --git a/src/Workspaces/Core/Portable/Execution/AssetStorages.Storage.cs b/src/Workspaces/Core/Portable/Execution/AssetStorages.Storage.cs index 7c5efa1ad80c0c0ed85a85c0cd0dec6786c3c54c..7191e7e363b9c320f6a6e35adee9f3ec932cbe39 100644 --- a/src/Workspaces/Core/Portable/Execution/AssetStorages.Storage.cs +++ b/src/Workspaces/Core/Portable/Execution/AssetStorages.Storage.cs @@ -32,7 +32,7 @@ public Storage(AssetStorages owner, SolutionState solutionState) { SolutionState = solutionState; - _serializer = new Serializer(SolutionState.Workspace.Services); + _serializer = new Serializer(SolutionState.Workspace); } public SolutionState SolutionState { get; } diff --git a/src/Workspaces/Core/Portable/Execution/CustomAssetBuilder.cs b/src/Workspaces/Core/Portable/Execution/CustomAssetBuilder.cs index 7097107e74139816497c1e4f6c8d846667a8334e..cbf54fc1c0cfcd21ca7b047871785122f1657f3d 100644 --- a/src/Workspaces/Core/Portable/Execution/CustomAssetBuilder.cs +++ b/src/Workspaces/Core/Portable/Execution/CustomAssetBuilder.cs @@ -4,6 +4,7 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Serialization; +using Microsoft.CodeAnalysis.Host; namespace Microsoft.CodeAnalysis.Execution { @@ -14,9 +15,17 @@ internal class CustomAssetBuilder { private readonly Serializer _serializer; - public CustomAssetBuilder(Solution solution) + public CustomAssetBuilder(Solution solution) : this(solution.Workspace) { - _serializer = new Serializer(solution.Workspace.Services); + } + + public CustomAssetBuilder(Workspace workspace) : this(workspace.Services) + { + } + + public CustomAssetBuilder(HostWorkspaceServices services) + { + _serializer = new Serializer(services); } public CustomAsset Build(OptionSet options, string language, CancellationToken cancellationToken) diff --git a/src/Workspaces/Core/Portable/Execution/Serializer.cs b/src/Workspaces/Core/Portable/Execution/Serializer.cs index 5e13ce58b8c8fdd598c87922fc4d35f51b12464c..d8e2401f2fc7ed139174f9dc6a213276288c5a95 100644 --- a/src/Workspaces/Core/Portable/Execution/Serializer.cs +++ b/src/Workspaces/Core/Portable/Execution/Serializer.cs @@ -25,6 +25,14 @@ internal partial class Serializer private readonly IReferenceSerializationService _hostSerializationService; private readonly ConcurrentDictionary _lazyLanguageSerializationService; + public Serializer(Solution solution) : this(solution.Workspace) + { + } + + public Serializer(Workspace workspace) : this(workspace.Services) + { + } + public Serializer(HostWorkspaceServices workspaceServices) { _workspaceServices = workspaceServices; diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState_Checksum.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState_Checksum.cs index c32d9a7748801e6741fff1d1aa5d497f03719f6a..a7bddfa37f6f797bc310b7d749930aa4d947fd46 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState_Checksum.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState_Checksum.cs @@ -29,7 +29,7 @@ private async Task ComputeChecksumsAsync(CancellationToke var documentChecksumsTasks = DocumentIds.Select(id => DocumentStates[id].GetChecksumAsync(cancellationToken)); var additionalDocumentChecksumTasks = AdditionalDocumentIds.Select(id => AdditionalDocumentStates[id].GetChecksumAsync(cancellationToken)); - var serializer = new Serializer(_solutionServices.Workspace.Services); + var serializer = new Serializer(_solutionServices.Workspace); var infoChecksum = serializer.CreateChecksum(ProjectInfo.Attributes, cancellationToken); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState_Checksum.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState_Checksum.cs index 99434591f6b040d64f2f40cd47658ec4ee4bb480..b6b65b5071711c95e377ec1b0585019b6dd4be99 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState_Checksum.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState_Checksum.cs @@ -28,7 +28,7 @@ private async Task ComputeChecksumsAsync(CancellationTok // get states by id order to have deterministic checksum var projectChecksumTasks = ProjectIds.Select(id => ProjectStates[id].GetChecksumAsync(cancellationToken)); - var serializer = new Serializer(_solutionServices.Workspace.Services); + var serializer = new Serializer(_solutionServices.Workspace); var infoChecksum = serializer.CreateChecksum(SolutionInfo.Attributes, cancellationToken); var projectChecksums = await Task.WhenAll(projectChecksumTasks).ConfigureAwait(false); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/StateChecksums.cs b/src/Workspaces/Core/Portable/Workspace/Solution/StateChecksums.cs index 87cc6ccec5405ee0169922af623964c943d9702b..8e0943f2400d364dabce6383f2516ac9d2c15aca 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/StateChecksums.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/StateChecksums.cs @@ -265,6 +265,7 @@ public DocumentStateChecksums With(Checksum infoChecksum = null, Checksum textCh CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); + // verify input Contract.ThrowIfFalse(state.TryGetStateChecksums(out var stateChecksum)); Contract.ThrowIfFalse(this == stateChecksum); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState_Checksum.cs b/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState_Checksum.cs index adc63229e82c9c3fd189ef00e511d95f5adfca40..6ab09a501f35d6c18aa2382e06683ef391035251 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState_Checksum.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState_Checksum.cs @@ -27,7 +27,7 @@ private async Task ComputeChecksumsAsync(CancellationTok { var textTask = GetTextAsync(cancellationToken); - var serializer = new Serializer(solutionServices.Workspace.Services); + var serializer = new Serializer(solutionServices.Workspace); var infoChecksum = serializer.CreateChecksum(Info.Attributes, cancellationToken); var textChecksum = serializer.CreateChecksum(await textTask.ConfigureAwait(false), cancellationToken); diff --git a/src/Workspaces/CoreTest/Execution/SnapshotSerializationTests.cs b/src/Workspaces/CoreTest/Execution/SnapshotSerializationTests.cs index 3de814b29e1ebf4846c8c6f70a87cd38cbeaea54..59568f028e62d9733607511f24d50a1c870319e7 100644 --- a/src/Workspaces/CoreTest/Execution/SnapshotSerializationTests.cs +++ b/src/Workspaces/CoreTest/Execution/SnapshotSerializationTests.cs @@ -232,7 +232,7 @@ public async Task MetadataReference_RoundTrip_Test() var workspace = new AdhocWorkspace(hostServices); var reference = MetadataReference.CreateFromFile(typeof(object).Assembly.Location); - var serializer = new Serializer(workspace.Services); + var serializer = new Serializer(workspace); var assetFromFile = SolutionAsset.Create(serializer.CreateChecksum(reference, CancellationToken.None), reference, serializer); var assetFromStorage = await CloneAssetAsync(serializer, assetFromFile).ConfigureAwait(false); @@ -347,7 +347,7 @@ public async Task OptionSet_Serialization_CustomValue() public async Task Missing_Metadata_Serailization_Test() { var workspace = new AdhocWorkspace(); - var serializer = new Serializer(workspace.Services); + var serializer = new Serializer(workspace); var reference = new MissingMetadataReference(); @@ -361,7 +361,7 @@ public async Task Missing_Metadata_Serailization_Test() public async Task Missing_Analyzer_Serailization_Test() { var workspace = new AdhocWorkspace(); - var serializer = new Serializer(workspace.Services); + var serializer = new Serializer(workspace); var reference = new AnalyzerFileReference("missing_reference", new MissingAnalyzerLoader()); @@ -378,7 +378,7 @@ public async Task Missing_Analyzer_Serailization_Desktop_Test() MefHostServices.DefaultAssemblies.Add(typeof(Host.TemporaryStorageServiceFactory.TemporaryStorageService).Assembly)); var workspace = new AdhocWorkspace(hostServices); - var serializer = new Serializer(workspace.Services); + var serializer = new Serializer(workspace); var reference = new AnalyzerFileReference("missing_reference", new MissingAnalyzerLoader()); @@ -429,7 +429,7 @@ public async Task UnknownLanguageTest() public async Task EmptyAssetChecksumTest() { var document = new AdhocWorkspace().CurrentSolution.AddProject("empty", "empty", LanguageNames.CSharp).AddDocument("empty", SourceText.From("")); - var serializer = new Serializer(document.Project.Solution.Workspace.Services); + var serializer = new Serializer(document.Project.Solution); var source = serializer.CreateChecksum(await document.GetTextAsync().ConfigureAwait(false), CancellationToken.None); var metadata = serializer.CreateChecksum(new MissingMetadataReference(), CancellationToken.None); @@ -454,8 +454,8 @@ public async Task VBParseOptionsInCompilationOptions() private static async Task VerifyOptionSetsAsync(Workspace workspace, string language) { - var assetBuilder = new CustomAssetBuilder(workspace.CurrentSolution); - var serializer = new Serializer(workspace.Services); + var assetBuilder = new CustomAssetBuilder(workspace); + var serializer = new Serializer(workspace); var asset = assetBuilder.Build(workspace.Options, language, CancellationToken.None); diff --git a/src/Workspaces/Remote/Core/Services/AssetService.cs b/src/Workspaces/Remote/Core/Services/AssetService.cs index 43b95460216d5572c340cc5c3732321da9237498..5888965b193471f39ffc6935a08c9cee5235ff42 100644 --- a/src/Workspaces/Remote/Core/Services/AssetService.cs +++ b/src/Workspaces/Remote/Core/Services/AssetService.cs @@ -21,7 +21,7 @@ namespace Microsoft.CodeAnalysis.Remote internal class AssetService { // PREVIEW: unfortunately, I need dummy workspace since workspace services can be workspace specific - private static readonly Serializer s_serializer = new Serializer(new AdhocWorkspace(RoslynServices.HostServices, workspaceKind: "dummy").Services); + private static readonly Serializer s_serializer = new Serializer(new AdhocWorkspace(RoslynServices.HostServices, workspaceKind: "dummy")); private readonly int _sessionId; private readonly AssetStorage _assetStorage; diff --git a/src/Workspaces/Remote/Core/Services/AssetStorage.cs b/src/Workspaces/Remote/Core/Services/AssetStorage.cs index 351fcd3b8ebe8637b7c95541440cf6ab5e975398..3898c4d5ee3e428ddd5695a0d7800072c0d68bcc 100644 --- a/src/Workspaces/Remote/Core/Services/AssetStorage.cs +++ b/src/Workspaces/Remote/Core/Services/AssetStorage.cs @@ -16,7 +16,7 @@ namespace Microsoft.CodeAnalysis.Remote /// internal class AssetStorage { - public static readonly AssetStorage Default = new AssetStorage(); + public static readonly AssetStorage Default = new AssetStorage(enableCleanup: true); private const int CleanupInterval = 3; // 3 minutes private const int PurgeAfter = 30; // 30 minutes @@ -30,9 +30,12 @@ internal class AssetStorage private readonly ConcurrentDictionary _assets = new ConcurrentDictionary(concurrencyLevel: 4, capacity: 10); - public AssetStorage() + public AssetStorage(bool enableCleanup) { - Task.Run(CleanAssetsAsync, CancellationToken.None); + if (enableCleanup) + { + Task.Run(CleanAssetsAsync, CancellationToken.None); + } } public AssetSource TryGetAssetSource(int sessionId) diff --git a/src/Workspaces/Remote/ServiceHub/ServiceHub.csproj b/src/Workspaces/Remote/ServiceHub/ServiceHub.csproj index 5091a64132cf8b076eade33a21d8a56dcfc9114d..493fb4d856754f46e7eeea4f404a67f82a16999e 100644 --- a/src/Workspaces/Remote/ServiceHub/ServiceHub.csproj +++ b/src/Workspaces/Remote/ServiceHub/ServiceHub.csproj @@ -78,8 +78,6 @@ - - - + \ No newline at end of file