private static void CheckIssue(
                ICodeAnalysisIssue issue,
                string affectedFileRelativePath,
                int?line,
                string rule,
                string ruleUrl,
                int priority,
                string message)
            {
                if (issue.AffectedFileRelativePath == null)
                {
                    affectedFileRelativePath.ShouldBeNull();
                }
                else
                {
                    issue.AffectedFileRelativePath.ToString().ShouldBe(new FilePath(affectedFileRelativePath).ToString());
                    issue.AffectedFileRelativePath.IsRelative.ShouldBe(true, "Issue path is not relative");
                }

                issue.Line.ShouldBe(line);
                issue.Rule.ShouldBe(rule);

                if (issue.RuleUrl == null)
                {
                    ruleUrl.ShouldBeNull();
                }
                else
                {
                    issue.RuleUrl.ToString().ShouldBe(ruleUrl);
                }

                issue.Priority.ShouldBe(priority);
                issue.Message.ShouldBe(message);
            }
Пример #2
0
 /// <summary>
 /// Checks if there's already a comment for an issue.
 /// </summary>
 /// <param name="issue">Issue to check.</param>
 /// <param name="issueComments">List of existing comments.</param>
 /// <returns>True if there are already comments for an issue.</returns>
 private static bool IssueHasMatchingComments(
     ICodeAnalysisIssue issue,
     IDictionary <ICodeAnalysisIssue, IEnumerable <IPrcaDiscussionComment> > issueComments)
 {
     return
         (issueComments.ContainsKey(issue) &&
          issueComments[issue].Any());
 }
Пример #3
0
 /// <summary>
 /// Checks if file path from an <see cref="ICodeAnalysisIssue"/> and <see cref="IPrcaDiscussionThread"/>
 /// are matching.
 /// </summary>
 /// <param name="issue">Issue to check.</param>
 /// <param name="thread">Comment thread to check.</param>
 /// <returns><c>true</c> if both paths are matching or if both paths are set to <c>null</c>.</returns>
 private static bool FilePathsAreMatching(ICodeAnalysisIssue issue, IPrcaDiscussionThread thread)
 {
     return
         ((issue.AffectedFileRelativePath == null && thread.AffectedFileRelativePath == null) ||
          (
              issue.AffectedFileRelativePath != null &&
              thread.AffectedFileRelativePath != null &&
              thread.AffectedFileRelativePath.ToString() == issue.AffectedFileRelativePath.ToString()));
 }
Пример #4
0
        /// <summary>
        /// Returns all matching comments from discussion threads for an issue.
        /// Comments are considered matching if they fulfill all of the following conditions:
        /// * The thread is active.
        /// * The thread is for the same file.
        /// * The thread was created by the same logic, i.e. the same <code>commentSource</code>.
        /// * The comment contains the same content.
        /// </summary>
        /// <remarks>
        /// The line cannot be used since comments can move around.
        /// </remarks>
        /// <param name="reportIssuesToPullRequestSettings">Settings to use.</param>
        /// <param name="issue">Issue for which the comments should be returned.</param>
        /// <param name="existingThreads">Existing discussion threads on the pull request.</param>
        /// <returns>Active comments for the issue.</returns>
        private IEnumerable <IPrcaDiscussionComment> GetMatchingComments(
            ReportIssuesToPullRequestSettings reportIssuesToPullRequestSettings,
            ICodeAnalysisIssue issue,
            IList <IPrcaDiscussionThread> existingThreads)
        {
            issue.NotNull(nameof(issue));
            existingThreads.NotNull(nameof(existingThreads));

            // Select threads that are active, that point to the same file and have been marked with the given comment source.
            var matchingThreads =
                (from thread in existingThreads
                 where
                 thread != null &&
                 thread.Status == PrcaDiscussionStatus.Active &&
                 FilePathsAreMatching(issue, thread) &&
                 thread.CommentSource == reportIssuesToPullRequestSettings.CommentSource
                 select thread).ToList();

            if (matchingThreads.Any())
            {
                this.log.Verbose(
                    "Found {0} matching thread(s) for the issue at {1} line {2}",
                    matchingThreads.Count,
                    issue.AffectedFileRelativePath,
                    issue.Line);
            }

            var result = new List <IPrcaDiscussionComment>();

            foreach (var thread in matchingThreads)
            {
                // Select comments from this thread that are not deleted and that match the given message.
                var matchingComments =
                    (from comment in thread.Comments
                     where
                     comment != null &&
                     !comment.IsDeleted &&
                     comment.Content == issue.Message
                     select
                     comment).ToList();

                if (matchingComments.Any())
                {
                    this.log.Verbose(
                        "Found {0} matching comment(s) for the issue at {1} line {2}",
                        matchingComments.Count,
                        issue.AffectedFileRelativePath,
                        issue.Line);
                }

                result.AddRange(matchingComments);
            }

            return(result);
        }
        private static void AddCodeFlowProperties(
            ICodeAnalysisIssue issue,
            int iterationId,
            int changeTrackingId,
            PropertiesCollection properties)
        {
            issue.NotNull(nameof(issue));
            properties.NotNull(nameof(properties));

            properties.Add("Microsoft.VisualStudio.Services.CodeReview.ItemPath", "/" + issue.AffectedFileRelativePath);
            properties.Add("Microsoft.VisualStudio.Services.CodeReview.Right.StartLine", issue.Line);
            properties.Add("Microsoft.VisualStudio.Services.CodeReview.Right.EndLine", issue.Line);
            properties.Add("Microsoft.VisualStudio.Services.CodeReview.Right.StartOffset", 0);
            properties.Add("Microsoft.VisualStudio.Services.CodeReview.Right.EndOffset", 1);
            properties.Add("Microsoft.VisualStudio.Services.CodeReview.FirstComparingIteration", iterationId);
            properties.Add("Microsoft.VisualStudio.Services.CodeReview.SecondComparingIteration", iterationId);
            properties.Add("Microsoft.VisualStudio.Services.CodeReview.ChangeTrackingId", changeTrackingId);
        }
        private bool AddThreadProperties(
            GitPullRequestCommentThread thread,
            GitPullRequestIterationChanges changes,
            ICodeAnalysisIssue issue,
            int iterationId,
            string commentSource)
        {
            thread.NotNull(nameof(thread));
            changes.NotNull(nameof(changes));
            issue.NotNull(nameof(issue));

            var properties = new PropertiesCollection();

            if (this.pullRequest.CodeReviewId > 0)
            {
                var changeTrackingId =
                    this.TryGetCodeFlowChangeTrackingId(changes, issue.AffectedFileRelativePath);
                if (changeTrackingId < 0)
                {
                    // Don't post comment if we couldn't determine the change.
                    return(false);
                }

                AddCodeFlowProperties(issue, iterationId, changeTrackingId, properties);
            }
            else
            {
                throw new NotSupportedException("Legacy code reviews are not supported.");
            }

            // A VSTS UI extension will recognize this and format the comments differently.
            properties.Add("CodeAnalysisThreadType", "CodeAnalysisIssue");

            thread.Properties = properties;

            // Add a custom property to be able to distinguish all comments created this way.
            thread.SetCommentSource(commentSource);

            // Add a custom property to be able to return issue message from existing threads,
            // without any formatting done by this addin, back to Cake.Prca.
            thread.SetIssueMessage(issue.Message);

            return(true);
        }
Пример #7
0
        /// <summary>
        /// Returns the content for an issue.
        /// </summary>
        /// <param name="issue">Issue for which the content should be returned.</param>
        /// <returns>Comment content for the issue.</returns>
        public static string GetContent(ICodeAnalysisIssue issue)
        {
            var result = issue.Message;

            if (string.IsNullOrWhiteSpace(issue.Rule))
            {
                return(result);
            }

            var ruleContent = issue.Rule;

            if (issue.RuleUrl != null)
            {
                ruleContent = $"[{issue.Rule}]({issue.RuleUrl})";
            }

            result = $"{ruleContent}: {result}";

            return(result);
        }