context: using System.Linq;
  using System.Threading;
  using Microsoft.Vbe.Interop;
  using Microsoft.VisualStudio.TestTools.UnitTesting;
  using Moq;
  using Rubberduck.Inspections;
  using Rubberduck.Parsing;
  using Rubberduck.Parsing.VBA;
  using Rubberduck.VBEditor.Extensions;
  using Rubberduck.VBEditor.VBEHost;
  using RubberduckTests.Mocks;
  
  namespace RubberduckTests.Inspections
  {
      [TestClass]
      public class ParameterCanBeByValInspectionTests
      {
          [TestMethod]
          [TestCategory("Inspections")]
          public void ParameterCanBeByVal_ReturnsResult_PassedByNotSpecified()
          {
              const string inputCode =
  @"Sub Foo(arg1 As String)
  End Sub";
  
              //Arrange
              var builder = new MockVbeBuilder();
              VBComponent component;
              var vbe = builder.BuildFromSingleStandardModule(inputCode, out component);
              var mockHost = new Mock<IHostApplication>();
              mockHost.SetupAllProperties();
              var parser = MockParser.Create(vbe.Object, new RubberduckParserState(new Mock<ISinks>().Object));
  
              parser.Parse(new CancellationTokenSource());
              if (parser.State.Status >= ParserState.Error) { Assert.Inconclusive("Parser Error"); }
  
              var inspection = new ParameterCanBeByValInspection(parser.State);
              var inspectionResults = inspection.GetInspectionResults();
  
              Assert.AreEqual(1, inspectionResults.Count());
          }
  
          [TestMethod]
          [TestCategory("Inspections")]
          public void ParameterCanBeByVal_ReturnsResult_PassedByRef_Unassigned()
          {
              const string inputCode =
  @"Sub Foo(ByRef arg1 As String)
  End Sub";
  
              //Arrange
              var builder = new MockVbeBuilder();
              VBComponent component;
              var vbe = builder.BuildFromSingleStandardModule(inputCode, out component);
              var mockHost = new Mock<IHostApplication>();
              mockHost.SetupAllProperties();
              var parser = MockParser.Create(vbe.Object, new RubberduckParserState(new Mock<ISinks>().Object));
  
              parser.Parse(new CancellationTokenSource());
              if (parser.State.Status >= ParserState.Error) { Assert.Inconclusive("Parser Error"); }
  
              var inspection = new ParameterCanBeByValInspection(parser.State);
              var inspectionResults = inspection.GetInspectionResults();
  
              Assert.AreEqual(1, inspectionResults.Count());
          }
  
          [TestMethod]
          [TestCategory("Inspections")]
          public void ParameterCanBeByVal_ReturnsResult_Multiple()
          {
              const string inputCode =
  @"Sub Foo(arg1 As String, arg2 As Date)
  End Sub";
  
              //Arrange
              var builder = new MockVbeBuilder();
              VBComponent component;
              var vbe = builder.BuildFromSingleStandardModule(inputCode, out component);
              var mockHost = new Mock<IHostApplication>();
              mockHost.SetupAllProperties();
              var parser = MockParser.Create(vbe.Object, new RubberduckParserState(new Mock<ISinks>().Object));
  
              parser.Parse(new CancellationTokenSource());
              if (parser.State.Status >= ParserState.Error) { Assert.Inconclusive("Parser Error"); }
  
              var inspection = new ParameterCanBeByValInspection(parser.State);
              var inspectionResults = inspection.GetInspectionResults();
  
              Assert.AreEqual(2, inspectionResults.Count());
          }
  
          [TestMethod]
          [TestCategory("Inspections")]
          public void ParameterCanBeByVal_DoesNotReturnResult_PassedByValExplicitly()
          {
              const string inputCode =
  @"Sub Foo(ByVal arg1 As String)
  End Sub";
  
              //Arrange
              var builder = new MockVbeBuilder();
              VBComponent component;
              var vbe = builder.BuildFromSingleStandardModule(inputCode, out component);
              var mockHost = new Mock<IHostApplication>();
              mockHost.SetupAllProperties();
              var parser = MockParser.Create(vbe.Object, new RubberduckParserState(new Mock<ISinks>().Object));
  
              parser.Parse(new CancellationTokenSource());
              if (parser.State.Status >= ParserState.Error) { Assert.Inconclusive("Parser Error"); }
  
              var inspection = new ParameterCanBeByValInspection(parser.State);
              var inspectionResults = inspection.GetInspectionResults();
  
              Assert.AreEqual(0, inspectionResults.Count());
          }
  
          [TestMethod]
          [TestCategory("Inspections")]
          public void ParameterCanBeByVal_DoesNotReturnResult_PassedByRefAndAssigned()
          {
              const string inputCode =
  @"Sub Foo(ByRef arg1 As String)
      arg1 = ""test""
  End Sub";
  
              //Arrange
              var builder = new MockVbeBuilder();
              VBComponent component;
              var vbe = builder.BuildFromSingleStandardModule(inputCode, out component);
              var mockHost = new Mock<IHostApplication>();
              mockHost.SetupAllProperties();
              var parser = MockParser.Create(vbe.Object, new RubberduckParserState(new Mock<ISinks>().Object));
  
              parser.Parse(new CancellationTokenSource());
              if (parser.State.Status >= ParserState.Error) { Assert.Inconclusive("Parser Error"); }
  
              var inspection = new ParameterCanBeByValInspection(parser.State);
              var inspectionResults = inspection.GetInspectionResults();
  
              Assert.AreEqual(0, inspectionResults.Count());
          }
  
          [TestMethod]
          [TestCategory("Inspections")]
          public void ParameterCanBeByVal_DoesNotReturnResult_BuiltInEventParam()
          {
              const string inputCode =
  @"Sub Foo(ByRef arg1 As String)
      arg1 = ""test""
  End Sub";
  
              //Arrange
              var builder = new MockVbeBuilder();
              VBComponent component;
              var vbe = builder.BuildFromSingleStandardModule(inputCode, out component);
              var mockHost = new Mock<IHostApplication>();
              mockHost.SetupAllProperties();
              var parser = MockParser.Create(vbe.Object, new RubberduckParserState(new Mock<ISinks>().Object));
  
              parser.Parse(new CancellationTokenSource());
              if (parser.State.Status >= ParserState.Error) { Assert.Inconclusive("Parser Error"); }
  
              var inspection = new ParameterCanBeByValInspection(parser.State);
              var inspectionResults = inspection.GetInspectionResults();
  
              Assert.AreEqual(0, inspectionResults.Count());
          }
  
          [TestMethod]
          [TestCategory("Inspections")]
          public void ParameterCanBeByVal_ReturnsResult_SomeParams()
          {
              const string inputCode =
  @"Sub Foo(arg1 As String, ByVal arg2 As Integer)
  End Sub";
  
              //Arrange
              var builder = new MockVbeBuilder();
              VBComponent component;
              var vbe = builder.BuildFromSingleStandardModule(inputCode, out component);
              var mockHost = new Mock<IHostApplication>();
              mockHost.SetupAllProperties();
              var parser = MockParser.Create(vbe.Object, new RubberduckParserState(new Mock<ISinks>().Object));
  
              parser.Parse(new CancellationTokenSource());
              if (parser.State.Status >= ParserState.Error) { Assert.Inconclusive("Parser Error"); }
  
              var inspection = new ParameterCanBeByValInspection(parser.State);
              var inspectionResults = inspection.GetInspectionResults();
  
              Assert.AreEqual(1, inspectionResults.Count());
          }
  
          [TestMethod]
          [TestCategory("Inspections")]
          public void GivenArrayParameter_ReturnsNoResult()
          {
              const string inputCode =
  @"Sub Foo(ByRef arg1() As Variant)
  End Sub";
  
              //Arrange
              var builder = new MockVbeBuilder();
              VBComponent component;
              var vbe = builder.BuildFromSingleStandardModule(inputCode, out component);
              var mockHost = new Mock<IHostApplication>();
              mockHost.SetupAllProperties();
              var parser = MockParser.Create(vbe.Object, new RubberduckParserState(new Mock<ISinks>().Object));
  
              parser.Parse(new CancellationTokenSource());
              if (parser.State.Status >= ParserState.Error) { Assert.Inconclusive("Parser Error"); }
  
              var inspection = new ParameterCanBeByValInspection(parser.State);
  
              var results = inspection.GetInspectionResults().ToList();
  
              Assert.AreEqual(0, results.Count);
          }
  
          [TestMethod]
          [TestCategory("Inspections")]
          public void ParameterCanBeByVal_ReturnsResult_PassedToByRefProc_NoAssignment()
          {
              const string inputCode =
  @"Sub DoSomething(foo As Integer)
      DoSomethingElse foo
  End Sub
  
  Sub DoSomethingElse(ByVal bar As Integer)
  End Sub";
  
              //Arrange
              var builder = new MockVbeBuilder();
              VBComponent component;
              var vbe = builder.BuildFromSingleStandardModule(inputCode, out component);
              var mockHost = new Mock<IHostApplication>();
              mockHost.SetupAllProperties();
              var parser = MockParser.Create(vbe.Object, new RubberduckParserState(new Mock<ISinks>().Object));
  
              parser.Parse(new CancellationTokenSource());
              if (parser.State.Status >= ParserState.Error) { Assert.Inconclusive("Parser Error"); }
  
              var inspection = new ParameterCanBeByValInspection(parser.State);
              var inspectionResults = inspection.GetInspectionResults();
  
              Assert.AreEqual(1, inspectionResults.Count());
          }
  
          [TestMethod]
          [TestCategory("Inspections")]
          public void ParameterCanBeByVal_DoesNotReturnResult_PassedToByRefProc_WithAssignment()
          {
              const string inputCode =
  @"Sub DoSomething(foo As Integer)
      DoSomethingElse foo
  End Sub
  
  Sub DoSomethingElse(ByRef bar As Integer)
      bar = 42
  End Sub";
  
              //Arrange
              var builder = new MockVbeBuilder();
              VBComponent component;
              var vbe = builder.BuildFromSingleStandardModule(inputCode, out component);
              var mockHost = new Mock<IHostApplication>();
              mockHost.SetupAllProperties();
              var parser = MockParser.Create(vbe.Object, new RubberduckParserState(new Mock<ISinks>().Object));
  
              parser.Parse(new CancellationTokenSource());
              if (parser.State.Status >= ParserState.Error) { Assert.Inconclusive("Parser Error"); }
  
              var inspection = new ParameterCanBeByValInspection(parser.State);
              var inspectionResults = inspection.GetInspectionResults();
  
              Assert.IsFalse(inspectionResults.Any());
          }
  
          [TestMethod]
          [TestCategory("Inspections")]
          public void ParameterCanBeByVal_ReturnsResult_PassedToByValProc_WithAssignment()
          {
              const string inputCode =
  @"Sub DoSomething(foo As Integer)
      DoSomethingElse foo
  End Sub
  
  Sub DoSomethingElse(ByVal bar As Integer)
      bar = 42
  End Sub";
  
              //Arrange
              var builder = new MockVbeBuilder();
              VBComponent component;
              var vbe = builder.BuildFromSingleStandardModule(inputCode, out component);
              var mockHost = new Mock<IHostApplication>();
              mockHost.SetupAllProperties();
              var parser = MockParser.Create(vbe.Object, new RubberduckParserState(new Mock<ISinks>().Object));
  
              parser.Parse(new CancellationTokenSource());
              if (parser.State.Status >= ParserState.Error) { Assert.Inconclusive("Parser Error"); }
  
              var inspection = new ParameterCanBeByValInspection(parser.State);
              var inspectionResults = inspection.GetInspectionResults();
  
              Assert.AreEqual(1, inspectionResults.Count());
          }
  
          [TestMethod]
          [TestCategory("Inspections")]
          public void ParameterCanBeByVal_Ignored_DoesNotReturnResult()
          {
              const string inputCode =
  @"'@Ignore ParameterCanBeByVal
  Sub Foo(arg1 As String)
  End Sub";
  
              //Arrange
              var builder = new MockVbeBuilder();
              VBComponent component;
              var vbe = builder.BuildFromSingleStandardModule(inputCode, out component);
              var mockHost = new Mock<IHostApplication>();
              mockHost.SetupAllProperties();
              var parser = MockParser.Create(vbe.Object, new RubberduckParserState(new Mock<ISinks>().Object));
  
              parser.Parse(new CancellationTokenSource());
              if (parser.State.Status >= ParserState.Error) { Assert.Inconclusive("Parser Error"); }
  
              var inspection = new ParameterCanBeByValInspection(parser.State);
              var inspectionResults = inspection.GetInspectionResults();
  
              Assert.IsFalse(inspectionResults.Any());
          }
  
          [TestMethod]
          [TestCategory("Inspections")]
          public void ParameterCanBeByVal_InterfaceMember_SingleParam()
          {
              //Input
              const string inputCode1 =
  @"Public Sub DoSomething(ByRef a As Integer)
  End Sub";
              const string inputCode2 =
  @"Implements IClass1
  
  Private Sub IClass1_DoSomething(ByRef a As Integer)
  End Sub";
  
              //Arrange
              var builder = new MockVbeBuilder();
              var project = builder.ProjectBuilder("TestProject1", vbext_ProjectProtection.vbext_pp_none)
                  .AddComponent("IClass1", vbext_ComponentType.vbext_ct_ClassModule, inputCode1)
                  .AddComponent("Class1", vbext_ComponentType.vbext_ct_ClassModule, inputCode2)
                  .AddComponent("Class2", vbext_ComponentType.vbext_ct_ClassModule, inputCode2)
                  .Build();
              var vbe = builder.AddProject(project).Build();
  
              var mockHost = new Mock<IHostApplication>();
              mockHost.SetupAllProperties();
              var parser = MockParser.Create(vbe.Object, new RubberduckParserState(new Mock<ISinks>().Object));
  
              parser.Parse(new CancellationTokenSource());
              if (parser.State.Status >= ParserState.Error) { Assert.Inconclusive("Parser Error"); }
  
              var inspection = new ParameterCanBeByValInspection(parser.State);
              var inspectionResults = inspection.GetInspectionResults();
  
              Assert.AreEqual(1, inspectionResults.Count());
          }
  
          [TestMethod]
          [TestCategory("Inspections")]
          public void ParameterCanBeByVal_InterfaceMember_SingleByValParam()
          {
              //Input
              const string inputCode1 =
  @"Public Sub DoSomething(ByVal a As Integer)
  End Sub";
              const string inputCode2 =
  @"Implements IClass1
  
  Private Sub IClass1_DoSomething(ByVal a As Integer)
  End Sub";
  
              //Arrange
              var builder = new MockVbeBuilder();
              var project = builder.ProjectBuilder("TestProject1", vbext_ProjectProtection.vbext_pp_none)
                  .AddComponent("IClass1", vbext_ComponentType.vbext_ct_ClassModule, inputCode1)
                  .AddComponent("Class1", vbext_ComponentType.vbext_ct_ClassModule, inputCode2)
                  .AddComponent("Class2", vbext_ComponentType.vbext_ct_ClassModule, inputCode2)
                  .Build();
              var vbe = builder.AddProject(project).Build();
  
              var mockHost = new Mock<IHostApplication>();
              mockHost.SetupAllProperties();
              var parser = MockParser.Create(vbe.Object, new RubberduckParserState(new Mock<ISinks>().Object));
  
              parser.Parse(new CancellationTokenSource());
              if (parser.State.Status >= ParserState.Error) { Assert.Inconclusive("Parser Error"); }
  
              var inspection = new ParameterCanBeByValInspection(parser.State);
              var inspectionResults = inspection.GetInspectionResults();
  
              Assert.IsFalse(inspectionResults.Any());
          }
  
          [TestMethod]
          [TestCategory("Inspections")]
          public void ParameterCanBeByVal_InterfaceMember_SingleParamUsedByRef()
          {
              //Input
              const string inputCode1 =
  @"Public Sub DoSomething(ByRef a As Integer)
  End Sub";
              const string inputCode2 =
  @"Implements IClass1
  
  Private Sub IClass1_DoSomething(ByRef a As Integer)
      a = 42
  End Sub";
  
              //Arrange
              var builder = new MockVbeBuilder();
              var project = builder.ProjectBuilder("TestProject1", vbext_ProjectProtection.vbext_pp_none)
                  .AddComponent("IClass1", vbext_ComponentType.vbext_ct_ClassModule, inputCode1)
                  .AddComponent("Class1", vbext_ComponentType.vbext_ct_ClassModule, inputCode2)
                  .AddComponent("Class2", vbext_ComponentType.vbext_ct_ClassModule, inputCode2)
                  .Build();
              var vbe = builder.AddProject(project).Build();
  
              var mockHost = new Mock<IHostApplication>();
              mockHost.SetupAllProperties();
              var parser = MockParser.Create(vbe.Object, new RubberduckParserState(new Mock<ISinks>().Object));
  
              parser.Parse(new CancellationTokenSource());
              if (parser.State.Status >= ParserState.Error) { Assert.Inconclusive("Parser Error"); }
  
              var inspection = new ParameterCanBeByValInspection(parser.State);
  
gt:             var inspectionResults = inspection.GetInspectionResults();