public void RoslynPlugin_GenerateForDependencyAnalyzers_Succeeds()
        {
            // Arrange
            TestLogger logger = new TestLogger();
            string outputDir = TestUtils.CreateTestDirectory(this.TestContext, ".out");
            string dummyContentFile = TestUtils.CreateTextFile("dummy.txt", outputDir, "non-analyzer content file");

            // Create a valid analyzer package
            RoslynAnalyzer11.CSharpAnalyzer analyzer = new RoslynAnalyzer11.CSharpAnalyzer();

            string fakeRemoteNuGetDir = TestUtils.CreateTestDirectory(this.TestContext, ".fakeRemoteNuGet");
            IPackageManager fakeRemotePkgMgr = CreatePackageManager(fakeRemoteNuGetDir);
            IPackage child1 = AddPackage(fakeRemotePkgMgr, "Analyzer.Child1", "1.1.0", analyzer.GetType().Assembly.Location);
            IPackage child2 = AddPackage(fakeRemotePkgMgr, "Analyzer.Child2", "1.2.0", analyzer.GetType().Assembly.Location);
            IPackage targetPkg = AddPackage(fakeRemotePkgMgr, "Empty.Parent", "1.0.0", dummyContentFile, child1, child2);

            string localPackageDestination = TestUtils.CreateTestDirectory(this.TestContext, ".localpackages");

            // Act
            NuGetPackageHandler nuGetHandler = new NuGetPackageHandler(fakeRemotePkgMgr.LocalRepository, localPackageDestination, logger);
            AnalyzerPluginGenerator apg = new AnalyzerPluginGenerator(nuGetHandler, logger);
            ProcessedArgs args = new ProcessedArgs(targetPkg.Id, targetPkg.Version, "cs", null, false,
                true /* generate plugins for dependencies with analyzers*/, outputDir);
            bool result = apg.Generate(args);

            // Assert
            Assert.IsTrue(result);

            // Expecting one plugin per dependency with analyzers
            CheckJarGeneratedForPackage(outputDir, analyzer, child1);
            CheckJarGeneratedForPackage(outputDir, analyzer, child2);
            AssertJarsGenerated(outputDir, 2);
        }
        public ProcessedArgs Process(string[] commandLineArgs)
        {
            ProcessedArgs processed = null;
            IEnumerable<ArgumentInstance> arguments;

            // This call will fail if there are duplicate, missing, or unrecognized arguments
            CommandLineParser parser = new CommandLineParser(Descriptors, false /* don't allow unrecognized */);
            bool parsedOk = parser.ParseArguments(commandLineArgs, this.logger, out arguments);

            NuGetReference analyzerRef;
            parsedOk &= TryParseAnalyzerRef(arguments, out analyzerRef);

            string sqaleFilePath;
            parsedOk &= TryParseSqaleFile(arguments, out sqaleFilePath);

            if (parsedOk)
            {
                Debug.Assert(analyzerRef != null, "Expecting to have a valid analyzer reference");
                processed = new ProcessedArgs(analyzerRef, sqaleFilePath);
            }

            return processed;
        }
 private static ProcessedArgs CreateArgs(string packageId, string packageVersion, string language, string sqaleFilePath, bool acceptLicenses, string outputDirectory)
 {
     ProcessedArgs args = new ProcessedArgs(
         packageId,
         new SemanticVersion(packageVersion),
         language,
         sqaleFilePath,
         acceptLicenses,
         outputDirectory);
     return args;
 }
        public void RoslynPlugin_GenerateForValidAnalyzer_Succeeds()
        {
            // Arrange
            TestLogger logger = new TestLogger();
            string outputDir = TestUtils.CreateTestDirectory(this.TestContext, ".out");

            // Create a valid analyzer package
            RoslynAnalyzer11.CSharpAnalyzer analyzer = new RoslynAnalyzer11.CSharpAnalyzer();

            string packageId = "Analyzer1.Pkgid1"; // package id is not all lowercase
            string fakeRemoteNuGetDir = TestUtils.CreateTestDirectory(this.TestContext, ".fakeRemoteNuGet");
            IPackageManager fakeRemotePkgMgr = CreatePackageManager(fakeRemoteNuGetDir);
            IPackage analyzerPkg =  AddPackage(fakeRemotePkgMgr, packageId, "1.0.2", analyzer.GetType().Assembly.Location);

            string localPackageDestination = TestUtils.CreateTestDirectory(this.TestContext, ".localpackages");

            // Act
            NuGetPackageHandler nuGetHandler = new NuGetPackageHandler(fakeRemotePkgMgr.LocalRepository, localPackageDestination, logger);
            AnalyzerPluginGenerator apg = new AnalyzerPluginGenerator(nuGetHandler, logger);
            ProcessedArgs args = new ProcessedArgs(packageId, new SemanticVersion("1.0.2"), "cs", null, false, false, outputDir);
            bool result = apg.Generate(args);

            // Assert
            Assert.IsTrue(result);

            // Expecting one plugin per dependency with analyzers
            CheckJarGeneratedForPackage(outputDir, analyzer, analyzerPkg);
            AssertJarsGenerated(outputDir, 1);
        }
 private static void AssertArgumentsNotProcessed(ProcessedArgs actualArgs, TestLogger logger)
 {
     Assert.IsNull(actualArgs, "Not expecting the arguments to have been processed successfully");
     logger.AssertErrorsLogged();
 }
        private static void AssertArgumentsProcessed(ProcessedArgs actualArgs, TestLogger logger, string expectedId, string expectedVersion, string expectedSqale, bool expectedAcceptLicenses)
        {
            Assert.IsNotNull(actualArgs, "Expecting the arguments to have been processed successfully");

            Assert.AreEqual(actualArgs.PackageId, expectedId, "Unexpected package id returned");

            if (expectedVersion == null)
            {
                Assert.IsNull(actualArgs.PackageVersion, "Expecting the version to be null");
            }
            else
            {
                Assert.IsNotNull(actualArgs.PackageVersion, "Not expecting the version to be null");
                Assert.AreEqual(expectedVersion, actualArgs.PackageVersion.ToString());
            }

            Assert.AreEqual(expectedSqale, actualArgs.SqaleFilePath, "Unexpected sqale file path");
            if (expectedSqale != null)
            {
                Assert.IsTrue(File.Exists(expectedSqale), "Specified sqale file should exist: {0}", expectedSqale);
            }

            Assert.AreEqual(expectedAcceptLicenses, actualArgs.AcceptLicenses, "Unexpected value for AcceptLicenses");

            logger.AssertErrorsLogged(0);
        }
        public void RoslynPlugin_GenerateForValidAnalyzer_Succeeds()
        {
            // Arrange
            TestLogger logger = new TestLogger();
            string outputDir = TestUtils.CreateTestDirectory(this.TestContext, ".out");

            // Create a valid analyzer package
            RoslynAnalyzer11.CSharpAnalyzer analyzer = new RoslynAnalyzer11.CSharpAnalyzer();

            string packageId = "Analyzer1.Pkgid1"; // package id is not all lowercase
            string fakeRemoteNuGetDir = TestUtils.CreateTestDirectory(this.TestContext, ".fakeRemoteNuGet");
            IPackageManager fakeRemotePkgMgr = CreatePackageManager(fakeRemoteNuGetDir);
            IPackage analyzerPkg =  AddPackage(fakeRemotePkgMgr, packageId, "1.0.2", analyzer.GetType().Assembly.Location);

            string localPackageDestination = TestUtils.CreateTestDirectory(this.TestContext, ".localpackages");

            // Act
            NuGetPackageHandler nuGetHandler = new NuGetPackageHandler(fakeRemotePkgMgr.LocalRepository, localPackageDestination, logger);
            AnalyzerPluginGenerator apg = new AnalyzerPluginGenerator(nuGetHandler, logger);
            ProcessedArgs args = new ProcessedArgs(packageId, new SemanticVersion("1.0.2"), "cs", null, false, outputDir);
            bool result = apg.Generate(args);

            // Assert
            Assert.IsTrue(result);
            string jarFilePath = AssertPluginJarExists(outputDir);

            // Check the content of the files embedded in the jar
            ZipFileChecker jarChecker = new ZipFileChecker(this.TestContext, jarFilePath);


            // Check the contents of the embedded config file
            string embeddedConfigFile = jarChecker.AssertFileExists("org\\sonar\\plugins\\roslynsdk\\configuration.xml");
            RoslynSdkConfiguration config = RoslynSdkConfiguration.Load(embeddedConfigFile);

            // Check the config settings
            Assert.AreEqual("analyzer1pkgid1", config.PluginKeyDifferentiator, "Unexpected repository differentiator");
            Assert.AreEqual("roslyn.analyzer1.pkgid1.cs", config.RepositoryKey, "Unexpected repository key");
            Assert.AreEqual("cs", config.RepositoryLanguage, "Unexpected language");
            Assert.AreEqual("dummy title", config.RepositoryName, "Unexpected repository name");
            
            // Check for the expected property values required by the C# plugin
            // Property name prefixes should be lower case; the case of the value should be the same as the package id
            AssertExpectedPropertyDefinitionValue("analyzer1.pkgid1.cs.analyzerId", "Analyzer1.Pkgid1", config);
            AssertExpectedPropertyDefinitionValue("analyzer1.pkgid1.cs.ruleNamespace", "Analyzer1.Pkgid1", config);
            AssertExpectedPropertyDefinitionValue("analyzer1.pkgid1.cs.nuget.packageId", "Analyzer1.Pkgid1", config);
            AssertExpectedPropertyDefinitionValue("analyzer1.pkgid1.cs.nuget.packageVersion", "1.0.2", config);
            AssertExpectedPropertyDefinitionValue("analyzer1.pkgid1.cs.staticResourceName", "Analyzer1.Pkgid1.1.0.2.zip", config);
            AssertExpectedPropertyDefinitionValue("analyzer1.pkgid1.cs.pluginKey", "analyzer1pkgid1", config);
            AssertExpectedPropertyDefinitionValue("analyzer1.pkgid1.cs.pluginVersion", "1.0.2", config);

            // Check the contents of the manifest
            string actualManifestFilePath = jarChecker.AssertFileExists("META-INF\\MANIFEST.MF");
            string[] actualManifest = File.ReadAllLines(actualManifestFilePath);
            AssertExpectedManifestValue(WellKnownPluginProperties.Key, "analyzer1pkgid1", actualManifest);
            AssertExpectedManifestValue("Plugin-Key", "analyzer1pkgid1", actualManifest); // plugin-key should be lowercase and alphanumeric
            AssertPackagePropertiesInManifest(analyzerPkg, actualManifest);


            // Check the rules
            string actualRuleFilePath = jarChecker.AssertFileExists("." + config.RulesXmlResourcePath);
            AssertExpectedRulesExist(analyzer, actualRuleFilePath);

            // Now create another checker to check the contents of the zip file (strict check this time)
            CheckEmbeddedAnalyzerPayload(jarChecker, "static\\analyzer1.pkgid1.1.0.2.zip",
                /* zip file contents */
                "analyzers\\RoslynAnalyzer11.dll");
        }
        public bool Generate(ProcessedArgs args)
        {
            if (args == null)
            {
                throw new ArgumentNullException("args");
            }

            IPackage targetPackage = this.packageHandler.FetchPackage(args.PackageId, args.PackageVersion);

            if (targetPackage == null)
            {
                return false;
            }

            IEnumerable<IPackage> dependencyPackages = this.packageHandler.GetInstalledDependencies(targetPackage);

            // Check that there are analyzers in the target package from which information can be extracted

            // Create a mapping of packages to analyzers to avoid having to search for analyzers more than once
            Dictionary<IPackage, IEnumerable<DiagnosticAnalyzer>> analyzersByPackage = new Dictionary<IPackage, IEnumerable<DiagnosticAnalyzer>>();
            IEnumerable<DiagnosticAnalyzer> targetAnalyzers = GetAnalyzers(targetPackage, args.Language);
            if (targetAnalyzers.Any())
            {
                analyzersByPackage.Add(targetPackage, targetAnalyzers);
            }
            else
            {
                this.logger.LogWarning(UIResources.APG_NoAnalyzersFound, targetPackage.Id);

                if (!args.RecurseDependencies)
                {
                    this.logger.LogWarning(UIResources.APG_NoAnalyzersInTargetSuggestRecurse);
                    return false;
                }
            }

            if (args.RecurseDependencies)
            {
                // Possible sub-case - target package has dependencies that contain analyzers
                foreach (IPackage dependencyPackage in dependencyPackages)
                {
                    IEnumerable<DiagnosticAnalyzer> dependencyAnalyzers = GetAnalyzers(dependencyPackage, args.Language);
                    if (dependencyAnalyzers.Any())
                    {
                        analyzersByPackage.Add(dependencyPackage, dependencyAnalyzers);
                    }
                    else
                    {
                        this.logger.LogWarning(UIResources.APG_NoAnalyzersFound, dependencyPackage.Id);
                    }
                }

                if (!analyzersByPackage.Any())
                {
                    return false;
                }
            }

            // Check for packages that require the user to accept a license
            IEnumerable<IPackage> licenseAcceptancePackages = this.GetPackagesRequiringLicenseAcceptance(targetPackage);
            if (licenseAcceptancePackages.Any() && !args.AcceptLicenses)
            {
                // NB: This warns for all packages under the target that require license acceptance
                // (even if they aren't related to the packages from which plugins were generated)
                this.logger.LogError(UIResources.APG_NGPackageRequiresLicenseAcceptance, targetPackage.Id, targetPackage.Version.ToString());
                this.ListPackagesRequiringLicenseAcceptance(licenseAcceptancePackages);
                return false;
            }

            List<string> generatedJarFiles = new List<string>();
            // Initial run with the user-targeted package and arguments
            if (analyzersByPackage.ContainsKey(targetPackage))
            {
                string generatedJarPath = GeneratePluginForPackage(args.OutputDirectory, args.Language, args.SqaleFilePath, targetPackage, analyzersByPackage[targetPackage]);
                if (generatedJarPath == null)
                {
                    return false;
                }

                generatedJarFiles.Add(generatedJarPath);
                analyzersByPackage.Remove(targetPackage);
            }

            // Dependent package generation changes the arguments
            if (args.RecurseDependencies)
            {
                this.logger.LogWarning(UIResources.APG_RecurseEnabled_SQALENotEnabled);

                foreach (IPackage currentPackage in analyzersByPackage.Keys)
                {
                    // No way to specify the SQALE file for any but the user-targeted package at this time
                    string generatedJarPath = GeneratePluginForPackage(args.OutputDirectory, args.Language, null, currentPackage, analyzersByPackage[currentPackage]);
                    if (generatedJarPath == null)
                    {
                        return false;
                    }

                    generatedJarFiles.Add(generatedJarPath);
                }
            }

            LogAcceptedPackageLicenses(licenseAcceptancePackages);

            foreach (string generatedJarFile in generatedJarFiles)
            {
                this.logger.LogInfo(UIResources.APG_PluginGenerated, generatedJarFile);
            }

            return true;
        }
        public ProcessedArgs Process(string[] commandLineArgs)
        {
            ProcessedArgs processed = null;
            IEnumerable<ArgumentInstance> arguments;

            // This call will fail if there are duplicate, missing, or unrecognized arguments
            CommandLineParser parser = new CommandLineParser(Descriptors, false /* don't allow unrecognized */);
            bool parsedOk = parser.ParseArguments(commandLineArgs, this.logger, out arguments);

            NuGetReference analyzerRef;
            parsedOk &= TryParseAnalyzerRef(arguments, out analyzerRef);

            string sqaleFilePath;
            parsedOk &= TryParseSqaleFile(arguments, out sqaleFilePath);

            bool acceptLicense = GetLicenseAcceptance(arguments);

            if (parsedOk)
            {
                Debug.Assert(analyzerRef != null, "Expecting to have a valid analyzer reference");
                processed = new ProcessedArgs(
                    analyzerRef.PackageId,
                    analyzerRef.Version,
                    SupportedLanguages.CSharp, /* TODO: support multiple languages */
                    sqaleFilePath,
                    acceptLicense,
                    System.IO.Directory.GetCurrentDirectory());
            }

            return processed;
        }
        public bool Generate(ProcessedArgs args)
        {
            if (args == null)
            {
                throw new ArgumentNullException("args");
            }

            IPackage package = this.packageHandler.FetchPackage(args.PackageId, args.PackageVersion);
            if (package == null)
            {
                return false;
            }

            string packageDir = this.packageHandler.GetLocalPackageRootDirectory(package);
            IEnumerable<DiagnosticAnalyzer> analyzers = GetAnalyzers(packageDir, this.packageHandler.LocalCacheRoot, args.Language);
            if (!analyzers.Any())
            {
                return false;
            }

            IEnumerable<IPackage> licenseAcceptancePackages = this.GetPackagesRequiringLicenseAcceptance(package);
            if (licenseAcceptancePackages.Any() && ! args.AcceptLicenses)
            {
                this.logger.LogError(UIResources.APG_NGPackageRequiresLicenseAcceptance, package.Id, package.Version.ToString());
                this.ListPackagesRequiringLicenseAcceptance(licenseAcceptancePackages);
                return false;
            }

            string createdJarFilePath = null;

            string baseDirectory = CreateBaseWorkingDirectory();

            // Collect the remaining data required to build the plugin
            RoslynPluginDefinition definition = new RoslynPluginDefinition();
            definition.Language = args.Language;
            definition.SqaleFilePath = args.SqaleFilePath;
            definition.PackageId = package.Id;
            definition.PackageVersion = package.Version.ToString();
            definition.Manifest = CreatePluginManifest(package);

            // Create a zip containing the required analyzer files
            definition.SourceZipFilePath = this.CreateAnalyzerStaticPayloadFile(packageDir, baseDirectory);
            definition.StaticResourceName = Path.GetFileName(definition.SourceZipFilePath);

            definition.RulesFilePath = GenerateRulesFile(analyzers, baseDirectory);

            string generatedSqaleFile = null;
            bool generate = true;
            if (definition.SqaleFilePath == null)
            {
                generatedSqaleFile = CalculateSqaleFileName(package, args.OutputDirectory);
                GenerateFixedSqaleFile(analyzers, generatedSqaleFile);
                Debug.Assert(File.Exists(generatedSqaleFile));
            }
            else
            {
                generate = IsValidSqaleFile(definition.SqaleFilePath);
            }

            if (generate)
            {
                createdJarFilePath = BuildPlugin(definition, args.OutputDirectory);
            }

            LogSummary(createdJarFilePath, generatedSqaleFile, licenseAcceptancePackages);

            return createdJarFilePath != null;
        }
        private static void AssertArgumentsProcessed(ProcessedArgs actualArgs, TestLogger logger, string expectedId, string expectedVersion, string expectedSqale)
        {
            Assert.IsNotNull(actualArgs, "Expecting the arguments to have been processed successfully");

            Assert.IsNotNull(actualArgs.AnalyzerRef, "Not expecting the analyzer reference to be null");
            Assert.AreEqual(actualArgs.AnalyzerRef.PackageId, expectedId, "Unexpected package id returned");

            NuGetReference actualRef = actualArgs.AnalyzerRef;
            if (expectedVersion == null)
            {
                Assert.IsNull(actualRef.Version, "Expecting the version to be null");
            }
            else
            {
                Assert.IsNotNull(actualRef.Version, "Not expecting the version to be null");
                Assert.AreEqual(expectedVersion, actualRef.Version.ToString());
            }

            Assert.AreEqual(expectedSqale, actualArgs.SqaleFilePath, "Unexpected sqale file path");
            if (expectedSqale != null)
            {
                Assert.IsTrue(File.Exists(expectedSqale), "Specified sqale file should exist: {0}", expectedSqale);
            }

            logger.AssertErrorsLogged(0);
        }