diff --git a/.github/workflows/infer.yml b/.github/workflows/infer.yml new file mode 100644 index 00000000..b9fc9b92 --- /dev/null +++ b/.github/workflows/infer.yml @@ -0,0 +1,36 @@ +name: Infer + +on: + push: + paths-ignore: [ '**.md' ] + branches: [ main ] + pull_request: + paths-ignore: [ '**.md' ] + branches: [ main ] + +jobs: + infer: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Setup .NET Core + uses: actions/setup-dotnet@v2 + with: + dotnet-version: 6.0.x + - name: Install dependencies + run: dotnet restore + - name: Build + run: dotnet build --configuration Release --no-restore + + - name: Run Infer# + uses: microsoft/infersharpaction@v1.4 + id: runinfersharp + with: + binary-path: BitFaster.Caching/bin + + - name: Upload SARIF output to GitHub Security Center + uses: github/codeql-action/upload-sarif@v2 + with: + sarif_file: infer-out/report.sarif \ No newline at end of file diff --git a/BitFaster.Caching.UnitTests/Atomic/AtomicFactoryScopedAsyncCacheTests.cs b/BitFaster.Caching.UnitTests/Atomic/AtomicFactoryScopedAsyncCacheTests.cs index 81d9cae5..e9244c6c 100644 --- a/BitFaster.Caching.UnitTests/Atomic/AtomicFactoryScopedAsyncCacheTests.cs +++ b/BitFaster.Caching.UnitTests/Atomic/AtomicFactoryScopedAsyncCacheTests.cs @@ -67,5 +67,21 @@ public void WhenNoInnerEventsNoOuterEvents() cache.Events.HasValue.Should().BeFalse(); } + + // Infer identified AddOrUpdate and TryUpdate as resource leaks. This test verifies correct disposal. + [Fact] + public void WhenEntryIsUpdatedOldEntryIsDisposed() + { + var disposable1 = new Disposable(); + var disposable2 = new Disposable(); + + this.cache.AddOrUpdate(1, disposable1); + + this.cache.TryUpdate(1, disposable2).Should().BeTrue(); + disposable1.IsDisposed.Should().BeTrue(); + + this.cache.TryUpdate(1, new Disposable()).Should().BeTrue(); + disposable2.IsDisposed.Should().BeTrue(); + } } } diff --git a/BitFaster.Caching.UnitTests/Atomic/AtomicFactoryScopedCacheTests.cs b/BitFaster.Caching.UnitTests/Atomic/AtomicFactoryScopedCacheTests.cs index 1786b7dd..0c6b6b6a 100644 --- a/BitFaster.Caching.UnitTests/Atomic/AtomicFactoryScopedCacheTests.cs +++ b/BitFaster.Caching.UnitTests/Atomic/AtomicFactoryScopedCacheTests.cs @@ -67,5 +67,21 @@ public void WhenNoInnerEventsNoOuterEvents() cache.Events.HasValue.Should().BeFalse(); } + + // Infer identified AddOrUpdate and TryUpdate as resource leaks. This test verifies correct disposal. + [Fact] + public void WhenEntryIsUpdatedOldEntryIsDisposed() + { + var disposable1 = new Disposable(); + var disposable2 = new Disposable(); + + this.cache.AddOrUpdate(1, disposable1); + + this.cache.TryUpdate(1, disposable2).Should().BeTrue(); + disposable1.IsDisposed.Should().BeTrue(); + + this.cache.TryUpdate(1, new Disposable()).Should().BeTrue(); + disposable2.IsDisposed.Should().BeTrue(); + } } } diff --git a/BitFaster.Caching.UnitTests/ScopedTests.cs b/BitFaster.Caching.UnitTests/ScopedTests.cs index 3bfb08ce..d2f1618b 100644 --- a/BitFaster.Caching.UnitTests/ScopedTests.cs +++ b/BitFaster.Caching.UnitTests/ScopedTests.cs @@ -9,6 +9,16 @@ namespace BitFaster.Caching.UnitTests { public class ScopedTests { + [Fact] + public void WhenScopeIsCreatedThenScopeDisposedValueIsDisposed() + { + var disposable = new Disposable(); + var scope = new Scoped(disposable); + + scope.Dispose(); + disposable.IsDisposed.Should().BeTrue(); + } + [Fact] public void WhenScopeIsCreatedThenScopeDisposedLifetimeDisposesValue() {