﻿// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#nullable disable

using System;
using System.Globalization;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Test.Utilities;
using Xunit;

namespace Microsoft.CodeAnalysis.UnitTests
{
    public class CommonCommandLineParserTests : TestBase
    {
        private void VerifyCommandLineSplitter(string commandLine, string[] expected, bool removeHashComments = false)
        {
            var actual = CommandLineParser.SplitCommandLineIntoArguments(commandLine, removeHashComments).ToArray();

            Assert.Equal(expected.Length, actual.Length);
            for (int i = 0; i < actual.Length; ++i)
            {
                Assert.Equal(expected[i], actual[i]);
            }
        }

        private RuleSet ParseRuleSet(string source, params string[] otherSources)
        {
            var dir = Temp.CreateDirectory();
            var file = dir.CreateFile("a.ruleset");
            file.WriteAllText(source);

            for (int i = 1; i <= otherSources.Length; i++)
            {
                var newFile = dir.CreateFile("file" + i + ".ruleset");
                newFile.WriteAllText(otherSources[i - 1]);
            }

            if (otherSources.Length != 0)
            {
                return RuleSet.LoadEffectiveRuleSetFromFile(file.Path);
            }

            return RuleSetProcessor.LoadFromFile(file.Path);
        }

        private void VerifyRuleSetError(string source, Func<string> messageFormatter, bool locSpecific = true, params string[] otherSources)
        {
            CultureInfo saveUICulture = Thread.CurrentThread.CurrentUICulture;

            if (locSpecific)
            {
                var preferred = EnsureEnglishUICulture.PreferredOrNull;
                if (preferred == null)
                {
                    locSpecific = false;
                }
                else
                {
                    Thread.CurrentThread.CurrentUICulture = preferred;
                }
            }

            try
            {
                ParseRuleSet(source, otherSources);
            }
            catch (Exception e)
            {
                Assert.Equal(messageFormatter(), e.Message);
                return;
            }
            finally
            {
                if (locSpecific)
                {
                    Thread.CurrentThread.CurrentUICulture = saveUICulture;
                }
            }

            Assert.True(false, "Didn't return an error");
        }

        [Fact]
        public void TestCommandLineSplitter()
        {
            VerifyCommandLineSplitter("", new string[0]);
            VerifyCommandLineSplitter("   \t   ", new string[0]);
            VerifyCommandLineSplitter("   abc\tdef baz    quuz   ", new[] { "abc", "def", "baz", "quuz" });
            VerifyCommandLineSplitter(@"  ""abc def""  fi""ddle dee de""e  ""hi there ""dude  he""llo there""  ",
                                        new string[] { @"abc def", @"fi""ddle dee de""e", @"""hi there ""dude", @"he""llo there""" });
            VerifyCommandLineSplitter(@"  ""abc def \"" baz quuz"" ""\""straw berry"" fi\""zz \""buzz fizzbuzz",
                                        new string[] { @"abc def \"" baz quuz", @"\""straw berry", @"fi\""zz", @"\""buzz", @"fizzbuzz" });
            VerifyCommandLineSplitter(@"  \\""abc def""  \\\""abc def"" ",
                                        new string[] { @"\\""abc def""", @"\\\""abc", @"def"" " });
            VerifyCommandLineSplitter(@"  \\\\""abc def""  \\\\\""abc def"" ",
                                        new string[] { @"\\\\""abc def""", @"\\\\\""abc", @"def"" " });
            VerifyCommandLineSplitter(@"  \\\\""abc def""  \\\\\""abc def"" q a r ",
                                        new string[] { @"\\\\""abc def""", @"\\\\\""abc", @"def"" q a r " });
            VerifyCommandLineSplitter(@"abc #Comment ignored",
                                        new string[] { @"abc" }, removeHashComments: true);
            VerifyCommandLineSplitter(@"""goo bar"";""baz"" ""tree""",
                                        new string[] { @"""goo bar"";""baz""", "tree" });
            VerifyCommandLineSplitter(@"/reference:""a, b"" ""test""",
                                        new string[] { @"/reference:""a, b""", "test" });
            VerifyCommandLineSplitter(@"fo""o ba""r",
                                        new string[] { @"fo""o ba""r" });
        }

        [Fact]
        public void TestRuleSetParsingDuplicateRule()
        {
            string source = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test""  ToolsVersion=""12.0"">
  <IncludeAll Action=""Warning"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Error"" />
    <Rule Id=""CA1012"" Action=""Warning"" />
    <Rule Id=""CA1013"" Action=""Warning"" />
    <Rule Id=""CA1014"" Action=""None"" />
  </Rules>
</RuleSet>";

            VerifyRuleSetError(source, () => string.Format(CodeAnalysisResources.RuleSetHasDuplicateRules, "CA1012", "Error", "Warn"));
        }

        [Fact]
        public void TestRuleSetParsingDuplicateRule2()
        {
            string source = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test""  ToolsVersion=""12.0"">
  <IncludeAll Action=""Warning"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Error"" />
    <Rule Id=""CA1014"" Action=""None"" />
  </Rules>
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Warning"" />
    <Rule Id=""CA1013"" Action=""None"" />
  </Rules>
</RuleSet>";

            VerifyRuleSetError(source, () => string.Format(CodeAnalysisResources.RuleSetHasDuplicateRules, "CA1012", "Error", "Warn"), locSpecific: false);
        }

        [Fact]
        public void TestRuleSetParsingDuplicateRule3()
        {
            string source = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test""  ToolsVersion=""12.0"">
  <IncludeAll Action=""Warning"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Error"" />
    <Rule Id=""CA1014"" Action=""None"" />
  </Rules>
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Error"" />
    <Rule Id=""CA1013"" Action=""None"" />
  </Rules>
</RuleSet>";

            var ruleSet = ParseRuleSet(source);
            Assert.Equal(expected: ReportDiagnostic.Error, actual: ruleSet.SpecificDiagnosticOptions["CA1012"]);
        }

        [Fact]
        public void TestRuleSetParsingDuplicateRuleSet()
        {
            string source = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test"">
  <IncludeAll Action=""Warning"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Error"" />
  </Rules>
</RuleSet>
<RuleSet Name=""Ruleset2"" Description=""Test"">
  <IncludeAll Action=""Warning"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Error"" />
  </Rules>
</RuleSet>
";
            VerifyRuleSetError(source, () => "There are multiple root elements. Line 8, position 2.");
        }

        [Fact]
        public void TestRuleSetParsingIncludeAll1()
        {
            string source = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test""  ToolsVersion=""12.0"">
  <IncludeAll Action=""Warning"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Error"" />
  </Rules>
</RuleSet>
";
            var ruleSet = ParseRuleSet(source);
            Assert.Equal(ReportDiagnostic.Warn, ruleSet.GeneralDiagnosticOption);
        }

        [Fact]
        public void TestRuleSetParsingIncludeAll2()
        {
            string source = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test""  ToolsVersion=""12.0"">
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Error"" />
  </Rules>
</RuleSet>
";
            var ruleSet = ParseRuleSet(source);
            Assert.Equal(ReportDiagnostic.Default, ruleSet.GeneralDiagnosticOption);
        }

        [Fact]
        public void TestRuleSetParsingWithIncludeOfSameFile()
        {
            string source = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test""  ToolsVersion=""12.0"">
  <Include Path=""a.ruleset"" Action=""Warning"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Error"" />
  </Rules>
</RuleSet>
";
            var ruleSet = ParseRuleSet(source, new string[] { "" });
            Assert.Equal(ReportDiagnostic.Default, ruleSet.GeneralDiagnosticOption);
            Assert.Equal(1, RuleSet.GetEffectiveIncludesFromFile(ruleSet.FilePath).Count());
        }

        [Fact]
        public void TestRuleSetParsingWithMutualIncludes()
        {
            string source = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test""  ToolsVersion=""12.0"">
  <Include Path=""file1.ruleset"" Action=""Warning"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Error"" />
  </Rules>
</RuleSet>
";

            string source1 = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test""  ToolsVersion=""12.0"">
  <Include Path=""a.ruleset"" Action=""Warning"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Error"" />
  </Rules>
</RuleSet>
";
            var ruleSet = ParseRuleSet(source, source1);
            Assert.Equal(ReportDiagnostic.Default, ruleSet.GeneralDiagnosticOption);
            Assert.Equal(2, RuleSet.GetEffectiveIncludesFromFile(ruleSet.FilePath).Count());
        }

        [Fact]
        public void TestRuleSetParsingWithSiblingIncludes()
        {
            string source = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test""  ToolsVersion=""12.0"">
  <Include Path=""file1.ruleset"" Action=""Warning"" />
  <Include Path=""file2.ruleset"" Action=""Warning"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Error"" />
  </Rules>
</RuleSet>
";

            string source1 = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test""  ToolsVersion=""12.0"">
  <Include Path=""file2.ruleset"" Action=""Warning"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Error"" />
  </Rules>
</RuleSet>
";

            string source2 = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test""  ToolsVersion=""12.0"">
  <Include Path=""file1.ruleset"" Action=""Warning"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Error"" />
  </Rules>
</RuleSet>
";
            var ruleSet = ParseRuleSet(source, source1, source2);
            Assert.Equal(ReportDiagnostic.Default, ruleSet.GeneralDiagnosticOption);
            Assert.Equal(3, RuleSet.GetEffectiveIncludesFromFile(ruleSet.FilePath).Count());
        }

        [Fact]
        public void TestRuleSetParsingIncludeAll3()
        {
            string source = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test""  ToolsVersion=""12.0"">
  <IncludeAll Action=""Default"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Error"" />
  </Rules>
</RuleSet>
";
            VerifyRuleSetError(source, () => string.Format(CodeAnalysisResources.RuleSetBadAttributeValue, "Action", "Default"));
        }

        [Fact]
        public void TestRuleSetParsingRulesMissingAttribute1()
        {
            string source = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test""  ToolsVersion=""12.0"">
  <IncludeAll Action=""Warning"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Action=""Error"" />
  </Rules>
</RuleSet>
";
            VerifyRuleSetError(source, () => string.Format(CodeAnalysisResources.RuleSetMissingAttribute, "Rule", "Id"));
        }

        [Fact]
        public void TestRuleSetParsingRulesMissingAttribute2()
        {
            string source = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test""  ToolsVersion=""12.0"">
  <IncludeAll Action=""Warning"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" />
  </Rules>
</RuleSet>
";
            VerifyRuleSetError(source, () => string.Format(CodeAnalysisResources.RuleSetMissingAttribute, "Rule", "Action"));
        }

        [Fact]
        public void TestRuleSetParsingRulesMissingAttribute3()
        {
            string source = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test""  ToolsVersion=""12.0"">
  <IncludeAll Action=""Warning"" />
  <Rules RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Error"" />
  </Rules>
</RuleSet>
";
            VerifyRuleSetError(source, () => string.Format(CodeAnalysisResources.RuleSetMissingAttribute, "Rules", "AnalyzerId"));
        }

        [Fact]
        public void TestRuleSetParsingRulesMissingAttribute4()
        {
            string source = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test""  ToolsVersion=""12.0"">
  <IncludeAll Action=""Warning"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"">
    <Rule Id=""CA1012"" Action=""Error"" />
  </Rules>
</RuleSet>
";
            VerifyRuleSetError(source, () => string.Format(CodeAnalysisResources.RuleSetMissingAttribute, "Rules", "RuleNamespace"));
        }

        [Fact]
        public void TestRuleSetParsingRulesMissingAttribute5()
        {
            string source = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test"" >
  <IncludeAll Action=""Warning"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Error"" />
  </Rules>
</RuleSet>
";

            VerifyRuleSetError(source, () => string.Format(CodeAnalysisResources.RuleSetMissingAttribute, "RuleSet", "ToolsVersion"));
        }

        [Fact]
        public void TestRuleSetParsingRulesMissingAttribute6()
        {
            string source = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Description=""Test"" ToolsVersion=""12.0"" >
  <IncludeAll Action=""Warning"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Error"" />
  </Rules>
</RuleSet>
";
            VerifyRuleSetError(source, () => string.Format(CodeAnalysisResources.RuleSetMissingAttribute, "RuleSet", "Name"));
        }

        [Fact]
        public void TestRuleSetParsingRules()
        {
            string source = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test"" ToolsVersion=""12.0"" >
  <IncludeAll Action=""Warning"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Error"" />
    <Rule Id=""CA1013"" Action=""Warning"" />
    <Rule Id=""CA1014"" Action=""None"" />
    <Rule Id=""CA1015"" Action=""Info"" />
    <Rule Id=""CA1016"" Action=""Hidden"" />
  </Rules>
</RuleSet>
";
            var ruleSet = ParseRuleSet(source);
            Assert.Contains("CA1012", ruleSet.SpecificDiagnosticOptions.Keys);
            Assert.Equal(ReportDiagnostic.Error, ruleSet.SpecificDiagnosticOptions["CA1012"]);
            Assert.Contains("CA1013", ruleSet.SpecificDiagnosticOptions.Keys);
            Assert.Equal(ReportDiagnostic.Warn, ruleSet.SpecificDiagnosticOptions["CA1013"]);
            Assert.Contains("CA1014", ruleSet.SpecificDiagnosticOptions.Keys);
            Assert.Equal(ReportDiagnostic.Suppress, ruleSet.SpecificDiagnosticOptions["CA1014"]);
            Assert.Contains("CA1015", ruleSet.SpecificDiagnosticOptions.Keys);
            Assert.Equal(ReportDiagnostic.Info, ruleSet.SpecificDiagnosticOptions["CA1015"]);
            Assert.Contains("CA1016", ruleSet.SpecificDiagnosticOptions.Keys);
            Assert.Equal(ReportDiagnostic.Hidden, ruleSet.SpecificDiagnosticOptions["CA1016"]);
        }

        [Fact]
        public void TestRuleSetParsingRules2()
        {
            string source = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test"" ToolsVersion=""12.0"" >
  <IncludeAll Action=""Warning"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Default"" />
    <Rule Id=""CA1013"" Action=""Warning"" />
    <Rule Id=""CA1014"" Action=""None"" />
  </Rules>
</RuleSet>
";

            VerifyRuleSetError(source, () => string.Format(CodeAnalysisResources.RuleSetBadAttributeValue, "Action", "Default"));
        }

        [Fact]
        public void TestRuleSetInclude()
        {
            string source = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test"" ToolsVersion=""12.0"" >
  <Include Path=""goo.ruleset"" Action=""Default"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1013"" Action=""Warning"" />
  </Rules>
</RuleSet>
";
            var ruleSet = ParseRuleSet(source);
            Assert.True(ruleSet.Includes.Count() == 1);
            Assert.Equal(ReportDiagnostic.Default, ruleSet.Includes.First().Action);
            Assert.Equal("goo.ruleset", ruleSet.Includes.First().IncludePath);
        }

#pragma warning disable CA2243 // Attribute string literals should parse correctly
        [WorkItem(1184500, "DevDiv 1184500")]
#pragma warning restore CA2243 // Attribute string literals should parse correctly
        [Fact]
        public void TestRuleSetInclude1()
        {
            string source = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test"" ToolsVersion=""12.0"" >
  <Include Path=""goo.ruleset"" Action=""Default"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1013"" Action=""Warning"" />
  </Rules>
</RuleSet>
";
            var dir = Temp.CreateDirectory();
            var file = dir.CreateFile("a.ruleset");
            file.WriteAllText(source);

            var ruleSet = RuleSet.LoadEffectiveRuleSetFromFile(file.Path);

            Assert.Equal(ReportDiagnostic.Default, ruleSet.GeneralDiagnosticOption);
            Assert.Contains("CA1013", ruleSet.SpecificDiagnosticOptions.Keys);
            Assert.Equal(ReportDiagnostic.Warn, ruleSet.SpecificDiagnosticOptions["CA1013"]);
        }

        [Fact]
        public void TestRuleSetInclude2()
        {
            string source = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test"" ToolsVersion=""12.0"" >
  <Include Path=""file1.ruleset"" Action=""Default"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Warning"" />
  </Rules>
</RuleSet>
";

            string source1 = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset2"" Description=""Test"" ToolsVersion=""12.0"" >
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1013"" Action=""Warning"" />
  </Rules>
</RuleSet>
";
            var ruleSet = ParseRuleSet(source, source1);
            Assert.Equal(ReportDiagnostic.Default, ruleSet.GeneralDiagnosticOption);
            Assert.Contains("CA1012", ruleSet.SpecificDiagnosticOptions.Keys);
            Assert.Equal(ReportDiagnostic.Warn, ruleSet.SpecificDiagnosticOptions["CA1012"]);
            Assert.Contains("CA1013", ruleSet.SpecificDiagnosticOptions.Keys);
            Assert.Equal(ReportDiagnostic.Warn, ruleSet.SpecificDiagnosticOptions["CA1013"]);
        }

        [Fact]
        public void TestRuleSetIncludeGlobalStrict()
        {
            string source = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test"" ToolsVersion=""12.0"" >
  <Include Path=""file1.ruleset"" Action=""Default"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Warning"" />
  </Rules>
</RuleSet>
";

            string source1 = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset2"" Description=""Test"" ToolsVersion=""12.0"" >
  <IncludeAll Action=""Hidden"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1013"" Action=""Warning"" />
  </Rules>
</RuleSet>
";
            var ruleSet = ParseRuleSet(source, source1);
            Assert.Equal(ReportDiagnostic.Hidden, ruleSet.GeneralDiagnosticOption);
            Assert.Contains("CA1012", ruleSet.SpecificDiagnosticOptions.Keys);
            Assert.Equal(ReportDiagnostic.Warn, ruleSet.SpecificDiagnosticOptions["CA1012"]);
            Assert.Contains("CA1013", ruleSet.SpecificDiagnosticOptions.Keys);
            Assert.Equal(ReportDiagnostic.Warn, ruleSet.SpecificDiagnosticOptions["CA1013"]);
        }

        [Fact]
        public void TestRuleSetIncludeGlobalStrict1()
        {
            string source = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test"" ToolsVersion=""12.0"" >
  <IncludeAll Action=""Info"" />
  <Include Path=""file1.ruleset"" Action=""Default"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Warning"" />
  </Rules>
</RuleSet>
";

            string source1 = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset2"" Description=""Test"" ToolsVersion=""12.0"" >
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1013"" Action=""Warning"" />
  </Rules>
</RuleSet>
";
            var ruleSet = ParseRuleSet(source, source1);
            Assert.Equal(ReportDiagnostic.Info, ruleSet.GeneralDiagnosticOption);
            Assert.Contains("CA1012", ruleSet.SpecificDiagnosticOptions.Keys);
            Assert.Equal(ReportDiagnostic.Warn, ruleSet.SpecificDiagnosticOptions["CA1012"]);
            Assert.Contains("CA1013", ruleSet.SpecificDiagnosticOptions.Keys);
            Assert.Equal(ReportDiagnostic.Warn, ruleSet.SpecificDiagnosticOptions["CA1013"]);
        }

        [Fact]
        public void TestRuleSetIncludeGlobalStrict2()
        {
            string source = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test"" ToolsVersion=""12.0"" >
  <IncludeAll Action=""Warning"" />
  <Include Path=""file1.ruleset"" Action=""Default"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Warning"" />
  </Rules>
</RuleSet>
";

            string source1 = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset2"" Description=""Test"" ToolsVersion=""12.0"" >
  <IncludeAll Action=""Error"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1013"" Action=""Warning"" />
  </Rules>
</RuleSet>
";
            var ruleSet = ParseRuleSet(source, source1);
            Assert.Equal(ReportDiagnostic.Error, ruleSet.GeneralDiagnosticOption);
            Assert.Contains("CA1012", ruleSet.SpecificDiagnosticOptions.Keys);
            Assert.Equal(ReportDiagnostic.Warn, ruleSet.SpecificDiagnosticOptions["CA1012"]);
            Assert.Contains("CA1013", ruleSet.SpecificDiagnosticOptions.Keys);
            Assert.Equal(ReportDiagnostic.Warn, ruleSet.SpecificDiagnosticOptions["CA1013"]);
        }

        [Fact]
        public void TestRuleSetIncludeGlobalStrict3()
        {
            string source = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test"" ToolsVersion=""12.0"" >
  <IncludeAll Action=""Warning"" />
  <Include Path=""file1.ruleset"" Action=""Error"" />
  <Include Path=""file2.ruleset"" Action=""Default"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Warning"" />
  </Rules>
</RuleSet>
";

            string source1 = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset2"" Description=""Test"" ToolsVersion=""12.0"" >
  <IncludeAll Action=""Error"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1013"" Action=""Warning"" />
  </Rules>
</RuleSet>
";
            string source2 = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset2"" Description=""Test"" ToolsVersion=""12.0"" >
  <IncludeAll Action=""Warning"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1013"" Action=""Warning"" />
  </Rules>
</RuleSet>
";
            var ruleSet = ParseRuleSet(source, source1, source2);
            Assert.Equal(ReportDiagnostic.Error, ruleSet.GeneralDiagnosticOption);
            Assert.Contains("CA1012", ruleSet.SpecificDiagnosticOptions.Keys);
            Assert.Equal(ReportDiagnostic.Warn, ruleSet.SpecificDiagnosticOptions["CA1012"]);
            Assert.Contains("CA1013", ruleSet.SpecificDiagnosticOptions.Keys);
            Assert.Equal(ReportDiagnostic.Error, ruleSet.SpecificDiagnosticOptions["CA1013"]);
        }

        [Fact]
        public void TestRuleSetIncludeRecursiveIncludes()
        {
            string source = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test"" ToolsVersion=""12.0"" >
  <IncludeAll Action=""Warning"" />
  <Include Path=""file1.ruleset"" Action=""Default"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Warning"" />
  </Rules>
</RuleSet>
";

            string source1 = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset2"" Description=""Test"" ToolsVersion=""12.0"" >
  <IncludeAll Action=""Error"" />
  <Include Path=""file2.ruleset"" Action=""Default"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1013"" Action=""Warning"" />
  </Rules>
</RuleSet>
";
            string source2 = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset2"" Description=""Test"" ToolsVersion=""12.0"" >
  <IncludeAll Action=""Warning"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1014"" Action=""Warning"" />
  </Rules>
</RuleSet>
";
            var ruleSet = ParseRuleSet(source, source1, source2);
            Assert.Equal(ReportDiagnostic.Error, ruleSet.GeneralDiagnosticOption);
            Assert.Contains("CA1012", ruleSet.SpecificDiagnosticOptions.Keys);
            Assert.Equal(ReportDiagnostic.Warn, ruleSet.SpecificDiagnosticOptions["CA1012"]);
            Assert.Contains("CA1013", ruleSet.SpecificDiagnosticOptions.Keys);
            Assert.Equal(ReportDiagnostic.Warn, ruleSet.SpecificDiagnosticOptions["CA1013"]);
            Assert.Contains("CA1014", ruleSet.SpecificDiagnosticOptions.Keys);
            Assert.Equal(ReportDiagnostic.Warn, ruleSet.SpecificDiagnosticOptions["CA1014"]);
        }

        [Fact]
        public void TestRuleSetIncludeSpecificStrict1()
        {
            string source = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test"" ToolsVersion=""12.0"" >
  <Include Path=""file1.ruleset"" Action=""Default"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Warning"" />
  </Rules>
</RuleSet>
";

            string source1 = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset2"" Description=""Test"" ToolsVersion=""12.0"" >
  <IncludeAll Action=""Error"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Error"" />
  </Rules>
</RuleSet>
";
            var ruleSet = ParseRuleSet(source, source1);
            // CA1012's value in source wins.
            Assert.Contains("CA1012", ruleSet.SpecificDiagnosticOptions.Keys);
            Assert.Equal(ReportDiagnostic.Warn, ruleSet.SpecificDiagnosticOptions["CA1012"]);
        }

        [Fact]
        public void TestRuleSetIncludeSpecificStrict2()
        {
            string source = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test"" ToolsVersion=""12.0"" >
  <Include Path=""file1.ruleset"" Action=""Default"" />
  <Include Path=""file2.ruleset"" Action=""Default"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Warning"" />
  </Rules>
</RuleSet>
";

            string source1 = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset2"" Description=""Test"" ToolsVersion=""12.0"" >
  <IncludeAll Action=""Error"" />
  <Include Path=""file2.ruleset"" Action=""Default"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Error"" />
  </Rules>
</RuleSet>
";
            string source2 = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset2"" Description=""Test"" ToolsVersion=""12.0"" >
  <IncludeAll Action=""Error"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Warning"" />
  </Rules>
</RuleSet>
";
            var ruleSet = ParseRuleSet(source, source1, source2);
            // CA1012's value in source still wins.
            Assert.Contains("CA1012", ruleSet.SpecificDiagnosticOptions.Keys);
            Assert.Equal(ReportDiagnostic.Warn, ruleSet.SpecificDiagnosticOptions["CA1012"]);
        }

        [Fact]
        public void TestRuleSetIncludeSpecificStrict3()
        {
            string source = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test"" ToolsVersion=""12.0"" >
  <Include Path=""file1.ruleset"" Action=""Default"" />
  <Include Path=""file2.ruleset"" Action=""Default"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Warning"" />
  </Rules>
</RuleSet>
";

            string source1 = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset2"" Description=""Test"" ToolsVersion=""12.0"" >
  <IncludeAll Action=""Error"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1013"" Action=""Warning"" />
  </Rules>
</RuleSet>
";
            string source2 = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset2"" Description=""Test"" ToolsVersion=""12.0"" >
  <IncludeAll Action=""Error"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1013"" Action=""Error"" />
  </Rules>
</RuleSet>
";
            var ruleSet = ParseRuleSet(source, source1, source2);
            Assert.Contains("CA1012", ruleSet.SpecificDiagnosticOptions.Keys);
            Assert.Equal(ReportDiagnostic.Warn, ruleSet.SpecificDiagnosticOptions["CA1012"]);
            // CA1013's value in source2 wins.
            Assert.Contains("CA1013", ruleSet.SpecificDiagnosticOptions.Keys);
            Assert.Equal(ReportDiagnostic.Error, ruleSet.SpecificDiagnosticOptions["CA1013"]);
        }

        [Fact]
        public void TestRuleSetIncludeEffectiveAction()
        {
            string source = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test"" ToolsVersion=""12.0"" >
  <Include Path=""file1.ruleset"" Action=""None"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Warning"" />
  </Rules>
</RuleSet>
";
            string source1 = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset2"" Description=""Test"" ToolsVersion=""12.0"" >
  <IncludeAll Action=""Error"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1013"" Action=""Warning"" />
  </Rules>
</RuleSet>
";
            var ruleSet = ParseRuleSet(source, source1);
            Assert.Contains("CA1012", ruleSet.SpecificDiagnosticOptions.Keys);
            Assert.Equal(ReportDiagnostic.Warn, ruleSet.SpecificDiagnosticOptions["CA1012"]);
            Assert.DoesNotContain("CA1013", ruleSet.SpecificDiagnosticOptions.Keys);
        }

        [Fact]
        public void TestRuleSetIncludeEffectiveAction1()
        {
            string source = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test"" ToolsVersion=""12.0"" >
  <Include Path=""file1.ruleset"" Action=""Error"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Warning"" />
  </Rules>
</RuleSet>
";
            string source1 = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset2"" Description=""Test"" ToolsVersion=""12.0"" >
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1013"" Action=""Warning"" />
  </Rules>
</RuleSet>
";
            var ruleSet = ParseRuleSet(source, source1);
            Assert.Contains("CA1012", ruleSet.SpecificDiagnosticOptions.Keys);
            Assert.Equal(ReportDiagnostic.Warn, ruleSet.SpecificDiagnosticOptions["CA1012"]);
            Assert.Contains("CA1013", ruleSet.SpecificDiagnosticOptions.Keys);
            Assert.Equal(ReportDiagnostic.Error, ruleSet.SpecificDiagnosticOptions["CA1013"]);
            Assert.Equal(ReportDiagnostic.Default, ruleSet.GeneralDiagnosticOption);
        }

        [Fact]
        public void TestRuleSetIncludeEffectiveActionGlobal1()
        {
            string source = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test"" ToolsVersion=""12.0"" >
  <Include Path=""file1.ruleset"" Action=""Error"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Warning"" />
  </Rules>
</RuleSet>
";
            string source1 = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset2"" Description=""Test"" ToolsVersion=""12.0"" >
  <IncludeAll Action=""Warning"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1013"" Action=""Warning"" />
  </Rules>
</RuleSet>
";
            var ruleSet = ParseRuleSet(source, source1);
            Assert.Equal(ReportDiagnostic.Error, ruleSet.GeneralDiagnosticOption);
        }

        [Fact]
        public void TestRuleSetIncludeEffectiveActionGlobal2()
        {
            string source = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test"" ToolsVersion=""12.0"" >
  <Include Path=""file1.ruleset"" Action=""Warning"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Warning"" />
  </Rules>
</RuleSet>
";
            string source1 = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset2"" Description=""Test"" ToolsVersion=""12.0"" >
  <IncludeAll Action=""Error"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1013"" Action=""Warning"" />
  </Rules>
</RuleSet>
";
            var ruleSet = ParseRuleSet(source, source1);
            Assert.Equal(ReportDiagnostic.Warn, ruleSet.GeneralDiagnosticOption);
        }

        [Fact]
        public void TestRuleSetIncludeEffectiveActionSpecific1()
        {
            string source = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test"" ToolsVersion=""12.0"" >
  <Include Path=""file1.ruleset"" Action=""Warning"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Warning"" />
  </Rules>
</RuleSet>
";
            string source1 = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset2"" Description=""Test"" ToolsVersion=""12.0"" >
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1013"" Action=""None"" />
  </Rules>
</RuleSet>
";
            var ruleSet = ParseRuleSet(source, source1);
            Assert.Contains("CA1012", ruleSet.SpecificDiagnosticOptions.Keys);
            Assert.Equal(ReportDiagnostic.Warn, ruleSet.SpecificDiagnosticOptions["CA1012"]);
            Assert.Contains("CA1013", ruleSet.SpecificDiagnosticOptions.Keys);
            Assert.Equal(ReportDiagnostic.Suppress, ruleSet.SpecificDiagnosticOptions["CA1013"]);
        }

        [Fact]
        public void TestRuleSetIncludeEffectiveActionSpecific2()
        {
            string source = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test"" ToolsVersion=""12.0"" >
  <Include Path=""file1.ruleset"" Action=""Error"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Warning"" />
  </Rules>
</RuleSet>
";
            string source1 = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset2"" Description=""Test"" ToolsVersion=""12.0"" >
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1013"" Action=""Warning"" />
  </Rules>
</RuleSet>
";
            var ruleSet = ParseRuleSet(source, source1);
            Assert.Contains("CA1012", ruleSet.SpecificDiagnosticOptions.Keys);
            Assert.Equal(ReportDiagnostic.Warn, ruleSet.SpecificDiagnosticOptions["CA1012"]);
            Assert.Contains("CA1013", ruleSet.SpecificDiagnosticOptions.Keys);
            Assert.Equal(ReportDiagnostic.Error, ruleSet.SpecificDiagnosticOptions["CA1013"]);
        }

        [Fact]
        public void TestAllCombinations()
        {
            string source = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""New Rule Set1"" Description=""Test"" ToolsVersion=""12.0"">
  <Include Path=""file1.ruleset"" Action=""Error"" />
  <Include Path=""file2.ruleset"" Action=""Warning"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1000"" Action=""Warning"" />
    <Rule Id=""CA1001"" Action=""Warning"" />
    <Rule Id=""CA2111"" Action=""None"" />
  </Rules>
</RuleSet>
";

            string source1 = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""New Rule Set2"" Description=""Test"" ToolsVersion=""12.0"">
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA2100"" Action=""Warning"" />
    <Rule Id=""CA2111"" Action=""Warning"" />
  </Rules>
</RuleSet>
";
            string source2 = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""New Rule Set3"" Description=""Test"" ToolsVersion=""12.0"">
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA2100"" Action=""Warning"" />
    <Rule Id=""CA2111"" Action=""Warning"" />
    <Rule Id=""CA2119"" Action=""None"" />
    <Rule Id=""CA2104"" Action=""Error"" />
    <Rule Id=""CA2105"" Action=""Warning"" />
  </Rules>
</RuleSet>";

            var ruleSet = ParseRuleSet(source, source1, source2);
            Assert.Equal(ReportDiagnostic.Warn, ruleSet.SpecificDiagnosticOptions["CA1000"]);
            Assert.Equal(ReportDiagnostic.Warn, ruleSet.SpecificDiagnosticOptions["CA1001"]);
            Assert.Equal(ReportDiagnostic.Error, ruleSet.SpecificDiagnosticOptions["CA2100"]);
            Assert.Equal(ReportDiagnostic.Warn, ruleSet.SpecificDiagnosticOptions["CA2104"]);
            Assert.Equal(ReportDiagnostic.Warn, ruleSet.SpecificDiagnosticOptions["CA2105"]);
            Assert.Equal(ReportDiagnostic.Suppress, ruleSet.SpecificDiagnosticOptions["CA2111"]);
            Assert.Equal(ReportDiagnostic.Suppress, ruleSet.SpecificDiagnosticOptions["CA2119"]);
        }

        [Fact]
        public void TestRuleSetIncludeError()
        {
            string source = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test"" ToolsVersion=""12.0"" >
  <Include Path=""file1.ruleset"" Action=""Error"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Warning"" />
  </Rules>
</RuleSet>
";
            string source1 = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset2"" Description=""Test"" ToolsVersion=""12.0"" >
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1013"" Action=""Default"" />
  </Rules>
</RuleSet>
";
            var dir = Temp.CreateDirectory();
            var file = dir.CreateFile("a.ruleset");
            file.WriteAllText(source);
            var newFile = dir.CreateFile("file1.ruleset");
            newFile.WriteAllText(source1);

            using (new EnsureEnglishUICulture())
            {
                try
                {
                    RuleSet.LoadEffectiveRuleSetFromFile(file.Path);
                    Assert.True(false, "Didn't throw an exception");
                }
                catch (InvalidRuleSetException e)
                {
                    Assert.Contains(string.Format(CodeAnalysisResources.InvalidRuleSetInclude, newFile.Path, string.Format(CodeAnalysisResources.RuleSetBadAttributeValue, "Action", "Default")), e.Message, StringComparison.Ordinal);
                }
            }
        }

        [Fact]
        public void GetEffectiveIncludes_NoIncludes()
        {
            string source = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""Ruleset1"" Description=""Test"" ToolsVersion=""12.0"" >
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1012"" Action=""Warning"" />
  </Rules>
</RuleSet>
";

            var dir = Temp.CreateDirectory();
            var file = dir.CreateFile("a.ruleset");
            file.WriteAllText(source);

            var includePaths = RuleSet.GetEffectiveIncludesFromFile(file.Path);

            Assert.Equal(expected: 1, actual: includePaths.Length);
            Assert.Equal(expected: file.Path, actual: includePaths[0]);
        }

        [Fact]
        public void GetEffectiveIncludes_OneLevel()
        {
            string ruleSetSource = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""New Rule Set1"" Description=""Test"" ToolsVersion=""12.0"">
  <Include Path=""file1.ruleset"" Action=""Error"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1000"" Action=""Warning"" />
    <Rule Id=""CA1001"" Action=""Warning"" />
    <Rule Id=""CA2111"" Action=""None"" />
  </Rules>
</RuleSet>
";

            string includeSource = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""New Rule Set2"" Description=""Test"" ToolsVersion=""12.0"">
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA2100"" Action=""Warning"" />
    <Rule Id=""CA2111"" Action=""Warning"" />
  </Rules>
</RuleSet>
";

            var dir = Temp.CreateDirectory();

            var file = dir.CreateFile("a.ruleset");
            file.WriteAllText(ruleSetSource);

            var include = dir.CreateFile("file1.ruleset");
            include.WriteAllText(includeSource);

            var includePaths = RuleSet.GetEffectiveIncludesFromFile(file.Path);

            Assert.Equal(expected: 2, actual: includePaths.Length);
            Assert.Equal(expected: file.Path, actual: includePaths[0]);
            Assert.Equal(expected: include.Path, actual: includePaths[1]);
        }

        [Fact]
        public void GetEffectiveIncludes_TwoLevels()
        {
            string ruleSetSource = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""New Rule Set1"" Description=""Test"" ToolsVersion=""12.0"">
  <Include Path=""file1.ruleset"" Action=""Error"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA1000"" Action=""Warning"" />
    <Rule Id=""CA1001"" Action=""Warning"" />
    <Rule Id=""CA2111"" Action=""None"" />
  </Rules>
</RuleSet>
";

            string includeSource1 = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""New Rule Set2"" Description=""Test"" ToolsVersion=""12.0"">
  <Include Path=""file2.ruleset"" Action=""Warning"" />
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA2100"" Action=""Warning"" />
    <Rule Id=""CA2111"" Action=""Warning"" />
  </Rules>
</RuleSet>
";
            string includeSource2 = @"<?xml version=""1.0"" encoding=""utf-8""?>
<RuleSet Name=""New Rule Set3"" Description=""Test"" ToolsVersion=""12.0"">
  <Rules AnalyzerId=""Microsoft.Analyzers.ManagedCodeAnalysis"" RuleNamespace=""Microsoft.Rules.Managed"">
    <Rule Id=""CA2100"" Action=""Warning"" />
    <Rule Id=""CA2111"" Action=""Warning"" />
    <Rule Id=""CA2119"" Action=""None"" />
    <Rule Id=""CA2104"" Action=""Error"" />
    <Rule Id=""CA2105"" Action=""Warning"" />
  </Rules>
</RuleSet>";

            var dir = Temp.CreateDirectory();

            var file = dir.CreateFile("a.ruleset");
            file.WriteAllText(ruleSetSource);

            var include1 = dir.CreateFile("file1.ruleset");
            include1.WriteAllText(includeSource1);

            var include2 = dir.CreateFile("file2.ruleset");
            include2.WriteAllText(includeSource2);

            var includePaths = RuleSet.GetEffectiveIncludesFromFile(file.Path);

            Assert.Equal(expected: 3, actual: includePaths.Length);
            Assert.Equal(expected: file.Path, actual: includePaths[0]);
            Assert.Equal(expected: include1.Path, actual: includePaths[1]);
            Assert.Equal(expected: include2.Path, actual: includePaths[2]);
        }

        private string[] ParseSeparatedStrings(string arg, char[] separators, bool removeEmptyEntries = true)
        {
            var builder = ArrayBuilder<ReadOnlyMemory<char>>.GetInstance();
            CommandLineParser.ParseSeparatedStrings(arg.AsMemory(), separators, removeEmptyEntries, builder);
            return builder.Select(x => x.ToString()).ToArray();
        }

        [Fact]
        public void ParseSeparatedStrings_ExcludeSeparatorChar()
        {
            Assert.Equal(
                ParseSeparatedStrings(@"a,b", new[] { ',' }),
                new[] { "a", "b" });

            Assert.Equal(
                ParseSeparatedStrings(@"a,,b", new[] { ',' }),
                new[] { "a", "b" });
        }

        /// <summary>
        /// This function considers quotes when splitting out the strings.  Ensure they are properly
        /// preserved in the final string.
        /// </summary>
        [Fact]
        public void ParseSeparatedStrings_IncludeQuotes()
        {
            Assert.Equal(
                ParseSeparatedStrings(@"""a"",b", new[] { ',' }),
                new[] { @"""a""", "b" });

            Assert.Equal(
                ParseSeparatedStrings(@"""a,b""", new[] { ',' }),
                new[] { @"""a,b""" });

            Assert.Equal(
                ParseSeparatedStrings(@"""a"",""b", new[] { ',' }),
                new[] { @"""a""", @"""b" });
        }

        /// <summary>
        /// This function should always preserve the slashes as they exist in the original command
        /// line.  The only serve to decide whether quotes should count as grouping constructors
        /// or not. 
        /// </summary>
        [Fact]
        public void SplitCommandLineIntoArguments_Slashes()
        {
            Assert.Equal(
                new[] { @"\\test" },
                CommandLineParser.SplitCommandLineIntoArguments(@"\\test", removeHashComments: false));

            // Even though there are an even number of slashes here that doesn't factor into the 
            // output.  It just means the quote is a grouping construct.
            Assert.Equal(
                new[] { @"\\""test" },
                CommandLineParser.SplitCommandLineIntoArguments(@"\\""test", removeHashComments: false));

            Assert.Equal(
                new[] { @"\\\""test" },
                CommandLineParser.SplitCommandLineIntoArguments(@"\\\""test", removeHashComments: false));

            Assert.Equal(
                new[] { @"\\\test" },
                CommandLineParser.SplitCommandLineIntoArguments(@"\\\test", removeHashComments: false));

            Assert.Equal(
                new[] { @"\\\\\test" },
                CommandLineParser.SplitCommandLineIntoArguments(@"\\\\\test", removeHashComments: false));
        }

        /// <summary>
        /// Quotes are used as grouping constructs unless they are escaped by an odd number of slashes.
        /// </summary>
        [Fact]
        public void SplitCommandLineIntoArguments_Quotes()
        {
            Assert.Equal(
                new[] { @"a", @"b" },
                CommandLineParser.SplitCommandLineIntoArguments(@"a b", removeHashComments: false));

            Assert.Equal(
                new[] { @"a b" },
                CommandLineParser.SplitCommandLineIntoArguments(@"""a b""", removeHashComments: false));

            Assert.Equal(
                new[] { @"a ", @"b""" },
                CommandLineParser.SplitCommandLineIntoArguments(@"""a "" b""", removeHashComments: false));

            // In this case the inner quote is escaped so it doesn't count as a real quote.  Strings which have
            // outer quotes with no real inner quotes have the outer quotes removed. 
            Assert.Equal(
                new[] { @"a \"" b" },
                CommandLineParser.SplitCommandLineIntoArguments(@"""a \"" b""", removeHashComments: false));

            Assert.Equal(
                new[] { @"\a", @"b" },
                CommandLineParser.SplitCommandLineIntoArguments(@"\a b", removeHashComments: false));

            // Escaped quote is not a grouping construct
            Assert.Equal(
                new[] { @"\""a", @"b\""" },
                CommandLineParser.SplitCommandLineIntoArguments(@"\""a b\""", removeHashComments: false));

            // Unescaped quote is a grouping construct. 
            Assert.Equal(
                new[] { @"\\""a b\\""" },
                CommandLineParser.SplitCommandLineIntoArguments(@"\\""a b\\""", removeHashComments: false));

            Assert.Equal(
                new[] { @"""a""m""b""" },
                CommandLineParser.SplitCommandLineIntoArguments(@"""a""m""b""", removeHashComments: false));
        }

        /// <summary>
        /// Test all of the cases around slashes in the RemoveQuotes function.  
        /// </summary>
        /// <remarks>
        /// It's important to remember this is testing slash behavior on the strings as they 
        /// are passed to RemoveQuotes, not as they are passed to the command line.  Command 
        /// line arguments have already gone through an initial round of processing.  So a
        /// string that appears here as "\\test.cs" actually came through the command line
        /// as \"\\test.cs\". 
        /// </remarks>
        [Fact]
        public void RemoveQuotesAndSlashes()
        {
            Assert.Equal(@"\\test.cs", CommandLineParser.RemoveQuotesAndSlashes(@"\\test.cs"));
            Assert.Equal(@"\\test.cs", CommandLineParser.RemoveQuotesAndSlashes(@"""\\test.cs"""));
            Assert.Equal(@"\\\test.cs", CommandLineParser.RemoveQuotesAndSlashes(@"\\\test.cs"));
            Assert.Equal(@"\\\\test.cs", CommandLineParser.RemoveQuotesAndSlashes(@"\\\\test.cs"));
            Assert.Equal(@"\\test\a\b.cs", CommandLineParser.RemoveQuotesAndSlashes(@"\\test\a\b.cs"));
            Assert.Equal(@"\\\\test\\a\\b.cs", CommandLineParser.RemoveQuotesAndSlashes(@"\\\\test\\a\\b.cs"));
            Assert.Equal(@"a""b.cs", CommandLineParser.RemoveQuotesAndSlashes(@"a\""b.cs"));
            Assert.Equal(@"a"" mid ""b.cs", CommandLineParser.RemoveQuotesAndSlashes(@"a\"" mid \""b.cs"));
            Assert.Equal(@"a mid b.cs", CommandLineParser.RemoveQuotesAndSlashes(@"a"" mid ""b.cs"));
            Assert.Equal(@"a.cs", CommandLineParser.RemoveQuotesAndSlashes(@"""a.cs"""));
            Assert.Equal(@"C:""My Folder\MyBinary.xml", CommandLineParser.RemoveQuotesAndSlashes(@"C:\""My Folder""\MyBinary.xml"));
        }

        /// <summary>
        /// Verify that for the standard cases we do not allocate new memory but instead 
        /// return a <see cref="ReadOnlyMemory{T}"/> to the existing string
        /// </summary>
        [Fact]
        public void RemoveQuotesAndSlashes_NoAllocation()
        {
            assertSame(@"c:\test.cs");
            assertSame(@"""c:\test.cs""");
            assertSame(@"""c:\\\\\\\\\test.cs""");
            assertSame(@"""c:\\\\\\\\\test.cs""");

            void assertSame(string arg)
            {
                var memory = CommandLineParser.RemoveQuotesAndSlashesEx(arg.AsMemory());
                Assert.True(MemoryMarshal.TryGetString(memory, out var memoryString, out _, out _));
                Assert.Same(arg, memoryString);
            }
        }
    }
}
