public void ProjectLoader()
        {
            // Arrange
            string testSourcePath = TestUtils.CreateTestSpecificFolder(this.TestContext);

            // Create sub-directories, some with project info XML files and some without
            TestUtils.EnsureTestSpecificFolder(this.TestContext, "EmptyDir1");

            ProjectDescriptor validTestProject = new ProjectDescriptor()
            {
                ParentDirectoryPath = testSourcePath,
                ProjectFolderName = "validTestProjectDir",
                ProjectFileName = "validTestProject.csproj",
                ProjectGuid = Guid.NewGuid(),
                IsTestProject = true
            };
            validTestProject.AddCompileInputFile("TestFile1.cs", true);
            validTestProject.AddCompileInputFile("TestFile1.cs", true);
            validTestProject.AddContentFile("contentFile1.js", true);
            CreateFilesFromDescriptor(validTestProject, "testCompileListFile", "testContentList", "testFxCopReport", "testVisualStudioCodeCoverageReport");

            TestUtils.EnsureTestSpecificFolder(this.TestContext, "EmptyDir2");

            ProjectDescriptor validNonTestProject = new ProjectDescriptor()
            {
                ParentDirectoryPath = testSourcePath,
                ProjectFolderName = "validNonTestProjectDir",
                ProjectFileName = "validNonTestproject.proj",
                ProjectGuid = Guid.NewGuid(),
                IsTestProject = false
            };
            validNonTestProject.AddContentFile("ASourceFile.vb", true);
            validNonTestProject.AddContentFile("AnotherSourceFile.vb", true);
            CreateFilesFromDescriptor(validNonTestProject, "list.txt", null, "fxcop.xml", "visualstudio-codecoverage.xml");

            ProjectDescriptor validNonTestNoReportsProject = new ProjectDescriptor()
            {
                ParentDirectoryPath = testSourcePath,
                ProjectFolderName = "validNonTestNoReportsProjectDir",
                ProjectFileName = "validNonTestNoReportsProject.proj",
                ProjectGuid = Guid.NewGuid(),
                IsTestProject = false
            };
            validNonTestNoReportsProject.AddContentFile("SomeFile.cs", true);
            CreateFilesFromDescriptor(validNonTestNoReportsProject, "SomeList.txt", null, null, null);

            // Act
            IEnumerable<ProjectInfo> projects = SonarRunner.Shim.ProjectLoader.LoadFrom(testSourcePath);

            // Assert
            Assert.AreEqual(3, projects.Count());

            AssertProjectResultExists(validTestProject.ProjectName, projects);

            AssertProjectResultExists(validNonTestProject.ProjectName, projects);

            AssertProjectResultExists(validNonTestNoReportsProject.ProjectName, projects);
        }
 /// <summary>
 /// Creates and returns a valid project descriptor for a project in the supplied folders
 /// </summary>
 public static ProjectDescriptor CreateValidProjectDescriptor(string parentDirectory, string projectFileName = "MyProject.xproj.txt", bool isVBProject = false)
 {
     ProjectDescriptor descriptor = new ProjectDescriptor()
     {
         ProjectLanguage = isVBProject ? SonarQube.Common.ProjectLanguages.VisualBasic : SonarQube.Common.ProjectLanguages.CSharp,
         ProjectGuid = Guid.NewGuid(),
         IsTestProject = false,
         ParentDirectoryPath = parentDirectory,
         ProjectFolderName = "MyProjectDir",
         ProjectFileName = projectFileName
     };
     return descriptor;
 }
        /// <summary>
        /// Creates a project file on disk from the specified descriptor.
        /// Sets the SonarQube output folder property, if specified.
        /// </summary>
        public static ProjectRootElement CreateInitializedProjectRoot(TestContext testContext, ProjectDescriptor descriptor, IDictionary<string, string> preImportProperties)
        {
            if (testContext == null)
            {
                throw new ArgumentNullException("testContext");
            }
            if (descriptor == null)
            {
                throw new ArgumentNullException("descriptor");
            }
            
            ProjectRootElement projectRoot = BuildUtilities.CreateAnalysisProject(testContext, descriptor, preImportProperties);

            projectRoot.ToolsVersion = MSBuildToolsVersionForTestProjects;

            projectRoot.Save(descriptor.FullFilePath);

            testContext.AddResultFile(descriptor.FullFilePath);
            return projectRoot;
        }
        public void E2E_MissingProjectGuid()
        {
            // Projects with missing guids should have a warning emitted. The project info
            // should not be generated.

            // Arrange
            string rootInputFolder = TestUtils.CreateTestSpecificFolder(this.TestContext, "Inputs");
            string rootOutputFolder = TestUtils.CreateTestSpecificFolder(this.TestContext, "Outputs");

            WellKnownProjectProperties preImportProperties = CreateDefaultAnalysisProperties(rootInputFolder, rootOutputFolder);

            preImportProperties["SonarConfigPath"] = rootInputFolder;

            ProjectDescriptor descriptor = new ProjectDescriptor()
            {
                // No guid property
                IsTestProject = false,
                ParentDirectoryPath = rootInputFolder,
                ProjectFolderName = "MyProjectDir",
                ProjectFileName = "MissingProjectGuidProject.proj"
            };
            AddEmptyAnalysedCodeFile(descriptor, rootInputFolder);

            ProjectRootElement projectRoot = BuildUtilities.CreateInitializedProjectRoot(this.TestContext, descriptor, preImportProperties);

            BuildLogger logger = new BuildLogger();

            // Act
            BuildResult result = BuildUtilities.BuildTargets(projectRoot, logger);

            // Assert
            BuildAssertions.AssertTargetSucceeded(result, TargetConstants.DefaultBuildTarget); // Build should succeed with warnings
            ProjectInfoAssertions.AssertNoProjectInfoFilesExists(rootOutputFolder);

            logger.AssertExpectedErrorCount(0);
            logger.AssertExpectedWarningCount(1);

            BuildWarningEventArgs warning = logger.Warnings[0];
            Assert.IsTrue(warning.Message.Contains(descriptor.FullFilePath),
                "Expecting the warning to contain the full path to the bad project file");
        }
        /// <summary>
        /// Creates and returns an empty MSBuild project using the data in the supplied descriptor.
        /// The project will import the SonarQube analysis targets file and the standard C# targets file.
        /// The project name and GUID will be set if the values are supplied in the descriptor.
        /// </summary>
        private static ProjectRootElement CreateAnalysisProject(TestContext testContext, ProjectDescriptor descriptor,
            IDictionary<string, string> preImportProperties)
        {
            if (testContext == null)
            {
                throw new ArgumentNullException("testContext");
            }
            if (descriptor == null)
            {
                throw new ArgumentNullException("descriptor");
            }

            string sqTargetFile = TestUtils.EnsureAnalysisTargetsExists(testContext);
            Assert.IsTrue(File.Exists(sqTargetFile), "Test error: the SonarQube analysis targets file could not be found. Full path: {0}", sqTargetFile);
            testContext.AddResultFile(sqTargetFile);

            IDictionary<string, string> properties = preImportProperties ?? new Dictionary<string, string>();

            // Disable the standard "ImportBefore/ImportAfter" behaviour if the caller
            // hasn't defined what they want to happen explicitly
            if (!properties.ContainsKey(StandardImportBeforePropertyName))
            {
                DisableStandardTargetsWildcardImporting(properties);
            }

            ProjectRootElement root = CreateMinimalBuildableProject(properties, descriptor.IsVbProject, sqTargetFile);

            // Set the location of the task assembly
            if (!properties.ContainsKey(TargetProperties.SonarBuildTasksAssemblyFile))
            {
                root.AddProperty(TargetProperties.SonarBuildTasksAssemblyFile, typeof(WriteProjectInfoFile).Assembly.Location);
            }

            if (descriptor.ProjectGuid != Guid.Empty)
            {
                root.AddProperty(TargetProperties.ProjectGuid, descriptor.ProjectGuid.ToString("D"));
            }

            foreach (ProjectDescriptor.FileInProject file in descriptor.Files)
            {
                root.AddItem(file.ItemGroup, file.FilePath);
            }

            if (descriptor.IsTestProject && !root.Properties.Any(p => string.Equals(p.Name, TargetProperties.SonarQubeTestProject)))
            {
                root.AddProperty(TargetProperties.SonarQubeTestProject, "true");
            }

            return root;
        }
        /// <summary>
        /// Creates a folder containing a ProjectInfo.xml and compiled file list as
        /// specified in the supplied descriptor
        /// </summary>
        private static void CreateFilesFromDescriptor(ProjectDescriptor descriptor, string compileFiles, string contentFiles, string fxcopReportFileName, string visualStudioCodeCoverageReportFileName)
        {
            if (!Directory.Exists(descriptor.FullDirectoryPath))
            {
                Directory.CreateDirectory(descriptor.FullDirectoryPath);
            }

            ProjectInfo projectInfo = descriptor.CreateProjectInfo();

            // Create the analysis file list if any input files have been specified
            if (descriptor.FilesToAnalyse.Any())
            {
                string fullAnalysisFileListPath = Path.Combine(descriptor.FullDirectoryPath, compileFiles);
                File.WriteAllLines(fullAnalysisFileListPath, descriptor.FilesToAnalyse);

                // Add the compile list as an analysis result
                projectInfo.AnalysisResults.Add(new AnalysisResult() { Id = AnalysisType.FilesToAnalyze.ToString(), Location = fullAnalysisFileListPath });
            }

            // Create the FxCop report file
            if (fxcopReportFileName != null)
            {
                string fullFxCopName = Path.Combine(descriptor.FullDirectoryPath, fxcopReportFileName);
                File.Create(fullFxCopName);

                // Add the FxCop report as an analysis result
                var analysisResult = new AnalysisResult() { Id = AnalysisType.FxCop.ToString(), Location = fullFxCopName };
                descriptor.AnalysisResults.Add(analysisResult);
                projectInfo.AnalysisResults.Add(analysisResult);
            }

            // Create the Visual Studio Code Coverage report file
            if (visualStudioCodeCoverageReportFileName != null)
            {
                string fullVisualStudioCodeCoverageName = Path.Combine(descriptor.FullDirectoryPath, visualStudioCodeCoverageReportFileName);
                File.Create(fullVisualStudioCodeCoverageName);

                // Add the Visual Studio Code Coverage report as an analysis result
                var analysisResult = new AnalysisResult() { Id = AnalysisType.VisualStudioCodeCoverage.ToString(), Location = fullVisualStudioCodeCoverageName };
                descriptor.AnalysisResults.Add(analysisResult);
                projectInfo.AnalysisResults.Add(analysisResult);
            }

            // Save a project info file in the target directory
            projectInfo.Save(Path.Combine(descriptor.FullDirectoryPath, FileConstants.ProjectInfoFileName));
        }
        public void ProjectLoader_NonRecursive()
        {
            // 0. Setup
            string rootTestDir = Path.Combine(this.TestContext.DeploymentDirectory, "ProjectLoader_NonRecursive");
            string childDir = Path.Combine(rootTestDir, "Child1");

            // Create a valid project in the child directory
            ProjectDescriptor validNonTestProject = new ProjectDescriptor()
            {
                ParentDirectoryPath = childDir,
                ProjectFolderName = "validNonTestProjectDir",
                ProjectFileName = "validNonTestproject.proj",
                ProjectGuid = Guid.NewGuid(),
                IsTestProject = false
            };
            validNonTestProject.AddCompileInputFile("ASourceFile.vb", true);
            validNonTestProject.AddCompileInputFile("AnotherSourceFile.vb", true);
            CreateFilesFromDescriptor(validNonTestProject, "CompileList.txt", null, null, null);

            // 1. Run against the root dir -> not expecting the project to be found
            IEnumerable<ProjectInfo> projects = SonarRunner.Shim.ProjectLoader.LoadFrom(rootTestDir);
            Assert.AreEqual(0, projects.Count());

            // 2. Run against the child dir -> project should be found
            projects = SonarRunner.Shim.ProjectLoader.LoadFrom(childDir);
            Assert.AreEqual(1, projects.Count());
        }
        private static ProjectInfo CreateExpectedProjectInfo (ProjectDescriptor expected, string projectOutputFolder)
        {
            ProjectInfo expectedProjectInfo = expected.CreateProjectInfo();

            // Work out what the expected analysis results are

            if (expected.Files.Any(f => f.ShouldBeAnalysed))
            {
                expectedProjectInfo.AnalysisResults.Add(
                    new AnalysisResult()
                    {
                        Id = AnalysisType.FilesToAnalyze.ToString(),
                        Location = Path.Combine(projectOutputFolder, ExpectedAnalysisFilesListFileName)
                    });
            }

            return expectedProjectInfo;
        }
        private void CheckAnalysisFileList(ProjectDescriptor expected, string projectOutputFolder)
        {
            string[] expectedFiles = GetExpectedAnalysisFiles(expected).ToArray();

            if (!expectedFiles.Any())
            {
                AssertFileDoesNotExist(projectOutputFolder, ExpectedAnalysisFilesListFileName);
            }
            else
            {
                string fullName = AssertFileExists(projectOutputFolder, ExpectedAnalysisFilesListFileName);

                string[] actualFiles = File.ReadAllLines(fullName);

                // The actual files might contain extra compiler generated files, so check the expected files
                // we expected is a subset of the actual
                CollectionAssert.IsSubsetOf(expectedFiles, actualFiles, "Analysis file does not contain the expected entries");

                // Check that any files that should not be analysed are not included
                if (expected.FilesNotToAnalyse != null && expected.FilesNotToAnalyse.Any())
                {
                    foreach(string unanalysedFile in expected.FilesNotToAnalyse)
                    {
                        string filePathToCheck = unanalysedFile;
                        if (!Path.IsPathRooted(filePathToCheck))
                        {
                            // Assume paths are relative to the project directory
                            filePathToCheck = Path.Combine(expected.FullDirectoryPath, filePathToCheck);
                        }
                        CollectionAssert.DoesNotContain(actualFiles, filePathToCheck, "Not expecting file to be included for analysis: {0}", filePathToCheck);
                    }
                }
            }
        }
 private static IList<string> GetExpectedAnalysisFiles(ProjectDescriptor descriptor)
 {
     return descriptor.Files.Where(f => f.ShouldBeAnalysed).Select(f => f.FilePath).ToList();
 }
        private void CheckProjectOutputFolder(ProjectDescriptor expected, string projectOutputFolder)
        {
            Assert.IsFalse(string.IsNullOrEmpty(projectOutputFolder), "Test error: projectOutputFolder should not be null/empty");
            Assert.IsTrue(Directory.Exists(projectOutputFolder), "Expected project folder does not exist: {0}", projectOutputFolder);

            // Check folder naming
            string folderName = Path.GetFileName(projectOutputFolder);
            Assert.IsTrue(folderName.StartsWith(expected.ProjectName), "Project output folder does not start with the project name. Expected: {0}, actual: {1}",
                expected.ProjectFolderName, folderName);

            // Check specific files
            ProjectInfo expectedProjectInfo = CreateExpectedProjectInfo(expected, projectOutputFolder);
            CheckProjectInfo(expectedProjectInfo, projectOutputFolder);
            CheckAnalysisFileList(expected, projectOutputFolder);

            // Check there are no other files
            List<string> allowedFiles = new List<string>(expectedProjectInfo.AnalysisResults.Select(ar => ar.Location));
            allowedFiles.Add(Path.Combine(projectOutputFolder, FileConstants.ProjectInfoFileName));
            AssertNoAdditionalFilesInFolder(projectOutputFolder, allowedFiles.ToArray());
        }
        /// <summary>
        /// Creates and builds a new Sonar-enabled project using the supplied descriptor.
        /// The method will check the build succeeded and that a single project output file was created.
        /// </summary>
        /// <returns>The full path of the project-specsific directory that was created during the build</returns>
        private string CreateAndBuildSonarProject(ProjectDescriptor descriptor, string rootOutputFolder, WellKnownProjectProperties preImportProperties)
        {
            ProjectRootElement projectRoot = BuildUtilities.CreateInitializedProjectRoot(this.TestContext, descriptor, preImportProperties);

            BuildLogger logger = new BuildLogger();

            // Act
            BuildResult result = BuildUtilities.BuildTargets(projectRoot, logger);

            // Assert
            BuildAssertions.AssertTargetSucceeded(result, TargetConstants.DefaultBuildTarget);

            // We expect the compiler to warn if there are no compiler inputs
            int expectedWarnings = (descriptor.ManagedSourceFiles.Any()) ? 0 : 1;
            logger.AssertExpectedErrorCount(0);
            logger.AssertExpectedWarningCount(expectedWarnings);

            logger.AssertExpectedTargetOrdering(
                TargetConstants.CategoriseProjectTarget,
                TargetConstants.DefaultBuildTarget,
                TargetConstants.CalculateFilesToAnalyzeTarget,
                TargetConstants.WriteProjectDataTarget);

            // Check expected folder structure exists
            CheckRootOutputFolder(rootOutputFolder);

            // Check expected project outputs
            Assert.AreEqual(1, Directory.EnumerateDirectories(rootOutputFolder).Count(), "Only expecting one child directory to exist under the root analysis output folder");
            ProjectInfoAssertions.AssertProjectInfoExists(rootOutputFolder, projectRoot.FullPath);

            return Directory.EnumerateDirectories(rootOutputFolder).Single();
        }
 private static void AddEmptyContentFile(ProjectDescriptor descriptor, string projectFolder)
 {
     string filePath = CreateEmptyFile(projectFolder, "txt");
     descriptor.AddContentFile(filePath, true);
 }
 private string AddEmptyAnalysedCodeFile(ProjectDescriptor descriptor, string projectFolder, string extension = "cs")
 {
     string filePath = CreateEmptyFile(projectFolder, extension);
     descriptor.AddCompileInputFile(filePath, true);
     return filePath;
 }
        /// <summary>
        /// Creates a new project using the supplied descriptor and properties, then
        /// execute the WriteProjectInfoFile task. The method will check the build succeeded
        /// and that a single project output file was created.
        /// </summary>
        /// <returns>The project info file that was created during the build</returns>
        private ProjectInfo ExecuteWriteProjectInfo(ProjectDescriptor descriptor, WellKnownProjectProperties preImportProperties, string rootOutputFolder)
        {
            ProjectRootElement projectRoot = CreateInitializedProject(descriptor, preImportProperties, rootOutputFolder);

            return this.ExecuteWriteProjectInfo(projectRoot, rootOutputFolder);
        }
        private ProjectRootElement CreateInitializedProject(ProjectDescriptor descriptor, WellKnownProjectProperties preImportProperties, string rootOutputFolder)
        {
            // Still need to set the conditions so the target is invoked
            preImportProperties.SonarQubeOutputPath = rootOutputFolder;

            // The temp folder needs to be set for the targets to succeed. Use the temp folder
            // if one has not been supplied.
            if (string.IsNullOrEmpty(preImportProperties.SonarQubeTempPath))
            {
                preImportProperties.SonarQubeTempPath = Path.GetTempPath();
            }

            // The config folder needs to be set for the targets to succeed. Use the temp folder
            // if one has not been supplied.
            if (string.IsNullOrEmpty(preImportProperties.SonarQubeConfigPath))
            {
                preImportProperties.SonarQubeConfigPath = Path.GetTempPath();
            }

            ProjectRootElement projectRoot = BuildUtilities.CreateInitializedProjectRoot(this.TestContext, descriptor, preImportProperties);

            return projectRoot;
        }