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

ConfiguredProject-level service for determining whether a project is implicitly active #3060

Merged
merged 21 commits into from
Dec 20, 2017
Merged
Show file tree
Hide file tree
Changes from 18 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
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ public static ConfiguredProject Create(IProjectCapabilitiesScope capabilities =
return mock.Object;
}

public static ConfiguredProject ImplementProjectConfiguration(string configuration)
{
return ImplementProjectConfiguration(ProjectConfigurationFactory.Create(configuration));
}

public static ConfiguredProject ImplementProjectConfiguration(ProjectConfiguration projectConfiguration)
{
return Create(projectConfiguration: projectConfiguration);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@

using System.Linq;
using System.Collections.Generic;
using System.Collections.Immutable;

namespace Microsoft.VisualStudio.ProjectSystem
{
internal static class IConfigurationGroupFactory
{
public static IConfigurationGroup<ProjectConfiguration> CreateFromConfigurationNames(params string[] configurationNames)
{
IEnumerable<StandardProjectConfiguration> configurations = configurationNames.Select(name => new StandardProjectConfiguration(name, ImmutableDictionary<string, string>.Empty));
IEnumerable<ProjectConfiguration> configurations = configurationNames.Select(name => ProjectConfigurationFactory.Create(name));

return Create(configurations);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,17 @@ namespace Microsoft.VisualStudio.ProjectSystem
{
internal static class IProjectAsynchronousTasksServiceFactory
{
public static IProjectAsynchronousTasksService Create(CancellationToken cancelToken)
public static IProjectAsynchronousTasksService Create()
{
return ImplementUnloadCancellationToken(CancellationToken.None);
}

public static IProjectAsynchronousTasksService ImplementUnloadCancellationToken(CancellationToken cancellationToken)
{
var mock = new Mock<IProjectAsynchronousTasksService>();

mock.Setup(s => s.UnloadCancellationToken).Returns(cancelToken);
mock.Setup(s => s.UnloadCancellationToken)
.Returns(cancellationToken);

return mock.Object;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ namespace Microsoft.VisualStudio.ProjectSystem
{
internal static class ProjectConfigurationFactory
{


public static ProjectConfiguration Create(string name, IImmutableDictionary<string, string> dimensions)
{
return new StandardProjectConfiguration(name, dimensions);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace Microsoft.VisualStudio.ProjectSystem
{
internal class ProjectValueDataSource<T> : ProjectValueDataSourceBase<T>
internal class ProjectValueDataSource<T> : ProjectValueDataSourceBase<T>
where T : class
{
private BroadcastBlock<IProjectVersionedValue<T>> _broadcastBlock;
Expand Down Expand Up @@ -44,12 +44,7 @@ protected override void Initialize()

public async Task SendAndCompleteAsync(T value, ITargetBlock<IProjectVersionedValue<IConfigurationGroup<ProjectConfiguration>>> targetBlock)
{
EnsureInitialized(true);

_version++;
await _broadcastBlock.SendAsync(new ProjectVersionedValue<T>(
value,
ImmutableDictionary<NamedIdentity, IComparable>.Empty.Add(DataSourceKey, _version)));
await SendAsync(value);

_broadcastBlock.Complete();

Expand All @@ -58,5 +53,15 @@ await _broadcastBlock.SendAsync(new ProjectVersionedValue<T>(
// block has finished.
await Task.WhenAll(_broadcastBlock.Completion, targetBlock.Completion);
}

public async Task SendAsync(T value)
{
EnsureInitialized(true);

_version++;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this going to be called from multiple threads?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this is test code.

await _broadcastBlock.SendAsync(new ProjectVersionedValue<T>(
value,
ImmutableDictionary<NamedIdentity, IComparable>.Empty.Add(DataSourceKey, _version)));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
// 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.Threading;
using System.Threading.Tasks;
using Xunit;

namespace Microsoft.VisualStudio.ProjectSystem
{
public class ConfiguredProjectImplicitActivationTrackingTests
{
[Fact]
public async Task IsImplicitlyActive_WhenDisposed_ThrowsObjectDisposed()
{
var service = CreateInstance();
await service.DisposeAsync();

Assert.Throws<ObjectDisposedException>(() =>
{
var ignored = service.IsImplicitlyActive;
});
}

[Fact]
public async Task IsImplicitlyActiveTask_WhenDisposed_ThrowsObjectDisposed()
{
var service = CreateInstance();
await service.DisposeAsync();

Assert.Throws<ObjectDisposedException>(() =>
{
var ignored = service.IsImplicitlyActiveTask;
});
}

[Fact]
public void IsImplicitlyActive_WhenActiveConfigurationHasNotFired_ReturnsFalse()
{
var service = CreateInstance();

var result = service.IsImplicitlyActive;

Assert.False(result);
}

[Fact]
public void IsImplicitlyActiveTask_WhenActiveConfiguredHasNotFired_ReturnsNonCompletedTask()
{
var service = CreateInstance();

var result = service.IsImplicitlyActiveTask;

Assert.False(result.IsCompleted);
}

[Fact]
public void IsImplicitlyActive_WhenProjectHasUnloaded_ReturnsFalse()
{
var cancellationToken = new CancellationToken(canceled: true);
var tasksService = IProjectAsynchronousTasksServiceFactory.ImplementUnloadCancellationToken(cancellationToken);

var service = CreateInstance(tasksService);

var result = service.IsImplicitlyActive;

Assert.False(result);
}

[Fact]
public void IsImplicitlyActiveTask_WhenProjectHasUnloaded_ReturnsCanceledTask()
{
var cancellationToken = new CancellationToken(canceled: true);
var tasksService = IProjectAsynchronousTasksServiceFactory.ImplementUnloadCancellationToken(cancellationToken);

var service = CreateInstance(tasksService);

var result = service.IsImplicitlyActiveTask;

Assert.True(result.IsCanceled);
}

[Fact]
public void IsImplicitlyActive_WhenProjectUnloadCancellationTokenSourceHasBeenDisposed_ReturnsFalse()
{
var cancellationTokenSource = new CancellationTokenSource();
var tasksService = IProjectAsynchronousTasksServiceFactory.ImplementUnloadCancellationToken(cancellationTokenSource.Token);
cancellationTokenSource.Dispose();

var service = CreateInstance(tasksService);

var result = service.IsImplicitlyActive;

Assert.False(result);
}

[Fact]
public void IsImplicitlyActiveTask_WhenProjectUnloadCancellationTokenSourceHasBeenDisposed_ReturnsNonCompletedTask()
{
var cancellationTokenSource = new CancellationTokenSource();
var tasksService = IProjectAsynchronousTasksServiceFactory.ImplementUnloadCancellationToken(cancellationTokenSource.Token);
cancellationTokenSource.Dispose();

var service = CreateInstance(tasksService);

var result = service.IsImplicitlyActiveTask;

Assert.False(result.IsCompleted);
}

[Fact]
public void IsImplicitlyActive_WhenProjectUnloadCancellationTokenSourceHasBeenCanceledAndDisposed_ReturnsFalse()
{
var cancellationTokenSource = new CancellationTokenSource();
var tasksService = IProjectAsynchronousTasksServiceFactory.ImplementUnloadCancellationToken(cancellationTokenSource.Token);
cancellationTokenSource.Cancel();
cancellationTokenSource.Dispose();

var service = CreateInstance(tasksService);

var result = service.IsImplicitlyActive;

Assert.False(result);
}

[Fact]
public void IsImplicitlyActiveTask_WhenProjectUnloadCancellationTokenSourceHasBeenCanceledAndDisposed_ReturnsCanceledTask()
{
var cancellationTokenSource = new CancellationTokenSource();
var tasksService = IProjectAsynchronousTasksServiceFactory.ImplementUnloadCancellationToken(cancellationTokenSource.Token);
cancellationTokenSource.Cancel();
cancellationTokenSource.Dispose();

var service = CreateInstance(tasksService);

var result = service.IsImplicitlyActiveTask;

Assert.True(result.IsCanceled);
}

[Fact]
public async Task Dispose_WhenNotInitialized_DoesNotThrow()
{
var service = CreateInstance();
await service.DisposeAsync();

Assert.True(service.IsDisposed);
}

[Theory] // Active configs Current
[InlineData(new object[] { new[] { "Debug|x86" }, "Debug|x86" })]
[InlineData(new object[] { new[] { "Debug|x86", "Release|x86" }, "Debug|x86" })]
[InlineData(new object[] { new[] { "Debug|x86", "Release|x86", "Release|AnyCPU" }, "Debug|x86" })]
[InlineData(new object[] { new[] { "Release|x86", "Debug|x86" }, "Debug|x86" })]
[InlineData(new object[] { new[] { "Release|x86", "Release|AnyCPU", "Debug|x86" }, "Debug|x86" })]
[InlineData(new object[] { new[] { "Debug|x86|net46" }, "Debug|x86|net46" })]
[InlineData(new object[] { new[] { "Debug|x86|net46", "Release|x86|net46" }, "Debug|x86|net46" })]
[InlineData(new object[] { new[] { "Debug|x86|net46", "Release|x86|net46", "Release|AnyCPU|net46" }, "Debug|x86|net46" })]
public async Task IsImplicitlyActive_WhenActionConfigurationChangesAndMatches_ReturnsTrue(string[] configurations, string currentConfiguration)
{
var project = ConfiguredProjectFactory.ImplementProjectConfiguration(currentConfiguration);
var service = CreateInstance(project, out ProjectValueDataSource<IConfigurationGroup<ProjectConfiguration>> source);

Assert.False(service.IsImplicitlyActive); // Just to init

var configurationGroups = IConfigurationGroupFactory.CreateFromConfigurationNames(configurations);
await source.SendAndCompleteAsync(configurationGroups, service.TargetBlock);

Assert.True(service.IsImplicitlyActive);
}

[Theory] // Active configs Current
[InlineData(new object[] { new[] { "Debug|x86" }, "Debug|x86" })]
[InlineData(new object[] { new[] { "Debug|x86", "Release|x86" }, "Debug|x86" })]
[InlineData(new object[] { new[] { "Debug|x86", "Release|x86", "Release|AnyCPU" }, "Debug|x86" })]
[InlineData(new object[] { new[] { "Release|x86", "Debug|x86" }, "Debug|x86" })]
[InlineData(new object[] { new[] { "Release|x86", "Release|AnyCPU", "Debug|x86" }, "Debug|x86" })]
[InlineData(new object[] { new[] { "Debug|x86|net46" }, "Debug|x86|net46" })]
[InlineData(new object[] { new[] { "Debug|x86|net46", "Release|x86|net46" }, "Debug|x86|net46" })]
[InlineData(new object[] { new[] { "Debug|x86|net46", "Release|x86|net46", "Release|AnyCPU|net46" }, "Debug|x86|net46" })]
public async Task IsImplicitlyActiveTask_WhenActionConfigurationChangesAndMatches_ReturnsCompletedTask(string[] configurations, string currentConfiguration)
{
var project = ConfiguredProjectFactory.ImplementProjectConfiguration(currentConfiguration);
var service = CreateInstance(project, out ProjectValueDataSource<IConfigurationGroup<ProjectConfiguration>> source);

var result = service.IsImplicitlyActiveTask;

var configurationGroups = IConfigurationGroupFactory.CreateFromConfigurationNames(configurations);
await source.SendAndCompleteAsync(configurationGroups, service.TargetBlock);

Assert.True(result.IsCompleted);
}

[Fact]
public async Task IsImplicitlyActiveTask_CompletedStateChangesOverLifetime()
{
var project = ConfiguredProjectFactory.ImplementProjectConfiguration("Debug|AnyCPU");
var service = CreateInstance(project, out ProjectValueDataSource<IConfigurationGroup<ProjectConfiguration>> source);

Assert.False(service.IsImplicitlyActive);

// Should now be considered active
var configurationGroups = IConfigurationGroupFactory.CreateFromConfigurationNames("Debug|AnyCPU");
await source.SendAsync(configurationGroups);

Assert.True(service.IsImplicitlyActiveTask.Wait(500));

configurationGroups = IConfigurationGroupFactory.CreateFromConfigurationNames("Debug|x86");
await source.SendAndCompleteAsync(configurationGroups, service.TargetBlock);

// Should now be considered in-active
Assert.False(service.IsImplicitlyActiveTask.IsCompleted);
}

private static ConfiguredProjectImplicitActivationTracking CreateInstance()
{
return CreateInstance(null, null, out _);
}

private static ConfiguredProjectImplicitActivationTracking CreateInstance(ConfiguredProject project, out ProjectValueDataSource<IConfigurationGroup<ProjectConfiguration>> source)
{
return CreateInstance(project, null, out source);
}

private static ConfiguredProjectImplicitActivationTracking CreateInstance(IProjectAsynchronousTasksService tasksService)
{
return CreateInstance(null, tasksService, out _);
}

private static ConfiguredProjectImplicitActivationTracking CreateInstance(ConfiguredProject project, IProjectAsynchronousTasksService tasksService, out ProjectValueDataSource<IConfigurationGroup<ProjectConfiguration>> source)
{
project = project ?? ConfiguredProjectFactory.Create();
var services = IProjectCommonServicesFactory.CreateWithDefaultThreadingPolicy();
source = ProjectValueDataSourceFactory.Create<IConfigurationGroup<ProjectConfiguration>>(services);
var activeConfigurationGroupService = IActiveConfigurationGroupServiceFactory.Implement(source);

tasksService= tasksService ?? IProjectAsynchronousTasksServiceFactory.Create();

return new ConfiguredProjectImplicitActivationTracking(project, activeConfigurationGroupService, tasksService);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ internal LaunchSettingsUnderTest GetLaunchSettingsProvider(IFileSystem fileSyste
var unconfiguredProject = UnconfiguredProjectFactory.Create(null, null, @"c:\test\Project1\Project1.csproj");
var properties = ProjectPropertiesFactory.Create(unconfiguredProject, new[] { debuggerData });
var commonServices = IUnconfiguredProjectCommonServicesFactory.Create(unconfiguredProject, null, new IProjectThreadingServiceMock(), null, properties);
var projectServices = IUnconfiguredProjectServicesFactory.Create(IProjectAsynchronousTasksServiceFactory.Create(CancellationToken.None));
var projectServices = IUnconfiguredProjectServicesFactory.Create(IProjectAsynchronousTasksServiceFactory.Create());
var provider = new LaunchSettingsUnderTest(unconfiguredProject, projectServices, fileSystem ?? new IFileSystemMock(), commonServices, null, specialFilesManager);
return provider;
}
Expand Down
Loading