Skip to content

Commit

Permalink
Add tests to check if SonarQubeNavigationItem is free-threaded
Browse files Browse the repository at this point in the history
Part of #4856
  • Loading branch information
duncanp-sonar committed Sep 12, 2023
1 parent 1dcbd05 commit ab52595
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@

using System;
using FluentAssertions;
using Microsoft.TeamFoundation.Controls;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SonarLint.VisualStudio.Integration.Resources;
using SonarLint.VisualStudio.Integration.TeamExplorer;
using SonarLint.VisualStudio.TestInfrastructure;
Expand All @@ -30,29 +32,49 @@ namespace SonarLint.VisualStudio.Integration.UnitTests.TeamExplorer
[TestClass]
public class SonarQubeNavigationItemTests
{
[TestMethod]
[Ignore] // Object is created successfully, but an exception when
// the MEF composition contained is disposed causing the test to fail
public void MefCtor_CheckIsExported()
=> MefTestHelpers.CheckTypeCanBeImported<SonarQubeNavigationItem, ITeamExplorerNavigationItem>(
MefTestHelpers.CreateExport<ITeamExplorerController>());

[TestMethod]
public void CheckIsNonSharedMefComponent()
=> MefTestHelpers.CheckIsNonSharedMefComponent<SonarQubeNavigationItem>();

[TestMethod]
public void MefCtor_DoesNotCallAnyServices()
{
var controller = new Mock<ITeamExplorerController>();

_ = new SonarQubeNavigationItem(controller.Object);

// The MEF constructor should be free-threaded, which it will be if
// it doesn't make any external calls.
controller.Invocations.Should().BeEmpty();
}

[TestMethod]
public void SonarQubeNavigationItem_Execute()
{
// Arrange
var controller = new ConfigurableTeamExplorerController();
var controller = new Mock<ITeamExplorerController>();

var testSubject = new SonarQubeNavigationItem(controller);
var testSubject = CreateTestSubject(controller.Object);

// Act
testSubject.Execute();

// Assert
controller.ShowConnectionsPageCallsCount.Should().Be(1);
controller.Verify(x => x.ShowSonarQubePage(), Times.Once);
}

[TestMethod]
public void SonarQubeNavigationItem_Ctor()
{
// Arrange
var controller = new ConfigurableTeamExplorerController();

// Act
var testSubject = new SonarQubeNavigationItem(controller);
// Arrange & Act
var testSubject = CreateTestSubject();

// Assert
testSubject.IsVisible.Should().BeTrue("Nav item should be visible");
Expand All @@ -67,5 +89,8 @@ public void SonarQubeNavigationItem_Ctor_NullArgChecks()
{
Exceptions.Expect<ArgumentNullException>(() => new SonarQubeNavigationItem(null));
}

private static SonarQubeNavigationItem CreateTestSubject(ITeamExplorerController controller = null)
=> new SonarQubeNavigationItem(controller ?? Mock.Of<ITeamExplorerController>());
}
}
7 changes: 7 additions & 0 deletions src/Integration.TeamExplorer/SonarQubeNavigationItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
namespace SonarLint.VisualStudio.Integration.TeamExplorer
{
[TeamExplorerNavigationItem(SonarQubeNavigationItem.ItemId, SonarQubeNavigationItem.Priority, TargetPageId = SonarQubePage.PageId)]
[PartCreationPolicy(CreationPolicy.NonShared)] // The VS navigations in MS.TeamFoundation.TeamExplorer.Navigation are non-shared
internal class SonarQubeNavigationItem : TeamExplorerNavigationItemBase
{
public const string ItemId = "172AF455-5F42-46FC-BFE6-23227A05806B";
Expand All @@ -49,6 +50,12 @@ internal SonarQubeNavigationItem([Import] ITeamExplorerController controller)
this.IsVisible = true;
this.IsEnabled = true;

// Note on threading:
// MEF constructors must be free-threaded i.e. capable of running to completion the calling thread.
// There's no public documentation on how these whether it's ok to create WPF artefacts like brushes
// and icons in the constructor. However, this is what the VS Team Explorer implementations of this
// class are doing, so we assume it's ok.
// See [VS installation directory]\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer\Microsoft.TeamFoundation.TeamExplorer.Navigation.dll
var image = ResourceHelper.Get<DrawingImage>("SonarQubeServerIcon");
this.m_icon = image != null ? new DrawingBrush(image.Drawing) : null;

Expand Down

0 comments on commit ab52595

Please sign in to comment.