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

Refactor SonarLintSettings to follow VS threading rules #4850

Merged
merged 4 commits into from
Sep 6, 2023

Conversation

duncanp-lseg
Copy link
Contributor

Fixes #4843

@duncanp-lseg duncanp-lseg linked an issue Sep 5, 2023 that may be closed by this pull request
@duncanp-lseg duncanp-lseg changed the title Refactor SonarLintSettings to follow VS threading rule Refactor SonarLintSettings to follow VS threading rules Sep 6, 2023

[ImportingConstructor]
public SonarLintSettings(SVsServiceProvider serviceProvider)
: this(new ShellSettingsManager(serviceProvider))
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Previously the class created a concrete VS class in its constructor.
I've made two related changes:

  1. the code that creates the VS class has been moved to a separate class, and
  2. I added a Lazy property to this class to delay fetching the VS service until it is required.

if (this.writableSettingsStore != null &&
!this.writableSettingsStore.CollectionExists(SettingsRoot))
{
this.writableSettingsStore.CreateCollection(SettingsRoot);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This logic is moved to the new factory class.

}

internal /* testing purposes */ bool GetValueOrDefault(string key, bool defaultValue)
{
try
{
return this.writableSettingsStore?.GetBoolean(SettingsRoot, key, defaultValue)
return writableSettingsStore.Value?.GetBoolean(SettingsRoot, key, defaultValue)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

All of the references in this class to writableSettingsStore have changed to writableSettingsStore.Value. There's a unit test that checks that the factory is only invoked once.

using SonarLint.VisualStudio.TestInfrastructure;

namespace SonarLint.VisualStudio.Integration.UnitTests.Settings
{
[TestClass]
public class SonarLintSettingsTests
{
private ConfigurableWritableSettingsStore settingsStore;
Copy link
Contributor Author

@duncanp-lseg duncanp-lseg Sep 6, 2023

Choose a reason for hiding this comment

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

I re-wrote the existing tests to get rid of the TestInitialize, and to change the tests to be data-driven. Also, the old tests didn't use mocks, so they were more complicated than necessary. I suggest ignoring the old tests; I'll describe the behaviour of the new tests.

this.settingsManager = new ConfigurableSettingsManager(this.settingsStore);
}
[TestMethod]
public void MefCtor_CheckIsExported()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

New - the two standard MEF-export tests.


[TestMethod]
public void IntegrationSettings_Ctor_NullArgChecks()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Redundant - MEF-created so they won't be null.


// Assert
actual2.Should().Be(expected2, "Did not return default value");
#region Boolean method tests
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The interface has get and set methods for booleans, integers and strings.

There are four tests for each data type:

  1. GetValueOrDefault -> value is returned
  2. Get when store throws -> default is returned
  3. Get when store is null -> default is returned
  4. SetValue -> value is set
  5. SetValue when store is null -> no error

I couldn't see a simple way to de-duplicated the test code, so I copied and tweaked the tests for each data type.

// Arrange
string expected1 = "value";
this.settingsStore.SetString(SonarLintSettings.SettingsRoot, "key1", expected1);
#region String method tests
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Same set of tests but for strings.

public void GetValueOrDefault_String(string valueToReturn, string defaultValueSuppliedByCaller)
{
var store = new Mock<WritableSettingsStore>();
store.Setup(x => x.GetString(SonarLintSettings.SettingsRoot, "aProp", defaultValueSuppliedByCaller)).Returns(valueToReturn);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is effectively the only real change in the test from the boolean/int version: we're mocking a different method.

// Arrange
int expected1 = 1;
this.settingsStore.SetInt32(SonarLintSettings.SettingsRoot, "key1", expected1);
#region Int method tests
Copy link
Contributor Author

Choose a reason for hiding this comment

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

And the same tests for integers.

public class WritableSettingsStoreFactoryTests
{
[TestMethod]
public void MefCtor_CheckIsExported()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Two standard MEF tests

// The MEF constructor should be free-threaded, which it will be if
// it doesn't make any external calls.
serviceProvider.Invocations.Should().BeEmpty();
threadHandling.Invocations.Should().BeEmpty();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Test the constructor is free-threaded.

@duncanp-lseg duncanp-lseg marked this pull request as ready for review September 6, 2023 10:49
@duncanp-lseg
Copy link
Contributor Author

FYI the failing tests are unrelated to these changes - flaky tests.

@sonarqubecloud
Copy link

sonarqubecloud bot commented Sep 6, 2023

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 0 Code Smells

94.7% 94.7% Coverage
0.0% 0.0% Duplication

Copy link
Contributor

@bigfluffycookie bigfluffycookie left a comment

Choose a reason for hiding this comment

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

Looks good!

@duncanp-lseg duncanp-lseg merged commit d3f14fe into branch-hardening-Aug23b Sep 6, 2023
@duncanp-lseg duncanp-lseg deleted the dp/fixSonarSettings branch September 6, 2023 13:19
duncanp-lseg pushed a commit that referenced this pull request Sep 26, 2023
* Refactor SonarLintSettings to follow VS threading rule

Fixes #4843
ugras-ergun-sonarsource pushed a commit that referenced this pull request Oct 9, 2023
* Refactor SonarLintSettings to follow VS threading rule

Fixes #4843
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Infra] Refactor SonarLintSettings to follow VS threading rules
2 participants