-
Notifications
You must be signed in to change notification settings - Fork 391
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
Changes from 18 commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
62e1eae
Add extension for CancellationToken.Register
davkean 02d25b7
Add IActiveConfiguredProjectService
davkean 4a1f3c5
Extract activation/deactivation into methods
davkean aa70d07
Rename 'currentlyActive' to 'nowActive'
davkean 0f4145d
Remove whitespace
davkean d281a78
Remove unneeded comment
davkean 21c271c
Handle cancelation race
davkean faa73be
Add doc comment to IActiveConfiguredProjectService
davkean de10515
Reorder parameters to be consistent
davkean 04ddb7f
On project unload, IsActive should return false.
davkean b3b0529
Ignore changes to active configuration if project has unloaded
davkean 7827263
Pull out cancellation into separate method
davkean 579c0c0
Apply [ImportingConstructor]
davkean 2ed7fd5
Fix <see cref/> comment
davkean b9b7a7b
Rename RegisterOptions value
davkean f4b01eb
Add unit tests for ActiveConfiguredProjectService
davkean 0924852
Rename service to match CPS equivalent
davkean cf65d66
Cancelled -> Canceled
davkean fa0ec84
Comment MemoryBarrier call
davkean d114a2a
Fix grammer of doc comment
davkean 2129319
Add XML docs for class
davkean File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
241 changes: 241 additions & 0 deletions
241
...ystem.Managed.UnitTests/ProjectSystem/ConfiguredProjectImplicitActivationTrackingTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.