Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Implement TempPEBuildManager using UnconfiguredProjectHostBridge #4318

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions build/Packages.targets
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,8 @@
<PackageReference Update="Microsoft.Test.Apex.VisualStudio" Version="15.8.27703" />

<!-- CPS -->
<PackageReference Update="Microsoft.VisualStudio.ProjectSystem.SDK" Version="16.0.201-pre-g7d366164d0" />
<PackageReference Update="Microsoft.VisualStudio.ProjectSystem.Analyzers" Version="16.0.201-pre-g7d366164d0" />
<PackageReference Update="Microsoft.VisualStudio.ProjectSystem.SDK" Version="16.0.429-pre-gf4f06355f6" />
<PackageReference Update="Microsoft.VisualStudio.ProjectSystem.Analyzers" Version="16.0.429-pre-gf4f06355f6" />

<!-- Roslyn -->
<PackageReference Update="Microsoft.VisualStudio.LanguageServices" Version="2.11.0-beta1-63430-03" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public static async Task TimeoutAfter(this Task task, TimeSpan timeout)
{
using (var cts = new CancellationTokenSource())
{
Task timeoutTask = Task.Delay(timeout, cts.Token);
var timeoutTask = Task.Delay(timeout, cts.Token);
Task completedTask = await Task.WhenAny(task, timeoutTask);

if (timeoutTask == completedTask)
Expand All @@ -23,7 +23,7 @@ public static async Task<TResult> TimeoutAfter<TResult>(this Task<TResult> task,
{
using (var cts = new CancellationTokenSource())
{
Task timeoutTask = Task.Delay(timeout, cts.Token);
var timeoutTask = Task.Delay(timeout, cts.Token);
Task completedTask = await Task.WhenAny(task, timeoutTask);

if (timeoutTask == completedTask)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Microsoft.Build.Construction;
using Microsoft.Build.Execution;
using Microsoft.VisualStudio.ProjectSystem;

using Moq;

namespace Microsoft.VisualStudio
{
internal static class IProjectSnapshotFactory
{
internal static IProjectSnapshot FromProjectXml(string xmlString)
{
var project = new ProjectInstance(ProjectRootElementFactory.Create(xmlString));

var mock = new Mock<IProjectSnapshot>();
mock.Setup(p => p.ProjectInstance).Returns(project);
return mock.Object;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,277 @@
using System;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

using Microsoft.VisualStudio.ProjectSystem.LanguageServices;
using Microsoft.VisualStudio.ProjectSystem.VS.TempPE;

using Xunit;

namespace Microsoft.VisualStudio.ProjectSystem.TempPE
{
public class TempPEBuildManagerTests
{
[Fact]
public async Task Preprocess_NoDesignTimeInput_ReturnsEmptyCollections()
{
// Initial state is an empty object
var mgr = new TestTempPEBuildManager();

// Apply our update
var update = IProjectSubscriptionUpdateFactory.FromJson(@"{
""ProjectChanges"": {
""Compile"": {
""Difference"": {
""AnyChanges"": true,
""AddedItems"" : [ ""Form1.cs"" ]
}
}
}
}");
var snapshot = IProjectSnapshotFactory.FromProjectXml(@"<Project>
<ItemGroup>
<Compile Include=""Form1.cs"" />
</ItemGroup>
</Project>");
var result = await mgr.TestProcessAsync(IProjectVersionedValueFactory.Create(Tuple.Create(snapshot, update)));

// Should have empty collections
Assert.Empty(result.Inputs);
Assert.Empty(result.SharedInputs);
}

[Fact]
public async Task Preprocess_SharedDesignTimeInput_ReturnsOneSharedInput()
{
// Initial state is an empty object
var mgr = new TestTempPEBuildManager();

// Apply our update
var update = IProjectSubscriptionUpdateFactory.FromJson(@"{
""ProjectChanges"": {
""Compile"": {
""Difference"": {
""AnyChanges"": true,
""AddedItems"" : [ ""Settings.Designer.cs"" ]
}
}
}
}");
var snapshot = IProjectSnapshotFactory.FromProjectXml(@"<Project>
<ItemGroup>
<Compile Include=""Settings.Designer.cs"">
<DesignTimeSharedInput>true</DesignTimeSharedInput>
</Compile>
</ItemGroup>
</Project>");
var result = await mgr.TestProcessAsync(IProjectVersionedValueFactory.Create(Tuple.Create(snapshot, update)));

// Should have empty collections
Assert.Empty(result.Inputs);
Assert.Single(result.SharedInputs);
Assert.Equal("Settings.Designer.cs", result.SharedInputs.First());
}

[Fact]
public async Task Preprocess_OneDesignTimeInput_ReturnsOneInput()
{
// Initial state is an empty object
var mgr = new TestTempPEBuildManager();

// Apply our update
var update = IProjectSubscriptionUpdateFactory.FromJson(@"{
""ProjectChanges"": {
""Compile"": {
""Difference"": {
""AnyChanges"": true,
""AddedItems"" : [
""Form1.cs"",
""Resources1.Designer.cs""
]
}
}
}
}");
var snapshot = IProjectSnapshotFactory.FromProjectXml(@"<Project>
<ItemGroup>
<Compile Include=""Form1.cs"" />
<Compile Include=""Resources1.Designer.cs"">
<DesignTime>true</DesignTime>
</Compile>
</ItemGroup>
</Project>");
var result = await mgr.TestProcessAsync(IProjectVersionedValueFactory.Create(Tuple.Create(snapshot, update)));

// One file should have been added
Assert.Single(result.Inputs);
Assert.Empty(result.SharedInputs);
Assert.Equal("Resources1.Designer.cs", result.Inputs.First().Key);
}


[Fact]
public async Task Preprocess_OneDesignTimeInputAndOneShared_ReturnsOneInputEach()
{
// Initial state is an empty object
var mgr = new TestTempPEBuildManager();

// Apply our update
var update = IProjectSubscriptionUpdateFactory.FromJson(@"{
""ProjectChanges"": {
""Compile"": {
""Difference"": {
""AnyChanges"": true,
""AddedItems"" : [
""Form1.cs"",
""Resources1.Designer.cs"",
""Settings.Designer.cs""
]
}
}
}
}");
var snapshot = IProjectSnapshotFactory.FromProjectXml(@"<Project>
<ItemGroup>
<Compile Include=""Form1.cs"" />
<Compile Include=""Resources1.Designer.cs"">
<DesignTime>true</DesignTime>
</Compile>
<Compile Include=""Settings.Designer.cs"">
<DesignTimeSharedInput>true</DesignTimeSharedInput>
</Compile>
</ItemGroup>
</Project>");
var result = await mgr.TestProcessAsync(IProjectVersionedValueFactory.Create(Tuple.Create(snapshot, update)));

// One file should have been added
Assert.Single(result.Inputs);
Assert.Single(result.SharedInputs);
Assert.Equal("Resources1.Designer.cs", result.Inputs.First().Key);
Assert.Equal("Settings.Designer.cs", result.SharedInputs.First());
}

[Fact]
public async Task Preprocess_AddDesignTimeInput_ReturnsCorrectInputs()
{
// Initial state is a single design time input called Resources1.Designer.cs
var baseDirectory = Directory.GetCurrentDirectory();
var mgr = new TestTempPEBuildManager();
await mgr.SetInputs(baseDirectory, new[] { "Resources1.Designer.cs" }, null);

// Apply our update
var update = IProjectSubscriptionUpdateFactory.FromJson(@"{
""ProjectChanges"": {
""Compile"": {
""Difference"": {
""AnyChanges"": true,
""AddedItems"" : [ ""Resources2.Designer.cs"" ]
}
}
}
}");
var projectState = IProjectSnapshotFactory.FromProjectXml(@"<Project>
<ItemGroup>
<Compile Include=""Form1.cs"" />
<Compile Include=""Resources1.Designer.cs"">
<DesignTime>true</DesignTime>
</Compile>
<Compile Include=""Resources2.Designer.cs"">
<DesignTime>true</DesignTime>
</Compile>
</ItemGroup>
</Project>");
var result = await mgr.TestProcessAsync(IProjectVersionedValueFactory.Create(Tuple.Create(projectState, update)));

// Should be two design time files now
Assert.Equal(2, result.Inputs.Count);
Assert.Empty(result.SharedInputs);
Assert.Contains("Resources1.Designer.cs", result.Inputs.Keys);
Assert.Contains("Resources2.Designer.cs", result.Inputs.Keys);
}

[Fact]
public async Task Preprocess_RemoveDesignTimeInput_ReturnsCorrectInput()
{
// Initial state is two design time inputs
var baseDirectory = Directory.GetCurrentDirectory();
var mgr = new TestTempPEBuildManager();
await mgr.SetInputs(baseDirectory,
new[] {
"Resources1.Designer.cs",
"Resources2.Designer.cs"
},
null);

// Apply our update
var update = IProjectSubscriptionUpdateFactory.FromJson(@"{
""ProjectChanges"": {
""Compile"": {
""Difference"": {
""AnyChanges"": true,
""RemovedItems"" : [ ""Resources2.Designer.cs"" ]
}
}
}
}");
var projectState = IProjectSnapshotFactory.FromProjectXml(@"<Project>
<ItemGroup>
<Compile Include=""Form1.cs"" />
<Compile Include=""Resources1.Designer.cs"">
<DesignTime>true</DesignTime>
</Compile>
</ItemGroup>
</Project>");
var result = await mgr.TestProcessAsync(IProjectVersionedValueFactory.Create(Tuple.Create(projectState, update)));

// One file should have been removed
Assert.Single(result.Inputs);
Assert.Empty(result.SharedInputs);
Assert.Equal("Resources1.Designer.cs", result.Inputs.First().Key);
}

internal class TestTempPEBuildManager : TempPEBuildManager
{
private bool _initialized;

public TestTempPEBuildManager()
: base(IProjectThreadingServiceFactory.Create(), IUnconfiguredProjectCommonServicesFactory.Create(), ILanguageServiceHostFactory.Create())
{
}
private async Task InitializeAsync()
{
if (!_initialized)
{
// Set up the default applied value
await InitializeInnerCoreAsync(CancellationToken.None);
_initialized = true;
}
}

public async Task<DesignTimeInputsItem> TestProcessAsync(IProjectVersionedValue<Tuple<IProjectSnapshot, IProjectSubscriptionUpdate>> input)
{
await InitializeAsync();

var result = await base.PreprocessAsync(input, null);
await base.ApplyAsync(result);
return AppliedValue.Value;
}


public async Task SetInputs(string baseDirectory, string[] designTimeInputs, string[] sharedDesignTimeInputs)
{
await InitializeAsync();

designTimeInputs = designTimeInputs ?? Array.Empty<string>();
sharedDesignTimeInputs = sharedDesignTimeInputs ?? Array.Empty<string>();
await base.ApplyAsync(new DesignTimeInputsDelta
{
AddedItems = ImmutableList.CreateRange<string>(designTimeInputs),
AddedSharedItems = ImmutableHashSet.CreateRange<string>(StringComparers.Paths, sharedDesignTimeInputs)
});
}
}
}
}
Loading