From 0cb08e827d7d6347e24687072be169f96e235f8c Mon Sep 17 00:00:00 2001 From: Vasileios Naskos Date: Fri, 20 Dec 2024 10:23:24 +0100 Subject: [PATCH 1/3] SLVS-1660 Create integration tests for CFamily --- .cirrus.yaml | 1 + .../FileAnalysisTestsRunner.cs | 26 +++++++++--- .../Resources/CFamilyIssues.cpp | 10 +++++ .../SLCore.IntegrationTests.csproj | 3 ++ .../SimpleAnalysisTests.cs | 42 +++++++++++++++++-- 5 files changed, 74 insertions(+), 8 deletions(-) create mode 100644 src/SLCore.IntegrationTests/Resources/CFamilyIssues.cpp diff --git a/.cirrus.yaml b/.cirrus.yaml index 34afcbae80..009046fd31 100644 --- a/.cirrus.yaml +++ b/.cirrus.yaml @@ -5,6 +5,7 @@ env: CIRRUS_SHELL: bash USERPROFILE: C:\sonar-ci # Fixes error MSB3073 and path too long issue with restored packages TMP_DIR: C:\sonar-ci\temp + MSVC: C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.40.33807\bin\Hostx64\x64\cl.exe # Required for CFamily integration tests ec2_instance_definition: &INSTANCE_DEFINITION region: eu-central-1 diff --git a/src/SLCore.IntegrationTests/FileAnalysisTestsRunner.cs b/src/SLCore.IntegrationTests/FileAnalysisTestsRunner.cs index e46c1af79b..0f06aab9e4 100644 --- a/src/SLCore.IntegrationTests/FileAnalysisTestsRunner.cs +++ b/src/SLCore.IntegrationTests/FileAnalysisTestsRunner.cs @@ -41,6 +41,7 @@ internal sealed class FileAnalysisTestsRunner : IDisposable internal static readonly JavaScriptIssuesFile JavaScriptIssues = new(); internal static readonly OneIssueRuleWithParamFile OneIssueRuleWithParam = new(); internal static readonly TypeScriptIssuesFile TypeScriptIssues = new(); + internal static readonly CFamilyIssuesFile CFamilyIssues = new(); internal static readonly CssIssuesFile CssIssues = new(); internal static readonly VueIssuesFile VueIssues = new(); internal static readonly SecretsIssuesFile SecretsIssues = new(); @@ -79,8 +80,11 @@ public void SetRuleConfiguration(Dictionary rul rulesCoreService.UpdateStandaloneRulesConfiguration(new UpdateStandaloneRulesConfigurationParams(ruleConfig)); } - public async Task>> RunFileAnalysis(ITestingFile testingFile, string configScope, - bool sendContent = false) + public async Task>> RunFileAnalysis( + ITestingFile testingFile, + string configScope, + bool sendContent = false, + Dictionary extraProperties = null) { try { @@ -97,7 +101,7 @@ public async Task>> RunFileAnalysis(ITe await ConcurrencyTestHelper.WaitForTaskWithTimeout(analysisReadyCompletionSource.Task); - await RunSlCoreFileAnalysis(configScope, testingFile.GetFullPath(), analysisId); + await RunSlCoreFileAnalysis(configScope, testingFile.GetFullPath(), analysisId, extraProperties); await ConcurrencyTestHelper.WaitForTaskWithTimeout(analysisRaisedIssues.Task); return analysisRaisedIssues.Task.Result.issuesByFileUri; @@ -141,13 +145,15 @@ private void SetUpAnalysisListener( }); } - private async Task RunSlCoreFileAnalysis(string configScopeId, string fileToAnalyzeAbsolutePath, Guid analysisId) + private async Task RunSlCoreFileAnalysis(string configScopeId, string fileToAnalyzeAbsolutePath, Guid analysisId, Dictionary extraProperties = null) { + extraProperties ??= []; + slCoreTestRunner.SLCoreServiceProvider.TryGetTransientService(out IAnalysisSLCoreService analysisService).Should().BeTrue(); var (failedAnalysisFiles, _) = await analysisService.AnalyzeFilesAndTrackAsync( new AnalyzeFilesAndTrackParams(configScopeId, analysisId, - [new FileUri(fileToAnalyzeAbsolutePath)], [], false, + [new FileUri(fileToAnalyzeAbsolutePath)], extraProperties, false, DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()), CancellationToken.None); failedAnalysisFiles.Should().BeEmpty(); } @@ -215,6 +221,16 @@ internal class TypeScriptIssuesFile : ITestingFile ]; } +internal class CFamilyIssuesFile : ITestingFile +{ + public string RelativePath => @"Resources\CFamilyIssues.cpp"; + + public List ExpectedIssues => + [ + new("cpp:S1135", new TextRangeDto(7, 4, 7, 17), CleanCodeAttribute.COMPLETE, 0), + ]; +} + internal class CssIssuesFile : ITestingFile { public string RelativePath => @"Resources\CssIssues.css"; diff --git a/src/SLCore.IntegrationTests/Resources/CFamilyIssues.cpp b/src/SLCore.IntegrationTests/Resources/CFamilyIssues.cpp new file mode 100644 index 0000000000..22859b93f9 --- /dev/null +++ b/src/SLCore.IntegrationTests/Resources/CFamilyIssues.cpp @@ -0,0 +1,10 @@ +#include + +using namespace std; + +int main() +{ + // TODO: Test + cout << "Hello CMake." << endl; + return 0; +} diff --git a/src/SLCore.IntegrationTests/SLCore.IntegrationTests.csproj b/src/SLCore.IntegrationTests/SLCore.IntegrationTests.csproj index ab5587b0b6..56631bcd22 100644 --- a/src/SLCore.IntegrationTests/SLCore.IntegrationTests.csproj +++ b/src/SLCore.IntegrationTests/SLCore.IntegrationTests.csproj @@ -53,6 +53,9 @@ PreserveNewest + + PreserveNewest + diff --git a/src/SLCore.IntegrationTests/SimpleAnalysisTests.cs b/src/SLCore.IntegrationTests/SimpleAnalysisTests.cs index 89ed8aa24d..5722dbf904 100644 --- a/src/SLCore.IntegrationTests/SimpleAnalysisTests.cs +++ b/src/SLCore.IntegrationTests/SimpleAnalysisTests.cs @@ -18,8 +18,8 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +using System.IO; using SonarLint.VisualStudio.SLCore.Common.Models; -using SonarLint.VisualStudio.SLCore.Listener.Analysis.Models; namespace SonarLint.VisualStudio.SLCore.IntegrationTests; @@ -66,6 +66,14 @@ public Task DefaultRuleConfig_ContentFromDisk_TypeScriptAnalysisProducesExpected public Task DefaultRuleConfig_ContentFromRpc_TypeScriptAnalysisProducesExpectedIssues() => DefaultRuleConfig_AnalysisProducesExpectedIssuesInFile(FileAnalysisTestsRunner.TypeScriptIssues, true); + [TestMethod] + public Task DefaultRuleConfig_ContentFromDisk_CFamilyAnalysisProducesExpectedIssues() + => DefaultRuleConfig_AnalysisProducesExpectedIssuesInFile(FileAnalysisTestsRunner.CFamilyIssues, false, GenerateTestCompilationDatabase()); + + [TestMethod] + public Task DefaultRuleConfig_ContentFromRpc_CFamilyAnalysisProducesExpectedIssues() + => DefaultRuleConfig_AnalysisProducesExpectedIssuesInFile(FileAnalysisTestsRunner.CFamilyIssues, true, GenerateTestCompilationDatabase()); + [TestMethod] public Task DefaultRuleConfig_ContentFromDisk_CssAnalysisProducesExpectedIssues() => DefaultRuleConfig_AnalysisProducesExpectedIssuesInFile(FileAnalysisTestsRunner.CssIssues, false); @@ -82,13 +90,41 @@ public Task DefaultRuleConfig_ContentFromDisk_CssAnalysisInVueProducesExpectedIs public Task DefaultRuleConfig_ContentFromRpc_CssAnalysisInVyeProducesExpectedIssues() => DefaultRuleConfig_AnalysisProducesExpectedIssuesInFile(FileAnalysisTestsRunner.VueIssues, true); - private async Task DefaultRuleConfig_AnalysisProducesExpectedIssuesInFile(ITestingFile testingFile, bool sendContent) + private async Task DefaultRuleConfig_AnalysisProducesExpectedIssuesInFile(ITestingFile testingFile, bool sendContent, Dictionary extraProperties = null) { - var issuesByFileUri = await sharedFileAnalysisTestsRunner.RunFileAnalysis(testingFile, TestContext.TestName, sendContent: sendContent); + var issuesByFileUri = await sharedFileAnalysisTestsRunner.RunFileAnalysis(testingFile, TestContext.TestName, sendContent: sendContent, extraProperties: extraProperties); issuesByFileUri.Should().HaveCount(1); var receivedIssues = issuesByFileUri[new FileUri(testingFile.GetFullPath())]; var receivedTestIssues = receivedIssues.Select(x => new TestIssue(x.ruleKey, x.textRange, x.severityMode.Right?.cleanCodeAttribute, x.flows.Count)); receivedTestIssues.Should().BeEquivalentTo(testingFile.ExpectedIssues); } + + private static Dictionary GenerateTestCompilationDatabase() + { + /* The CFamily analysis apart from the source code file requires also the compilation database file. + The compilation database file must contain the absolute path to the source code file the compilation database json file and the compiler path. + For the compiler we use the MSVC which is set as an environment variable. Make sure the environment variable is set to point to the compiler path + (the absolute path to cl.exe). */ + var compilerPath = Environment.GetEnvironmentVariable("MSVC")?.Replace(@"\", @"\\"); + var cFamilyIssuesFileAbsolutePath = FileAnalysisTestsRunner.CFamilyIssues.GetFullPath().Replace(@"\", @"\\"); + var analysisDirectory = cFamilyIssuesFileAbsolutePath.Substring(0, cFamilyIssuesFileAbsolutePath.LastIndexOf('\\') + 1); + var jsonContent = $$""" + [ + { + "directory": "{{analysisDirectory}}", + "command": "\"{{compilerPath}}\" /nologo /TP /DWIN32 /D_WINDOWS /W3 /GR /EHsc /MDd /Ob0 /Od /RTC1 -std:c++20 -ZI /FoCFamilyIssues.cpp.obj /FS -c {{cFamilyIssuesFileAbsolutePath}}", + "file": "{{cFamilyIssuesFileAbsolutePath}}" + } + ] + """; + var tempCompilationDatabase = Path.ChangeExtension(Path.GetTempFileName(), ".json"); + File.WriteAllText(tempCompilationDatabase, jsonContent); + + var compilationDatabase = new Dictionary + { + { "sonar.cfamily.compile-commands", tempCompilationDatabase } + }; + return compilationDatabase; + } } From f66a1086b59517e89d947920fbae713f281156b1 Mon Sep 17 00:00:00 2001 From: Vasileios Naskos Date: Fri, 10 Jan 2025 11:42:09 +0100 Subject: [PATCH 2/3] Add more issues --- src/SLCore.IntegrationTests/FileAnalysisTestsRunner.cs | 5 ++++- src/SLCore.IntegrationTests/Resources/CFamilyIssues.cpp | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/SLCore.IntegrationTests/FileAnalysisTestsRunner.cs b/src/SLCore.IntegrationTests/FileAnalysisTestsRunner.cs index 0f06aab9e4..bcc1e2df15 100644 --- a/src/SLCore.IntegrationTests/FileAnalysisTestsRunner.cs +++ b/src/SLCore.IntegrationTests/FileAnalysisTestsRunner.cs @@ -227,7 +227,10 @@ internal class CFamilyIssuesFile : ITestingFile public List ExpectedIssues => [ - new("cpp:S1135", new TextRangeDto(7, 4, 7, 17), CleanCodeAttribute.COMPLETE, 0), + new("cpp:S1135", new TextRangeDto(7, 4, 7, 29), CleanCodeAttribute.COMPLETE, 0), + new("cpp:S1481", new TextRangeDto(10, 9, 10, 10), CleanCodeAttribute.CLEAR, 0), + new("cpp:S5350", new TextRangeDto(10, 4, 10, 17), CleanCodeAttribute.CLEAR, 0), + new("cpp:S4962", new TextRangeDto(10, 13, 10, 17), CleanCodeAttribute.CONVENTIONAL, 0), ]; } diff --git a/src/SLCore.IntegrationTests/Resources/CFamilyIssues.cpp b/src/SLCore.IntegrationTests/Resources/CFamilyIssues.cpp index 22859b93f9..66672f8458 100644 --- a/src/SLCore.IntegrationTests/Resources/CFamilyIssues.cpp +++ b/src/SLCore.IntegrationTests/Resources/CFamilyIssues.cpp @@ -4,7 +4,10 @@ using namespace std; int main() { - // TODO: Test + // TODO: This is an issue cout << "Hello CMake." << endl; + + int* a = NULL; // Some more issues + return 0; } From fcabc720dbeae2bdb435481bcb8aaecffe76951d Mon Sep 17 00:00:00 2001 From: Vasileios Naskos Date: Fri, 10 Jan 2025 13:56:33 +0100 Subject: [PATCH 3/3] Normalize paths --- src/SLCore.IntegrationTests/SimpleAnalysisTests.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/SLCore.IntegrationTests/SimpleAnalysisTests.cs b/src/SLCore.IntegrationTests/SimpleAnalysisTests.cs index 5722dbf904..b0448c0d89 100644 --- a/src/SLCore.IntegrationTests/SimpleAnalysisTests.cs +++ b/src/SLCore.IntegrationTests/SimpleAnalysisTests.cs @@ -106,9 +106,9 @@ private static Dictionary GenerateTestCompilationDatabase() The compilation database file must contain the absolute path to the source code file the compilation database json file and the compiler path. For the compiler we use the MSVC which is set as an environment variable. Make sure the environment variable is set to point to the compiler path (the absolute path to cl.exe). */ - var compilerPath = Environment.GetEnvironmentVariable("MSVC")?.Replace(@"\", @"\\"); - var cFamilyIssuesFileAbsolutePath = FileAnalysisTestsRunner.CFamilyIssues.GetFullPath().Replace(@"\", @"\\"); - var analysisDirectory = cFamilyIssuesFileAbsolutePath.Substring(0, cFamilyIssuesFileAbsolutePath.LastIndexOf('\\') + 1); + var compilerPath = NormalizePath(Environment.GetEnvironmentVariable("MSVC")); + var cFamilyIssuesFileAbsolutePath = NormalizePath(FileAnalysisTestsRunner.CFamilyIssues.GetFullPath()); + var analysisDirectory = NormalizePath(Path.GetDirectoryName(cFamilyIssuesFileAbsolutePath)); var jsonContent = $$""" [ { @@ -127,4 +127,11 @@ For the compiler we use the MSVC which is set as an environment variable. Make s }; return compilationDatabase; } + + private static string NormalizePath(string path) + { + var singleDirectorySeparator = Path.DirectorySeparatorChar.ToString(); + var doubleDirectorySeparator = singleDirectorySeparator + singleDirectorySeparator; + return path?.Replace(singleDirectorySeparator, doubleDirectorySeparator); + } }