<commit_before>// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Testing;
using Test.Utilities;
using Xunit;
using VerifyCS = Test.Utilities.CSharpCodeFixVerifier<
    Microsoft.NetCore.Analyzers.Runtime.CallGCSuppressFinalizeCorrectlyAnalyzer,
    Microsoft.NetCore.CSharp.Analyzers.Runtime.CSharpCallGCSuppressFinalizeCorrectlyFixer>;
using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier<
    Microsoft.NetCore.Analyzers.Runtime.CallGCSuppressFinalizeCorrectlyAnalyzer,
    Microsoft.NetCore.VisualBasic.Analyzers.Runtime.BasicCallGCSuppressFinalizeCorrectlyFixer>;

namespace Microsoft.NetCore.Analyzers.Runtime.UnitTests
{
    public class CallGCSuppressFinalizeCorrectlyTests
    {
        private const string GCSuppressFinalizeMethodSignature_CSharp = "GC.SuppressFinalize(object)";
        private const string GCSuppressFinalizeMethodSignature_Basic = "GC.SuppressFinalize(Object)";

        private static DiagnosticResult GetCA1816CSharpResultAt(int line, int column, DiagnosticDescriptor rule, string containingMethodName, string gcSuppressFinalizeMethodName) =>
            VerifyCS.Diagnostic(rule)
                .WithLocation(line, column)
                .WithArguments(containingMethodName, gcSuppressFinalizeMethodName);

        private static DiagnosticResult GetCA1816BasicResultAt(int line, int column, DiagnosticDescriptor rule, string containingMethodName, string gcSuppressFinalizeMethodName) =>
            VerifyVB.Diagnostic(rule)
                .WithLocation(line, column)
                .WithArguments(containingMethodName, gcSuppressFinalizeMethodName);

        #region NoDiagnosticCases

        [Fact]
        public async Task DisposableWithoutFinalizer_CSharp_NoDiagnostic()
        {
            var code = @"
using System;
using System.ComponentModel;

public class DisposableWithoutFinalizer : IDisposable
{
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        Console.WriteLine(this);
        Console.WriteLine(disposing);
    }
}";
            await VerifyCS.VerifyAnalyzerAsync(code);
        }

        [Fact]
        public async Task DisposableWithoutFinalizer_Basic_NoDiagnostic()
        {
            var code = @"
Imports System
Imports System.ComponentModel

Public Class DisposableWithoutFinalizer
	Implements IDisposable
	Public Sub Dispose() Implements IDisposable.Dispose
		Dispose(True)
		GC.SuppressFinalize(Me)
	End Sub

	Protected Overridable Sub Dispose(disposing As Boolean)
		Console.WriteLine(Me)
		Console.WriteLine(disposing)
	End Sub
End Class";
            await VerifyVB.VerifyAnalyzerAsync(code);
        }

        [Fact]
        public async Task DisposableWithFinalizer_CSharp_NoDiagnostic()
        {
            var code = @"
using System;
using System.ComponentModel;

public class DisposableWithFinalizer : IDisposable
{
    ~DisposableWithFinalizer()
    {
        Dispose(false);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        Console.WriteLine(this);
        Console.WriteLine(disposing);
    }
}";
            await VerifyCS.VerifyAnalyzerAsync(code);
        }

        [Fact]
        public async Task DisposableWithFinalizer_Basic_NoDiagnostic()
        {
            var code = @"
Imports System
Imports System.ComponentModel

Public Class DisposableWithFinalizer
	Implements IDisposable
	Protected Overrides Sub Finalize()
		Try
			Dispose(False)
		Finally
			MyBase.Finalize()
		End Try
	End Sub

	Public Sub Dispose() Implements IDisposable.Dispose
		Dispose(True)
		GC.SuppressFinalize(Me)
	End Sub

	Protected Overridable Sub Dispose(disposing As Boolean)
		Console.WriteLine(Me)
		Console.WriteLine(disposing)
	End Sub
End Class";
            await VerifyVB.VerifyAnalyzerAsync(code);
        }

        [Fact]
        public async Task AsyncDisposableWithFinalizer_CSharp_NoDiagnostic()
        {
            var code = @"
using System;
using System.Runtime.InteropServices;
using System.Threading.Tasks;

class MyAsyncDisposable : IAsyncDisposable
{
    [DllImport(""example.dll"")]
    private static extern int GetHandle();

    [DllImport(""example.dll"")]
    private static extern void FreeHandle(int handle);

    private readonly int handle;

    public MyAsyncDisposable()
    {
        this.handle = GetHandle();
    }

    ~MyAsyncDisposable()
    {
        FreeHandle(this.handle);
    }

    public async ValueTask DisposeAsync()
    {
        await Task.Run(() => FreeHandle(this.handle)).ConfigureAwait(false);
        GC.SuppressFinalize(this);
    }
}";
            await new VerifyCS.Test
            {
                ReferenceAssemblies = AdditionalMetadataReferences.DefaultWithAsyncInterfaces,
                TestCode = code
            }.RunAsync();
        }

        [Fact]
        public async Task SealedDisposableWithoutFinalizer_CSharp_NoDiagnostic()
        {

            var code = @"
using System;<commit_msg>Add VB test<commit_after>// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Testing;
using Test.Utilities;
using Xunit;
using VerifyCS = Test.Utilities.CSharpCodeFixVerifier<
    Microsoft.NetCore.Analyzers.Runtime.CallGCSuppressFinalizeCorrectlyAnalyzer,
    Microsoft.NetCore.CSharp.Analyzers.Runtime.CSharpCallGCSuppressFinalizeCorrectlyFixer>;
using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier<
    Microsoft.NetCore.Analyzers.Runtime.CallGCSuppressFinalizeCorrectlyAnalyzer,
    Microsoft.NetCore.VisualBasic.Analyzers.Runtime.BasicCallGCSuppressFinalizeCorrectlyFixer>;

namespace Microsoft.NetCore.Analyzers.Runtime.UnitTests
{
    public class CallGCSuppressFinalizeCorrectlyTests
    {
        private const string GCSuppressFinalizeMethodSignature_CSharp = "GC.SuppressFinalize(object)";
        private const string GCSuppressFinalizeMethodSignature_Basic = "GC.SuppressFinalize(Object)";

        private static DiagnosticResult GetCA1816CSharpResultAt(int line, int column, DiagnosticDescriptor rule, string containingMethodName, string gcSuppressFinalizeMethodName) =>
            VerifyCS.Diagnostic(rule)
                .WithLocation(line, column)
                .WithArguments(containingMethodName, gcSuppressFinalizeMethodName);

        private static DiagnosticResult GetCA1816BasicResultAt(int line, int column, DiagnosticDescriptor rule, string containingMethodName, string gcSuppressFinalizeMethodName) =>
            VerifyVB.Diagnostic(rule)
                .WithLocation(line, column)
                .WithArguments(containingMethodName, gcSuppressFinalizeMethodName);

        #region NoDiagnosticCases

        [Fact]
        public async Task DisposableWithoutFinalizer_CSharp_NoDiagnostic()
        {
            var code = @"
using System;
using System.ComponentModel;

public class DisposableWithoutFinalizer : IDisposable
{
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        Console.WriteLine(this);
        Console.WriteLine(disposing);
    }
}";
            await VerifyCS.VerifyAnalyzerAsync(code);
        }

        [Fact]
        public async Task DisposableWithoutFinalizer_Basic_NoDiagnostic()
        {
            var code = @"
Imports System
Imports System.ComponentModel

Public Class DisposableWithoutFinalizer
	Implements IDisposable
	Public Sub Dispose() Implements IDisposable.Dispose
		Dispose(True)
		GC.SuppressFinalize(Me)
	End Sub

	Protected Overridable Sub Dispose(disposing As Boolean)
		Console.WriteLine(Me)
		Console.WriteLine(disposing)
	End Sub
End Class";
            await VerifyVB.VerifyAnalyzerAsync(code);
        }

        [Fact]
        public async Task DisposableWithFinalizer_CSharp_NoDiagnostic()
        {
            var code = @"
using System;
using System.ComponentModel;

public class DisposableWithFinalizer : IDisposable
{
    ~DisposableWithFinalizer()
    {
        Dispose(false);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        Console.WriteLine(this);
        Console.WriteLine(disposing);
    }
}";
            await VerifyCS.VerifyAnalyzerAsync(code);
        }

        [Fact]
        public async Task DisposableWithFinalizer_Basic_NoDiagnostic()
        {
            var code = @"
Imports System
Imports System.ComponentModel

Public Class DisposableWithFinalizer
	Implements IDisposable
	Protected Overrides Sub Finalize()
		Try
			Dispose(False)
		Finally
			MyBase.Finalize()
		End Try
	End Sub

	Public Sub Dispose() Implements IDisposable.Dispose
		Dispose(True)
		GC.SuppressFinalize(Me)
	End Sub

	Protected Overridable Sub Dispose(disposing As Boolean)
		Console.WriteLine(Me)
		Console.WriteLine(disposing)
	End Sub
End Class";
            await VerifyVB.VerifyAnalyzerAsync(code);
        }

        [Fact]
        public async Task AsyncDisposableWithFinalizer_CSharp_NoDiagnostic()
        {
            var code = @"
using System;
using System.Runtime.InteropServices;
using System.Threading.Tasks;

class MyAsyncDisposable : IAsyncDisposable
{
    [DllImport(""example.dll"")]
    private static extern int GetHandle();

    [DllImport(""example.dll"")]
    private static extern void FreeHandle(int handle);

    private readonly int handle;

    public MyAsyncDisposable()
    {
        this.handle = GetHandle();
    }

    ~MyAsyncDisposable()
    {
        FreeHandle(this.handle);
    }

    public async ValueTask DisposeAsync()
    {
        await Task.Run(() => FreeHandle(this.handle)).ConfigureAwait(false);
        GC.SuppressFinalize(this);
    }
}";
            await new VerifyCS.Test
            {
                ReferenceAssemblies = AdditionalMetadataReferences.DefaultWithAsyncInterfaces,
                TestCode = code
            }.RunAsync();
        }

        [Fact]
        public async Task AsyncDisposableWithFinalizer_Basic_NoDiagnostic()
        {
            var code = @"
Imports System.Runtime.InteropServices
Imports System.Threading.Tasks

Class MyAsyncDisposable
    Inherits IAsyncDisposable

    <DllImport(""example.dll"")>
    Private Shared Function GetHandle() As Integer

    <DllImport(""example.dll"")>
    Private Shared Sub FreeHandle(handle As Integer)

    Private ReadOnly handle As Integer

    Public Sub New()
        Me.handle = GetHandle()
    End Sub

    Protected Overrides Sub Finalize()
        FreeHandle(Me.handle)
    End Sub

    Public Async Function DisposeAsync() As ValueTask
        Await Task.Run(Sub() FreeHandle(Me.handle)).ConfigureAwait(False)
        GC.SuppressFinalize(Me)
    End Function
End Class";
            await new VerifyVB.Test
            {
                ReferenceAssemblies = AdditionalMetadataReferences.DefaultWithAsyncInterfaces,
                TestCode = code
            }.RunAsync();
        }
        
        [Fact]
        public async Task SealedDisposableWithoutFinalizer_CSharp_NoDiagnostic()
        {

            var code = @"
using System;
{'fraction_non_alphanumeric': 0.07405807120636018, 'fraction_numerical': 0.0017283097131005876, 'mean_word_length': 3.066408995080815, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5049314', 'n_tokens_mistral': 3293, 'n_tokens_neox': 3175, 'n_words': 691}