diff --git a/src/Core.UnitTests/Binding/ServerConnectionTests.cs b/src/Core.UnitTests/Binding/ServerConnectionTests.cs index f5ba24837a..4f0c753a8b 100644 --- a/src/Core.UnitTests/Binding/ServerConnectionTests.cs +++ b/src/Core.UnitTests/Binding/ServerConnectionTests.cs @@ -57,6 +57,7 @@ public void Ctor_SonarCloud_NullCredentials_SetsNull() [TestMethod] public void Ctor_SonarCloud_SetsProperties() { + var expectedServerUri = new Uri("https://sonarcloud.io"); var serverConnectionSettings = new ServerConnectionSettings(false); var credentials = Substitute.For(); var sonarCloud = new ServerConnection.SonarCloud(Org, serverConnectionSettings, credentials); diff --git a/src/Core.UnitTests/Persistence/JsonFileHandlerTest.cs b/src/Core.UnitTests/Persistence/JsonFileHandlerTest.cs index 385a484d50..93a99ffdd1 100644 --- a/src/Core.UnitTests/Persistence/JsonFileHandlerTest.cs +++ b/src/Core.UnitTests/Persistence/JsonFileHandlerTest.cs @@ -55,76 +55,29 @@ public void MefCtor_CheckExports() [TestMethod] public void Mef_CheckIsSingleton() { - MefTestHelpers.CheckIsSingletonMefComponent(); + MefTestHelpers.CheckIsNonSharedMefComponent(); } [TestMethod] - public void TryReadFile_FileDoesNotExist_ReturnsFalse() - { - fileSystem.File.Exists(FilePath).Returns(false); - - var succeeded = testSubject.TryReadFile(FilePath, out TestType deserializedContent); - - succeeded.Should().BeFalse(); - deserializedContent.Should().BeNull(); - fileSystem.File.Received(1).Exists(FilePath); - } - - [TestMethod] - public void TryReadFile_FileExists_ReturnsTrueAndDeserializeContent() - { - var expectedContent = new TestType("test"); - var serializedContent = JsonConvert.SerializeObject(expectedContent); - fileSystem.File.Exists(FilePath).Returns(true); - fileSystem.File.ReadAllText(FilePath).Returns(serializedContent); - serializer.TryDeserialize(Arg.Any(), out Arg.Any()).Returns(true); - - var succeeded = testSubject.TryReadFile(FilePath, out TestType _); - - succeeded.Should().BeTrue(); - Received.InOrder(() => - { - fileSystem.File.Exists(FilePath); - fileSystem.File.ReadAllText(FilePath); - serializer.TryDeserialize(Arg.Any(), out Arg.Any()); - }); - } - - [TestMethod] - public void TryReadFile_ReadingFileThrowsException_WritesLogAndReturnsFalse() + public void ReadFile_ReadingFileThrowsException_TrowsException() { var exceptionMsg = "IO failed"; - fileSystem.File.Exists(FilePath).Returns(true); fileSystem.File.When(x => x.ReadAllText(FilePath)).Do(x => throw new Exception(exceptionMsg)); - var succeeded = testSubject.TryReadFile(FilePath, out TestType _); - - succeeded.Should().BeFalse(); - logger.Received(1).WriteLine(exceptionMsg); - } - - [TestMethod] - public void TryReadFile_DeserializationThrowsException_WritesLogAndReturnsFalse() - { - var exceptionMsg = "deserialization failed"; - fileSystem.File.Exists(FilePath).Returns(true); - serializer.When(x => x.TryDeserialize(Arg.Any(), out Arg.Any())).Do(x => throw new Exception(exceptionMsg)); + Action act = () => testSubject.ReadFile(FilePath); - var succeeded = testSubject.TryReadFile(FilePath, out TestType _); - - succeeded.Should().BeFalse(); - logger.Received(1).WriteLine(exceptionMsg); + act.Should().Throw().WithMessage(exceptionMsg); } [TestMethod] - public void TryReadFile_DeserializationFails_WritesLogAndReturnsFalse() + public void ReadFile_DeserializationThrowsException_TrowsException() { - fileSystem.File.Exists(FilePath).Returns(true); - serializer.TryDeserialize(Arg.Any(), out Arg.Any()).Returns(false); + var exceptionMsg = "IO failed"; + serializer.When(x => x.Deserialize(Arg.Any())).Do(x => throw new Exception(exceptionMsg)); - var succeeded = testSubject.TryReadFile(FilePath, out TestType _); + Action act = () => testSubject.ReadFile(FilePath); - succeeded.Should().BeFalse(); + act.Should().Throw().WithMessage(exceptionMsg); } [TestMethod] diff --git a/src/Core.UnitTests/Persistence/JsonSerializerTests.cs b/src/Core.UnitTests/Persistence/JsonSerializerTests.cs index 50d87bcf55..00411e046c 100644 --- a/src/Core.UnitTests/Persistence/JsonSerializerTests.cs +++ b/src/Core.UnitTests/Persistence/JsonSerializerTests.cs @@ -96,4 +96,24 @@ public void TryDeserialize_Fails_LogsAndReturnsFalse() deserializedObj.Should().BeNull(); logger.Received(1).WriteLine(string.Format(PersistenceStrings.FailedToDeserializeObject, nameof(TestType))); } + + [TestMethod] + public void Deserialize_Succeeds_ReturnsString() + { + var serializedString = "{\"PropName\":\"abc\"}"; + + var deserializedObj = testSubject.Deserialize(serializedString); + + deserializedObj.Should().BeEquivalentTo(new TestType("abc")); + } + + [TestMethod] + public void Deserialize_Fails_ThrowsException() + { + var serializedString = "invalid"; + + Action act = () => testSubject.Deserialize(serializedString); + + act.Should().Throw(); + } } diff --git a/src/Core/Persistence/JsonFileHandler.cs b/src/Core/Persistence/JsonFileHandler.cs index 2673e49f4a..7444cbd358 100644 --- a/src/Core/Persistence/JsonFileHandler.cs +++ b/src/Core/Persistence/JsonFileHandler.cs @@ -27,18 +27,32 @@ namespace SonarLint.VisualStudio.Core.Persistence; public interface IJsonFileHandler { - bool TryReadFile(string filePath, out T content) where T : class; + /// + /// Reads the json file and deserializes its content to the provided type. + /// + /// The type of the model that will be serialized + /// The path to the file + /// Returns the content of the json file deserialized to the provided type. + T ReadFile(string filePath) where T : class; + + /// + /// Tries to deserialize the model and write it to the json file. + /// If the file does not exist, it will be created. + /// + /// The type of the model that will be deserialized + /// The path to the file + /// The model that will be deserialized + /// True if the model was deserialized successfully and written to the file. False otherwise bool TryWriteToFile(string filePath, T model) where T : class; } [Export(typeof(IJsonFileHandler))] -[PartCreationPolicy(CreationPolicy.Shared)] +[PartCreationPolicy(CreationPolicy.NonShared)] public class JsonFileHandler : IJsonFileHandler { private readonly ILogger logger; private readonly IFileSystem fileSystem; private readonly IJsonSerializer jsonSerializer; - private static readonly object Locker = new(); [ImportingConstructor] public JsonFileHandler(IJsonSerializer jsonSerializer, ILogger logger) : this(new FileSystem(), jsonSerializer, logger) { } @@ -50,52 +64,34 @@ public JsonFileHandler(IJsonSerializer jsonSerializer, ILogger logger) : this(ne this.logger = logger; } - public bool TryReadFile(string filePath, out T content) where T: class + public T ReadFile(string filePath) where T : class { - content = null; - if (!fileSystem.File.Exists(filePath)) - { - return false; - } - - try - { - var jsonContent = fileSystem.File.ReadAllText(filePath); - return jsonSerializer.TryDeserialize(jsonContent, out content); - - } - catch (Exception ex) when (!ErrorHandler.IsCriticalException(ex)) - { - logger.WriteLine(ex.Message); - return false; - } + var jsonContent = fileSystem.File.ReadAllText(filePath); + return jsonSerializer.Deserialize(jsonContent); } public bool TryWriteToFile(string filePath, T model) where T : class { - lock (Locker) + try { - try + var directoryName = Path.GetDirectoryName(filePath); + if (!fileSystem.Directory.Exists(directoryName)) { - var directoryName = Path.GetDirectoryName(filePath); - if (!fileSystem.Directory.Exists(directoryName)) - { - fileSystem.Directory.CreateDirectory(directoryName); - } - - var wasContentDeserialized = jsonSerializer.TrySerialize(model, out string serializedObj, Formatting.Indented); - if (wasContentDeserialized) - { - fileSystem.File.WriteAllText(filePath, serializedObj); - return true; - } + fileSystem.Directory.CreateDirectory(directoryName); } - catch (Exception ex) when (!ErrorHandler.IsCriticalException(ex)) + + var wasContentDeserialized = jsonSerializer.TrySerialize(model, out string serializedObj, Formatting.Indented); + if (wasContentDeserialized) { - logger.WriteLine(ex.Message); + fileSystem.File.WriteAllText(filePath, serializedObj); + return true; } - - return false; } + catch (Exception ex) when (!ErrorHandler.IsCriticalException(ex)) + { + logger.WriteLine(ex.Message); + } + + return false; } } diff --git a/src/Core/Persistence/JsonSerializer.cs b/src/Core/Persistence/JsonSerializer.cs index a604f86036..6ec3f0be5f 100644 --- a/src/Core/Persistence/JsonSerializer.cs +++ b/src/Core/Persistence/JsonSerializer.cs @@ -27,6 +27,7 @@ namespace SonarLint.VisualStudio.Core.Persistence; public interface IJsonSerializer { bool TryDeserialize(string json, out T deserializedObj, JsonSerializerSettings serializerSettings = null) where T : class; + T Deserialize(string json, JsonSerializerSettings serializerSettings = null) where T : class; bool TrySerialize(T objectToSerialize, out string serializedObj, Formatting formatting = Formatting.None, JsonSerializerSettings serializerSettings = null) where T : class; } @@ -55,7 +56,7 @@ public bool TryDeserialize(string json, out T deserializedObj, JsonSerializer deserializedObj = null; try { - deserializedObj = JsonConvert.DeserializeObject(json, serializerSettings); + deserializedObj = Deserialize(json, serializerSettings); return true; } catch (Exception) @@ -65,6 +66,11 @@ public bool TryDeserialize(string json, out T deserializedObj, JsonSerializer } } + public T Deserialize(string json, JsonSerializerSettings serializerSettings = null) where T : class + { + return JsonConvert.DeserializeObject(json, serializerSettings); + } + public bool TrySerialize(T objectToSerialize, out string serializedObj, Formatting formatting = Formatting.None, JsonSerializerSettings serializerSettings = null) where T: class { serializedObj = null;