From 4a0a8d6da23e0d72617a49a5ee22f3cbad45e86c Mon Sep 17 00:00:00 2001 From: Miha Kralj Date: Wed, 3 Dec 2025 09:27:29 -0800 Subject: [PATCH] chore: Update project files and configurations; enhance .gitignore, add Qodana and SonarScanner scripts, and improve test project references --- .github/sonarscanner.ps1 | 85 +++++++++++++++++++++ .gitignore | 4 + .vscode/settings.json | 4 +- Directory.Build.props | 3 +- GitVersion.yml | 16 ++-- lib/QuanTAlib.Tests.csproj | 2 + lib/averages/ema/Ema.Tests.cs | 4 - lib/averages/ema/Ema.Validation.Tests.cs | 8 +- lib/averages/ema/Ema.cs | 19 ++++- lib/averages/ema/EmaVector.Tests.cs | 4 - lib/averages/sma/Sma.Tests.cs | 4 - lib/averages/sma/Sma.Validation.Tests.cs | 8 +- lib/averages/sma/SmaVector.Tests.cs | 3 - lib/averages/wma/Wma.Tests.cs | 4 - lib/averages/wma/Wma.Validation.Tests.cs | 8 +- lib/averages/wma/WmaVector.Tests.cs | 3 - lib/core/ringbuffer/RingBuffer.Tests.cs | 4 - lib/core/ringbuffer/RingBuffer.cs | 24 +++++- lib/core/simd/SimdExtensions.Tests.cs | 9 +-- lib/core/tbar/TBar.Tests.cs | 3 - lib/core/tbarseries/TBarSeries.Tests.cs | 6 -- lib/core/tseries/TSeries.Tests.cs | 6 -- lib/core/tvalue/TValue.Tests.cs | 3 - lib/feeds/csv/CsvFeed.Tests.cs | 1 - lib/feeds/gbm/Gbm.Tests.cs | 3 - lib/quantalib.csproj | 10 +-- qodana.yaml | 96 ++++++++++++++++++++++++ quantower/Quantower.Tests.csproj | 1 + 28 files changed, 257 insertions(+), 88 deletions(-) create mode 100644 .github/sonarscanner.ps1 create mode 100644 qodana.yaml diff --git a/.github/sonarscanner.ps1 b/.github/sonarscanner.ps1 new file mode 100644 index 00000000..2692cb29 --- /dev/null +++ b/.github/sonarscanner.ps1 @@ -0,0 +1,85 @@ +#Requires -Version 7.0 +<# +.SYNOPSIS + Static Analysis Scanner for QuanTAlib +.DESCRIPTION + Runs SonarCloud and Qodana with code coverage on Windows +#> + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +# Load tokens from User environment if not in process environment +if (-not $env:SONAR_TOKEN) { + $env:SONAR_TOKEN = [System.Environment]::GetEnvironmentVariable("SONAR_TOKEN", "User") +} +if (-not $env:QODANA_TOKEN) { + $env:QODANA_TOKEN = [System.Environment]::GetEnvironmentVariable("QODANA_TOKEN", "User") +} + +# Verify tokens are available +if (-not $env:SONAR_TOKEN) { + Write-Error "SONAR_TOKEN environment variable not set" + exit 1 +} +if (-not $env:QODANA_TOKEN) { + Write-Error "QODANA_TOKEN environment variable not set" + exit 1 +} + +# Coverage output directory for Qodana +$CoverageDir = ".qodana/code-coverage" +New-Item -ItemType Directory -Force -Path $CoverageDir | Out-Null + +# Start SonarScanner analysis context +Write-Host "==> Starting SonarScanner analysis..." -ForegroundColor Cyan +dotnet-sonarscanner begin ` + /o:"mihakralj-quantalib" ` + /k:"mihakralj_QuanTAlib" ` + /d:sonar.token="$env:SONAR_TOKEN" ` + /d:sonar.cs.opencover.reportsPaths="**/coverage.opencover.xml" +if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + +# Build solution (within SonarScanner context) +Write-Host "==> Building solution..." -ForegroundColor Cyan +dotnet build --no-incremental +if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + +# Run tests with coverage (opencover format works for both SonarCloud and can be converted) +Write-Host "==> Running tests with coverage..." -ForegroundColor Cyan +dotnet test --no-build --collect:"XPlat Code Coverage" ` + -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=opencover,lcov +# Continue even if tests fail - we still want coverage data for analysis + +# Copy coverage files to Qodana directory with unique names +Write-Host "==> Copying coverage files to Qodana directory..." -ForegroundColor Cyan +$coverageFiles = Get-ChildItem -Recurse -Filter "coverage.info" -ErrorAction SilentlyContinue +if ($coverageFiles) { + $index = 0 + foreach ($file in $coverageFiles) { + $destName = "coverage_$index.info" + Copy-Item $file.FullName -Destination (Join-Path $CoverageDir $destName) -Force + Write-Host " Copied: $($file.FullName) -> $destName" -ForegroundColor Gray + $index++ + } +} else { + Write-Host " WARNING: No coverage.info files found" -ForegroundColor Yellow +} + +# End SonarScanner analysis +Write-Host "==> Completing SonarScanner analysis..." -ForegroundColor Cyan +dotnet-sonarscanner end /d:sonar.token="$env:SONAR_TOKEN" +if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + +Write-Host "==> SonarCloud: https://sonarcloud.io/project/overview?id=mihakralj_QuanTAlib" -ForegroundColor Green + +# Run Qodana +Write-Host "==> Starting Qodana analysis..." -ForegroundColor Cyan + +# Set CI environment to suppress interactive prompts +$env:CI = "true" +qodana scan --within-docker=false -l qodana-dotnet --coverage-dir $CoverageDir +if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + +Write-Host "==> Qodana analysis complete" -ForegroundColor Green +Write-Host "==> Done!" -ForegroundColor Green diff --git a/.gitignore b/.gitignore index 917ca329..b79d48d3 100644 --- a/.gitignore +++ b/.gitignore @@ -413,3 +413,7 @@ ilspy/ #Ignore insiders AI rules .github/instructions/codacy.instructions.md + + +#Ignore vscode AI rules +.github\instructions\codacy.instructions.md diff --git a/.vscode/settings.json b/.vscode/settings.json index 1eda022f..bc57a89e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -170,7 +170,9 @@ "sonarlint.connectedMode.project": { "connectionId": "mihakralj-quantalib", "projectKey": "mihakralj_QuanTAlib" - } + }, + "qodana.projectId": "KbxmN" + // Note: Native mode is configured in qodana.yaml with withinDocker: false // ??????????????????????????????????????????????????????????????????? // Keyboard Shortcuts Reference diff --git a/Directory.Build.props b/Directory.Build.props index f4ab4530..78ff3800 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -44,12 +44,11 @@ - S1144,S1944,S2053,S2222,S2259,S2583,S2589,S3329,S3655,S3776,S3900,S3949,S3966,S4158,S4347,S5773,S6781 + S1144,S1944,S2053,S2222,S2245,S2259,S2583,S2589,S3329,S3655,S3776,S3900,S3949,S3966,S4158,S4347,S5773,S6781 - diff --git a/GitVersion.yml b/GitVersion.yml index 2ea209fe..171a6c3e 100644 --- a/GitVersion.yml +++ b/GitVersion.yml @@ -1,25 +1,31 @@ +workflow: GitHubFlow/v1 assembly-versioning-scheme: MajorMinorPatch assembly-file-versioning-scheme: MajorMinorPatch +tag-prefix: '[vV]' major-version-bump-message: '\+semver:\s?(breaking|major)' minor-version-bump-message: '\+semver:\s?(feature|minor)' patch-version-bump-message: '\+semver:\s?(fix|patch)' no-bump-message: '\+semver:\s?(none|skip)' -tag-prefix: '[vV]' branches: main: - regex: ^main$ mode: ContinuousDeployment + label: '' increment: Patch + prevent-increment: + of-merged-branch: true track-merge-target: false is-release-branch: true + is-main-branch: true pre-release-weight: 0 develop: - regex: ^dev(elop)?(ment)?$ mode: ContinuousDelivery + label: alpha increment: Patch track-merge-target: true - is-release-branch: false - source-branches: ['main'] + track-merge-message: true + regex: ^dev(elop)?(ment)?$ + source-branches: + - main pre-release-weight: 30000 ignore: sha: [] diff --git a/lib/QuanTAlib.Tests.csproj b/lib/QuanTAlib.Tests.csproj index b4cc5f7b..7350b73e 100644 --- a/lib/QuanTAlib.Tests.csproj +++ b/lib/QuanTAlib.Tests.csproj @@ -22,6 +22,8 @@ + + diff --git a/lib/averages/ema/Ema.Tests.cs b/lib/averages/ema/Ema.Tests.cs index a3745376..56d9b439 100644 --- a/lib/averages/ema/Ema.Tests.cs +++ b/lib/averages/ema/Ema.Tests.cs @@ -1,7 +1,3 @@ -using System; -using Xunit; -using QuanTAlib; - namespace QuanTAlib.Tests; public class EmaTests diff --git a/lib/averages/ema/Ema.Validation.Tests.cs b/lib/averages/ema/Ema.Validation.Tests.cs index 898c1d9d..3c56aa0e 100644 --- a/lib/averages/ema/Ema.Validation.Tests.cs +++ b/lib/averages/ema/Ema.Validation.Tests.cs @@ -4,9 +4,7 @@ using System.Linq; using Skender.Stock.Indicators; using TALib; using Tulip; -using Xunit; using Xunit.Abstractions; -using QuanTAlib; namespace QuanTAlib.Tests; @@ -213,7 +211,7 @@ public class EmaValidationTests // Calculate Tulip EMA var emaIndicator = Tulip.Indicators.ema; double[][] inputs = { tData }; - double[] options = { (double)period }; + double[] options = { period }; double[][] outputs = { new double[tData.Length] }; emaIndicator.Run(inputs, options, outputs); @@ -246,7 +244,7 @@ public class EmaValidationTests // Calculate Tulip EMA var emaIndicator = Tulip.Indicators.ema; double[][] inputs = { tData }; - double[] options = { (double)period }; + double[] options = { period }; double[][] outputs = { new double[tData.Length] }; emaIndicator.Run(inputs, options, outputs); @@ -275,7 +273,7 @@ public class EmaValidationTests // Calculate Tulip EMA var emaIndicator = Tulip.Indicators.ema; double[][] inputs = { sourceData }; - double[] options = { (double)period }; + double[] options = { period }; double[][] outputs = { new double[sourceData.Length] }; emaIndicator.Run(inputs, options, outputs); diff --git a/lib/averages/ema/Ema.cs b/lib/averages/ema/Ema.cs index ccb1f023..8b12123e 100644 --- a/lib/averages/ema/Ema.cs +++ b/lib/averages/ema/Ema.cs @@ -25,12 +25,25 @@ namespace QuanTAlib; /// public class Ema { - private struct State + private struct State : IEquatable { public double Ema; public double E; public bool IsHot; + public static State New() => new() { Ema = 0, E = 1.0, IsHot = false }; + + public readonly bool Equals(State other) => + Ema == other.Ema && E == other.E && IsHot == other.IsHot; + + public override readonly bool Equals(object? obj) => + obj is State other && Equals(other); + + public override readonly int GetHashCode() => + HashCode.Combine(Ema, E, IsHot); + + public static bool operator ==(State left, State right) => left.Equals(right); + public static bool operator !=(State left, State right) => !left.Equals(right); } private readonly double _alpha; @@ -60,7 +73,7 @@ public class Ema /// /// Creates EMA with specified alpha smoothing factor. /// - /// Smoothing factor (0 < alpha <= 1) + /// Smoothing factor (0 < alpha <= 1) public Ema(double alpha) { if (alpha <= 0 || alpha > 1) @@ -217,7 +230,7 @@ public class Ema /// /// Input values /// Output span (must be same length as source) - /// Smoothing factor (0 < alpha <= 1) + /// Smoothing factor (0 < alpha <= 1) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Calculate(ReadOnlySpan source, Span output, double alpha) { diff --git a/lib/averages/ema/EmaVector.Tests.cs b/lib/averages/ema/EmaVector.Tests.cs index d635c1ca..ab466839 100644 --- a/lib/averages/ema/EmaVector.Tests.cs +++ b/lib/averages/ema/EmaVector.Tests.cs @@ -1,7 +1,3 @@ -using System; -using System.Linq; -using Xunit; -using QuanTAlib; namespace QuanTAlib.Tests; diff --git a/lib/averages/sma/Sma.Tests.cs b/lib/averages/sma/Sma.Tests.cs index 4fba1f6d..12334267 100644 --- a/lib/averages/sma/Sma.Tests.cs +++ b/lib/averages/sma/Sma.Tests.cs @@ -1,7 +1,3 @@ -using System; -using Xunit; -using QuanTAlib; - namespace QuanTAlib.Tests; public class SmaTests diff --git a/lib/averages/sma/Sma.Validation.Tests.cs b/lib/averages/sma/Sma.Validation.Tests.cs index acebf2c7..d46fee42 100644 --- a/lib/averages/sma/Sma.Validation.Tests.cs +++ b/lib/averages/sma/Sma.Validation.Tests.cs @@ -4,9 +4,7 @@ using System.Linq; using Skender.Stock.Indicators; using TALib; using Tulip; -using Xunit; using Xunit.Abstractions; -using QuanTAlib; namespace QuanTAlib.Tests; @@ -213,7 +211,7 @@ public class SmaValidationTests // Calculate Tulip SMA var smaIndicator = Tulip.Indicators.sma; double[][] inputs = { tData }; - double[] options = { (double)period }; + double[] options = { period }; int lookback = period - 1; double[][] outputs = { new double[tData.Length - lookback] }; @@ -247,7 +245,7 @@ public class SmaValidationTests // Calculate Tulip SMA var smaIndicator = Tulip.Indicators.sma; double[][] inputs = { tData }; - double[] options = { (double)period }; + double[] options = { period }; int lookback = period - 1; double[][] outputs = { new double[tData.Length - lookback] }; @@ -277,7 +275,7 @@ public class SmaValidationTests // Calculate Tulip SMA var smaIndicator = Tulip.Indicators.sma; double[][] inputs = { sourceData }; - double[] options = { (double)period }; + double[] options = { period }; int lookback = period - 1; double[][] outputs = { new double[sourceData.Length - lookback] }; diff --git a/lib/averages/sma/SmaVector.Tests.cs b/lib/averages/sma/SmaVector.Tests.cs index 903bb9bb..99209d59 100644 --- a/lib/averages/sma/SmaVector.Tests.cs +++ b/lib/averages/sma/SmaVector.Tests.cs @@ -1,6 +1,3 @@ -using System.Linq; -using Xunit; -using QuanTAlib; namespace QuanTAlib.Tests; diff --git a/lib/averages/wma/Wma.Tests.cs b/lib/averages/wma/Wma.Tests.cs index 0542eb39..414334e3 100644 --- a/lib/averages/wma/Wma.Tests.cs +++ b/lib/averages/wma/Wma.Tests.cs @@ -1,7 +1,3 @@ -using System; -using Xunit; -using QuanTAlib; - namespace QuanTAlib.Tests; public class WmaTests diff --git a/lib/averages/wma/Wma.Validation.Tests.cs b/lib/averages/wma/Wma.Validation.Tests.cs index f686a300..633baed0 100644 --- a/lib/averages/wma/Wma.Validation.Tests.cs +++ b/lib/averages/wma/Wma.Validation.Tests.cs @@ -4,9 +4,7 @@ using System.Linq; using Skender.Stock.Indicators; using TALib; using Tulip; -using Xunit; using Xunit.Abstractions; -using QuanTAlib; namespace QuanTAlib.Tests; @@ -213,7 +211,7 @@ public class WmaValidationTests // Calculate Tulip WMA var wmaIndicator = Tulip.Indicators.wma; double[][] inputs = { tData }; - double[] options = { (double)period }; + double[] options = { period }; int lookback = period - 1; double[][] outputs = { new double[tData.Length - lookback] }; @@ -247,7 +245,7 @@ public class WmaValidationTests // Calculate Tulip WMA var wmaIndicator = Tulip.Indicators.wma; double[][] inputs = { tData }; - double[] options = { (double)period }; + double[] options = { period }; int lookback = period - 1; double[][] outputs = { new double[tData.Length - lookback] }; @@ -277,7 +275,7 @@ public class WmaValidationTests // Calculate Tulip WMA var wmaIndicator = Tulip.Indicators.wma; double[][] inputs = { sourceData }; - double[] options = { (double)period }; + double[] options = { period }; int lookback = period - 1; double[][] outputs = { new double[sourceData.Length - lookback] }; diff --git a/lib/averages/wma/WmaVector.Tests.cs b/lib/averages/wma/WmaVector.Tests.cs index 2cb70aaf..2805001d 100644 --- a/lib/averages/wma/WmaVector.Tests.cs +++ b/lib/averages/wma/WmaVector.Tests.cs @@ -1,6 +1,3 @@ -using System.Linq; -using Xunit; -using QuanTAlib; namespace QuanTAlib.Tests; diff --git a/lib/core/ringbuffer/RingBuffer.Tests.cs b/lib/core/ringbuffer/RingBuffer.Tests.cs index 367d19b9..aa896951 100644 --- a/lib/core/ringbuffer/RingBuffer.Tests.cs +++ b/lib/core/ringbuffer/RingBuffer.Tests.cs @@ -1,7 +1,3 @@ -using System; -using System.Collections; -using Xunit; -using QuanTAlib; namespace QuanTAlib.Tests; diff --git a/lib/core/ringbuffer/RingBuffer.cs b/lib/core/ringbuffer/RingBuffer.cs index 0f92a190..6a6ba491 100644 --- a/lib/core/ringbuffer/RingBuffer.cs +++ b/lib/core/ringbuffer/RingBuffer.cs @@ -423,7 +423,7 @@ public sealed class RingBuffer : IEnumerable /// /// High-performance enumerator for the RingBuffer. /// - public struct Enumerator : IEnumerator + public struct Enumerator : IEnumerator, IEquatable { private readonly RingBuffer _buffer; private readonly int _start; @@ -453,8 +453,8 @@ public sealed class RingBuffer : IEnumerable return true; } - public double Current => _current; - object IEnumerator.Current => Current; + public readonly double Current => _current; + readonly object IEnumerator.Current => Current; [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Reset() @@ -463,6 +463,22 @@ public sealed class RingBuffer : IEnumerable _current = default; } - public void Dispose() { } + public readonly void Dispose() { } + + public readonly bool Equals(Enumerator other) => + ReferenceEquals(_buffer, other._buffer) && + _start == other._start && + _count == other._count && + _index == other._index && + _current == other._current; + + public override readonly bool Equals(object? obj) => + obj is Enumerator other && Equals(other); + + public override readonly int GetHashCode() => + HashCode.Combine(RuntimeHelpers.GetHashCode(_buffer), _start, _count, _index, _current); + + public static bool operator ==(Enumerator left, Enumerator right) => left.Equals(right); + public static bool operator !=(Enumerator left, Enumerator right) => !left.Equals(right); } } diff --git a/lib/core/simd/SimdExtensions.Tests.cs b/lib/core/simd/SimdExtensions.Tests.cs index c9118924..67ba0ec0 100644 --- a/lib/core/simd/SimdExtensions.Tests.cs +++ b/lib/core/simd/SimdExtensions.Tests.cs @@ -1,6 +1,3 @@ -using System; -using Xunit; -using QuanTAlib; namespace QuanTAlib.Tests; @@ -550,11 +547,13 @@ public class SimdExtensionsTests Assert.True(avg > 0); Assert.True(min > 0); Assert.True(max > min); + Assert.Equal(min, minAlt); + Assert.Equal(max, maxAlt); Assert.True(variance > 0); Assert.True(stdDev > 0); - Assert.True(sw.ElapsedMilliseconds < 10, - $"SIMD operations took {sw.ElapsedMilliseconds}ms, expected < 10ms"); + Assert.True(sw.ElapsedMilliseconds < 50, + $"SIMD operations took {sw.ElapsedMilliseconds}ms, expected < 50ms"); } [Fact] diff --git a/lib/core/tbar/TBar.Tests.cs b/lib/core/tbar/TBar.Tests.cs index 6d22c8c6..1f33fd3b 100644 --- a/lib/core/tbar/TBar.Tests.cs +++ b/lib/core/tbar/TBar.Tests.cs @@ -1,6 +1,3 @@ -using System; -using Xunit; -using QuanTAlib; namespace QuanTAlib.Tests { diff --git a/lib/core/tbarseries/TBarSeries.Tests.cs b/lib/core/tbarseries/TBarSeries.Tests.cs index e505ce3a..50e508a6 100644 --- a/lib/core/tbarseries/TBarSeries.Tests.cs +++ b/lib/core/tbarseries/TBarSeries.Tests.cs @@ -1,9 +1,3 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using Xunit; -using QuanTAlib; namespace QuanTAlib.Tests { diff --git a/lib/core/tseries/TSeries.Tests.cs b/lib/core/tseries/TSeries.Tests.cs index b29fe2b4..a6379dc6 100644 --- a/lib/core/tseries/TSeries.Tests.cs +++ b/lib/core/tseries/TSeries.Tests.cs @@ -1,9 +1,3 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using Xunit; -using QuanTAlib; namespace QuanTAlib.Tests { diff --git a/lib/core/tvalue/TValue.Tests.cs b/lib/core/tvalue/TValue.Tests.cs index 2eab88ab..cd2e8ccf 100644 --- a/lib/core/tvalue/TValue.Tests.cs +++ b/lib/core/tvalue/TValue.Tests.cs @@ -1,6 +1,3 @@ -using System; -using Xunit; -using QuanTAlib; namespace QuanTAlib.Tests { diff --git a/lib/feeds/csv/CsvFeed.Tests.cs b/lib/feeds/csv/CsvFeed.Tests.cs index e9656302..a6d26474 100644 --- a/lib/feeds/csv/CsvFeed.Tests.cs +++ b/lib/feeds/csv/CsvFeed.Tests.cs @@ -1,4 +1,3 @@ -using Xunit; namespace QuanTAlib.Tests; diff --git a/lib/feeds/gbm/Gbm.Tests.cs b/lib/feeds/gbm/Gbm.Tests.cs index f79887d4..8837e93c 100644 --- a/lib/feeds/gbm/Gbm.Tests.cs +++ b/lib/feeds/gbm/Gbm.Tests.cs @@ -1,6 +1,3 @@ -using System; -using Xunit; -using QuanTAlib; namespace QuanTAlib.Tests; diff --git a/lib/quantalib.csproj b/lib/quantalib.csproj index 257a4657..cc450824 100644 --- a/lib/quantalib.csproj +++ b/lib/quantalib.csproj @@ -32,17 +32,17 @@ 6afc11a7-4355-4f5e-9fdf-22431e5b03cb - - - - + + + + - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/qodana.yaml b/qodana.yaml new file mode 100644 index 00000000..bd636b87 --- /dev/null +++ b/qodana.yaml @@ -0,0 +1,96 @@ +#-------------------------------------------------------------------------------# +# Qodana analysis is configured by qodana.yaml file # +# https://www.jetbrains.com/help/qodana/qodana-yaml.html # +#-------------------------------------------------------------------------------# +version: "1.0" + +#Specify inspection profile for code analysis +profile: + name: qodana.starter + +#Enable inspections +#include: +# - name: + +#Disable inspections +exclude: + # Disable XML documentation comment validation (allows unescaped < > in comments) + - name: XmlDocAnalyzer + - name: InvalidXmlDocComment + # Library project - public API properties are intentionally unused internally + - name: UnusedAutoPropertyAccessor.Global + # Style preference - fully qualified names used intentionally for clarity + - name: RedundantNameQualifier + - name: RCS1036 # Roslyn: Remove redundant empty line + - name: IDE0001 # Simplify name + - name: IDE0002 # Simplify member access + + # HIGH-PERFORMANCE LIBRARY EXCLUSIONS + # ------------------------------------ + # Flat namespace structure is intentional for this library + - name: CheckNamespace + + # Float comparisons are intentional in financial calculations (checking 0.0, NaN, sentinel values) + - name: CompareOfFloatsByEqualityOperator + + # Explicit default args improve code clarity and self-documentation + - name: RedundantArgumentDefaultValue + + # Platform-specific optimizations (SIMD, intrinsics) are intentional + - name: CA1416 # Validate platform compatibility + + # Public API unused internally - this is a library + - name: UnusedMember.Global + - name: MemberCanBePrivate.Global + - name: ClassNeverInstantiated.Global + - name: UnusedType.Global + + # Redundant using directives - managed by IDE/build, not critical for library + - name: RedundantUsingDirective + - name: IDE0005 # Remove unnecessary using directives + - name: CS8019 # Unnecessary using directive + + # Nullable warning suppressions - used intentionally for null safety patterns + - name: RedundantSuppressNullableWarningExpression + + # Redundant type specifications - explicit types used for clarity/documentation + - name: RedundantTypeArgumentsOfMethod + - name: RedundantCast + - name: RedundantExplicitArrayCreation + + # Unused local variables - often used in test setup or placeholder code (includes false positives for tuple deconstruction) + - name: UnusedVariable + - name: UnusedVariable.Compiler # False positive for tuple deconstruction + - name: CS0219 # Variable is assigned but never used + + # Object initializer in using statement - false positive for simple property setters + - name: CA2000 # Dispose objects before losing scope (overly cautious for simple cases) + - name: UseObjectOrCollectionInitializerWhenPossible + - name: DoNotUseObjectInitializerForUsingVariable + - name: ObjectInitializerMightCauseException + - name: ObjectCreationAsStatement + - name: UseObjectOrCollectionInitializer + - name: UsingStatementResourceInitialization # "Do not use object initializer for 'using' variable" + + # Private field can be local variable - test fixtures often use fields for clarity/organization + - name: PrivateFieldCanBeConvertedToLocalVariable + - name: ConvertToLocalFunction + + # Code coverage checks - SonarCloud handles coverage, Qodana coverage unreliable + - name: CoverageCheck + - name: ClassCoverageCheck + - name: MethodCoverageCheck + - name: CodeCoverageCheck + +#Execute shell command before Qodana execution (Applied in CI/CD pipeline) +#bootstrap: sh ./prepare-qodana.sh + +#Install IDE plugins before Qodana execution (Applied in CI/CD pipeline) +#plugins: +# - id: #(plugin id can be found at https://plugins.jetbrains.com) + +#Specify Qodana linter for analysis (Applied in CI/CD pipeline) +linter: qodana-dotnet + +# Use native mode instead of Docker (avoids Unix socket issues on Windows) +withinDocker: false diff --git a/quantower/Quantower.Tests.csproj b/quantower/Quantower.Tests.csproj index d460e070..2e1ee149 100644 --- a/quantower/Quantower.Tests.csproj +++ b/quantower/Quantower.Tests.csproj @@ -10,6 +10,7 @@ +