/// <inheritdoc /> public async Task <Context> Create(string repoPath) { // get historical changes var historicalChanges = gitEngine.GetGitHistoricalChangesToParent(repoPath); var context = DefaultContext.Value.ShallowCopy(); // if there are no history changes into the repo then return the default context. if (historicalChanges.Count == 0) { return(context); } // do the quantification based on th default context to get accurate numbers IPullRequestQuantifier prQuantifier = new PullRequestQuantifier(context); var quantifierInput = new QuantifierInput(); quantifierInput.Changes.AddRange(historicalChanges.Values.SelectMany(v => v)); await prQuantifier.Quantify(quantifierInput); context.AdditionPercentile = AdditionDeletionPercentile(historicalChanges, true); context.DeletionPercentile = AdditionDeletionPercentile(historicalChanges, false); context.FormulaPercentile = FormulaPercentile(historicalChanges); return(context); }
public async Task ToMarkdownCommentAsync_Successful() { // Arrange gitRepoHelpers.AddUntrackedFileToRepoWithNumLines("fake.cs", 2); gitRepoHelpers.AddUntrackedFileToRepoWithNumLines("fake2.cs", 4); gitRepoHelpers.CommitFilesToRepo(); gitRepoHelpers.AddUntrackedFileToRepoWithNumLines("fake.cs", 5); gitRepoHelpers.AddUntrackedFileToRepoWithNumLines("fake2.cs", 2); var quantifierInput = new QuantifierInput(); quantifierInput.Changes.AddRange(gitEngine.GetGitChanges(gitRepoHelpers.RepoPath)); var quantifyClient = new QuantifyClient(string.Empty); // Act var quantifierResult = await quantifyClient.Compute(quantifierInput); var comment = await quantifierResult.ToMarkdownCommentAsync( RepositoryLink, ContextFileLink, PullRequestLink, AuthorName); // Assert Assert.True(!string.IsNullOrWhiteSpace(comment)); Assert.StartsWith("### Pull Request Quantified", comment); }
private void CountTotalChanges( QuantifierInput quantifierInput, QuantifierResult quantifierResult) { quantifierResult.QuantifiedLinesAdded = quantifierInput.Changes.Sum(c => c.QuantifiedLinesAdded); quantifierResult.QuantifiedLinesDeleted = quantifierInput.Changes.Sum(c => c.QuantifiedLinesDeleted); }
/// <inheritdoc /> public async Task <QuantifierResult> Quantify(QuantifierInput quantifierInput) { ArgumentCheck.ParameterIsNotNull(quantifierInput, nameof(quantifierInput)); // execute quantifier for this context and this particular input return(await Compute(quantifierInput)); }
public async Task FilesWithoutExt_ShowFullPathInSummary() { // Arrange gitRepoHelpers.AddUntrackedFileToRepoWithNumLines("fake.cs", 2); gitRepoHelpers.AddUntrackedFileToRepoWithNumLines("fake2", 4); gitRepoHelpers.CommitFilesToRepo(); gitRepoHelpers.AddUntrackedFileToRepoWithNumLines("fake.cs", 5); gitRepoHelpers.AddUntrackedFileToRepoWithNumLines("fake2", 2); var quantifierInput = new QuantifierInput(); quantifierInput.Changes.AddRange(gitEngine.GetGitChanges(gitRepoHelpers.RepoPath)); var quantifyClient = new QuantifyClient(string.Empty); // Act var quantifierResult = await quantifyClient.Compute(quantifierInput); var comment = await quantifierResult.ToMarkdownCommentAsync( RepositoryLink, ContextFileLink, PullRequestLink, AuthorName); // Assert Assert.True(comment.IndexOf("fake2 : +0 -2", StringComparison.Ordinal) > -1); Assert.True(comment.IndexOf(".cs : +3 -0", StringComparison.Ordinal) > -1); }
private QuantifierInput GetChanges(string repoPath) { var quantifierInput = new QuantifierInput(); quantifierInput.Changes.AddRange(gitEngine.GetGitChanges(repoPath)); return(quantifierInput); }
/// <inheritdoc /> public async Task <QuantifierResult> Compute( string gitRepoPath, string commitSha1) { var quantifierInput = new QuantifierInput(); quantifierInput.Changes.AddRange(gitEngine.GetGitChange(gitRepoPath, commitSha1)); // quantify the changes return(await prQuantifier.Quantify(quantifierInput)); }
public static async Task Main(string[] args) { var commandLine = new CommandLine(args); contextFilePath = commandLine.ContextPath; if (commandLine.QuantifierInputFile != null) { // quantifier input is specified as a file // run once and exit var quantifierInput = new QuantifierInput(); var changes = JsonSerializer.Deserialize <List <GitFilePatch> >( await File.ReadAllTextAsync(commandLine.QuantifierInputFile)); changes?.ForEach(c => quantifierInput.Changes.Add(c)); quantifyClient = new QuantifyClient(contextFilePath); PrintResult( await quantifyClient.Compute(quantifierInput), commandLine.Output); } else { var gitRepoPath = commandLine.GitRepoPath ?? Environment.CurrentDirectory; var repoRootPath = Repository.Discover(gitRepoPath); // if no repo was found to this path then exit, don't crash if (string.IsNullOrWhiteSpace(repoRootPath)) { Console.WriteLine("GitRepoPath couldn't be found!"); return; } contextFilePath ??= Path.Combine( new DirectoryInfo(repoRootPath).Parent?.FullName, "prquantifier.yaml"); // run this as a service in case is configured otherwise only run once if (commandLine.Service) { #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed Task.Factory.StartNew(() => QuantifyLoop(repoRootPath, commandLine.Output)); #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed await Run(repoRootPath); } // in case service is not set or false then run once and exit quantifyClient = new QuantifyClient(contextFilePath); PrintResult( await quantifyClient.Compute(repoRootPath), commandLine.Output); } }
private async Task <QuantifierInput> GetQuantifierInputFromPullRequest(PullRequestEventPayload payload, IGitHubClientAdapter gitHubClientAdapter) { // get pull request files var pullRequestFiles = await gitHubClientAdapter.GetPullRequestFilesAsync( payload.Repository.Id, payload.PullRequest.Number); // convert to quantifier input var quantifierInput = new QuantifierInput(); foreach (var pullRequestFile in pullRequestFiles) { if (pullRequestFile.Patch == null) { continue; } var changeType = GitChangeType.Modified; switch (pullRequestFile.Status) { case "modified": break; case "added": changeType = GitChangeType.Added; break; case "deleted": changeType = GitChangeType.Deleted; break; } var fileExtension = !string.IsNullOrWhiteSpace(pullRequestFile.FileName) ? new FileInfo(pullRequestFile.FileName).Extension : string.Empty; var gitFilePatch = new GitFilePatch( pullRequestFile.FileName, fileExtension) { ChangeType = changeType, AbsoluteLinesAdded = pullRequestFile.Additions, AbsoluteLinesDeleted = pullRequestFile.Deletions, DiffContent = pullRequestFile.Patch, }; quantifierInput.Changes.Add(gitFilePatch); } return(quantifierInput); }
public async Task Quantify_NoChanges_ReturnsZeroCounts() { // Arrange var prQuantifier = new PullRequestQuantifier(context); var quantifierInput = new QuantifierInput(); quantifierInput.Changes.AddRange(gitEngine.GetGitChanges(gitRepoHelpers.RepoPath)); // Act var quantifierResult = await prQuantifier.Quantify(quantifierInput); // Assert Assert.Equal("No Changes", quantifierResult.Label); Assert.Equal(0, quantifierResult.QuantifiedLinesAdded); Assert.Equal(0, quantifierResult.QuantifiedLinesDeleted); }
public async Task Quantify_FilesWithPlusMinusContent() { // Arrange gitRepoHelpers.AddUntrackedFileToRepoWithContent("fake.cs", "-text\n+add\nnormal\n-44"); var prQuantifier = new PullRequestQuantifier(context); var quantifierInput = new QuantifierInput(); quantifierInput.Changes.AddRange(gitEngine.GetGitChanges(gitRepoHelpers.RepoPath)); // Act var quantifierResult = await prQuantifier.Quantify(quantifierInput); // Assert Assert.True(!string.IsNullOrEmpty(quantifierResult.Label)); Assert.Equal(4, quantifierResult.QuantifiedLinesAdded); Assert.Equal(0, quantifierResult.QuantifiedLinesDeleted); }
public async Task Quantify_UntrackedFilesOnly() { // Arrange gitRepoHelpers.AddUntrackedFileToRepoWithNumLines("fake.cs", 2); var prQuantifier = new PullRequestQuantifier(context); var quantifierInput = new QuantifierInput(); quantifierInput.Changes.AddRange(gitEngine.GetGitChanges(gitRepoHelpers.RepoPath)); // Act var quantifierResult = await prQuantifier.Quantify(quantifierInput); // Assert Assert.True(!string.IsNullOrEmpty(quantifierResult.Label)); Assert.Equal(2, quantifierResult.QuantifiedLinesAdded); Assert.Equal(0, quantifierResult.QuantifiedLinesDeleted); }
public async Task Quantify_IgnoreSpacesFalse() { // Arrange gitRepoHelpers.AddUntrackedFileToRepoWithContent("fake.cs", "some text\n\nmore text"); context.LanguageOptions.IgnoreSpaces = false; var prQuantifier = new PullRequestQuantifier(context); var quantifierInput = new QuantifierInput(); quantifierInput.Changes.AddRange(gitEngine.GetGitChanges(gitRepoHelpers.RepoPath)); // Act var quantifierResult = await prQuantifier.Quantify(quantifierInput); // Assert Assert.True(!string.IsNullOrEmpty(quantifierResult.Label)); Assert.Equal(3, quantifierResult.QuantifiedLinesAdded); Assert.Equal(0, quantifierResult.QuantifiedLinesDeleted); }
public async Task ToMarkdownCommentAsync_Options_CollapseChangesSummarySection( bool collapseChangesSummarySection) { // Arrange gitRepoHelpers.AddUntrackedFileToRepoWithNumLines("fake.cs", 2); gitRepoHelpers.AddUntrackedFileToRepoWithNumLines("fake2.cs", 4); gitRepoHelpers.CommitFilesToRepo(); gitRepoHelpers.AddUntrackedFileToRepoWithNumLines("fake.cs", 5); gitRepoHelpers.AddUntrackedFileToRepoWithNumLines("fake2.cs", 2); var quantifierInput = new QuantifierInput(); quantifierInput.Changes.AddRange(gitEngine.GetGitChanges(gitRepoHelpers.RepoPath)); var quantifyClient = new QuantifyClient(string.Empty); // Act var quantifierResult = await quantifyClient.Compute(quantifierInput); var comment = await quantifierResult.ToMarkdownCommentAsync( RepositoryLink, ContextFileLink, PullRequestLink, AuthorName, new MarkdownCommentOptions { CollapseChangesSummarySection = collapseChangesSummarySection }); // Assert Assert.True(!string.IsNullOrWhiteSpace(comment)); Assert.StartsWith("### Pull Request Quantified", comment); if (collapseChangesSummarySection) { Assert.True(comment.IndexOf( await File.ReadAllTextAsync(@"Data/AssertCommentSummaryCollapsed.txt"), StringComparison.Ordinal) > -1); } else { Assert.True(comment.IndexOf( await File.ReadAllTextAsync(@"Data/AssertCommentSummaryNotCollapsed.txt"), StringComparison.Ordinal) > -1); } }
public async Task ToMarkdownCommentAsync_Successful() { // Arrange var quantifierInput = new QuantifierInput(); quantifierInput.Changes.AddRange(gitEngine.GetGitChanges(gitRepoHelpers.RepoPath)); var quantifyClient = new QuantifyClient(string.Empty); // Act var quantifierResult = await quantifyClient.Compute(quantifierInput); var comment = await quantifierResult.ToMarkdownCommentAsync( RepositoryLink, ContextFileLink, PullRequestLink, AuthorName); // Assert Assert.True(!string.IsNullOrWhiteSpace(comment)); Assert.StartsWith("### ![](https://img.shields.io/static/v1?label=Quantified&message=Extra%20Small&color=green)", comment); }
public async Task ToMarkdownCommentAsync_Options_CollapseQuantificationDetailsSection(bool collapseQuantifiedDetailsSection) { // Arrange var quantifierInput = new QuantifierInput(); quantifierInput.Changes.AddRange(gitEngine.GetGitChanges(gitRepoHelpers.RepoPath)); var quantifyClient = new QuantifyClient(string.Empty); // Act var quantifierResult = await quantifyClient.Compute(quantifierInput); var comment = await quantifierResult.ToMarkdownCommentAsync( RepositoryLink, ContextFileLink, PullRequestLink, AuthorName, false, new MarkdownCommentOptions { CollapsePullRequestQuantifiedSection = collapseQuantifiedDetailsSection }); // Assert Assert.True(!string.IsNullOrWhiteSpace(comment)); Assert.StartsWith("### ![](https://img.shields.io/static/v1?label=Quantified&message=Extra%20Small&color=green)", comment); if (collapseQuantifiedDetailsSection) { Assert.True( comment.IndexOf( await File.ReadAllTextAsync(@"Data/AssertCommentSummaryCollapsed.txt"), StringComparison.Ordinal) > -1); } else { Assert.True( comment.IndexOf( await File.ReadAllTextAsync(@"Data/AssertCommentSummaryNotCollapsed.txt"), StringComparison.Ordinal) > -1); } }
public async Task ToMarkdownCommentAsync_IdealSizeCheck(int formulaLinesChanged, bool isIdeal) { // Arrange var quantifierInput = new QuantifierInput(); quantifierInput.Changes.AddRange(gitEngine.GetGitChanges(gitRepoHelpers.RepoPath)); var quantifyClient = new QuantifyClient(string.Empty); var quantifierResult = await quantifyClient.Compute(quantifierInput); quantifierResult.FormulaLinesChanged = formulaLinesChanged; var comment = await quantifierResult.ToMarkdownCommentAsync( RepositoryLink, ContextFileLink, PullRequestLink, AuthorName); // Assert var idealJobIndex = comment.IndexOf("Great job", StringComparison.Ordinal); Assert.True((isIdeal && idealJobIndex > -1) || (!isIdeal && idealJobIndex == -1)); }
private async Task <QuantifierResult> Compute(QuantifierInput quantifierInput) { QuantifierResult quantifierResult = null; await Task.Factory.StartNew( () => { quantifierResult = new QuantifierResult { QuantifierInput = quantifierInput }; // involve context and compute Parallel.ForEach(quantifierInput.Changes, ApplyContext); CountTotalChanges(quantifierInput, quantifierResult); // compute the label using the context percentile information and the thresholds SetLabel(quantifierResult); }); return(quantifierResult); }
public async Task QuantifyClient_MissingFormulaPercentileContext() { // Arrange gitRepoHelpers.AddUntrackedFileToRepoWithNumLines("fake.cs", 2); gitRepoHelpers.AddUntrackedFileToRepoWithNumLines("fake2.cs", 4); gitRepoHelpers.CommitFilesToRepo(); gitRepoHelpers.AddUntrackedFileToRepoWithNumLines("fake.cs", 5); gitRepoHelpers.AddUntrackedFileToRepoWithNumLines("fake2.cs", 2); var quantifierInput = new QuantifierInput(); quantifierInput.Changes.AddRange(gitEngine.GetGitChanges(gitRepoHelpers.RepoPath)); var quantifyClient = new QuantifyClient(@"Data/MissingFormulaPercentileContext.prquantifier.yaml"); // Act var quantifierResult = await quantifyClient.Compute(quantifierInput); // Assert Assert.True(!string.IsNullOrEmpty(quantifierResult.Label)); Assert.Equal(3, quantifierResult.QuantifiedLinesAdded); Assert.Equal(2, quantifierResult.QuantifiedLinesDeleted); Assert.Equal(0, quantifierResult.PercentileAddition); Assert.Equal(0, quantifierResult.PercentileDeletion); Assert.Equal(0, quantifierResult.FormulaPercentile); }
/// <inheritdoc /> public async Task <QuantifierResult> Compute(QuantifierInput quantifierInput) { // quantify the changes return(await prQuantifier.Quantify(quantifierInput)); }
private async Task <QuantifierResult> QuantifyPullRequest(PullRequestEventPayload payload) { var gitHubClientAdapter = await gitHubClientAdapterFactory.GetGitHubClientAdapterAsync( payload.Installation.Id, new Uri(payload.PullRequest.HtmlUrl).DnsSafeHost); // get pull request var pullRequest = await gitHubClientAdapter.GetPullRequestAsync( payload.Repository.Id, payload.PullRequest.Number); // get pull request files var pullRequestFiles = await gitHubClientAdapter.GetPullRequestFilesAsync( payload.Repository.Id, payload.PullRequest.Number); // convert to quantifier input var quantifierInput = new QuantifierInput(); foreach (var pullRequestFile in pullRequestFiles) { if (pullRequestFile.Patch == null) { continue; } var changeType = GitChangeType.Modified; switch (pullRequestFile.Status) { case "modified": break; case "added": changeType = GitChangeType.Added; break; case "deleted": changeType = GitChangeType.Deleted; break; } var fileExtension = !string.IsNullOrWhiteSpace(pullRequestFile.FileName) ? new FileInfo(pullRequestFile.FileName).Extension : string.Empty; var gitFilePatch = new GitFilePatch( pullRequestFile.FileName, fileExtension) { ChangeType = changeType, AbsoluteLinesAdded = pullRequestFile.Additions, AbsoluteLinesDeleted = pullRequestFile.Deletions, DiffContent = pullRequestFile.Patch, }; quantifierInput.Changes.Add(gitFilePatch); } // get context if present string context = null; try { var rawContext = await gitHubClientAdapter.GetRawFileAsync( payload.Repository.Owner.Login, payload.Repository.Name, "/prquantifier.yaml"); context = Encoding.UTF8.GetString(rawContext); } catch (NotFoundException) { } catch { // ignored } var quantifyClient = new QuantifyClient(context); var quantifierClientResult = await quantifyClient.Compute(quantifierInput); // create a new label in the repository if doesn't exist try { var existingLabel = await gitHubClientAdapter.GetLabelAsync( payload.Repository.Id, quantifierClientResult.Label); } catch (NotFoundException) { // create new label var color = Color.FromName(quantifierClientResult.Color); await gitHubClientAdapter.CreateLabelAsync( payload.Repository.Id, new NewLabel(quantifierClientResult.Label, ConvertToHex(color))); } // apply label to pull request await gitHubClientAdapter.ApplyLabelAsync( payload.Repository.Id, payload.PullRequest.Number, new[] { quantifierClientResult.Label }); // create a comment on the issue var defaultBranch = payload.Repository.DefaultBranch; var quantifierContextLink = $"{payload.Repository.HtmlUrl}/blob/{defaultBranch}/prquantifier.yaml"; var comment = await quantifierClientResult.ToMarkdownCommentAsync( payload.Repository.HtmlUrl, quantifierContextLink, payload.PullRequest.HtmlUrl, payload.PullRequest.User.Login); await gitHubClientAdapter.CreateIssueCommentAsync( payload.Repository.Id, payload.PullRequest.Number, comment); return(quantifierClientResult); }
private static async Task RunQuantifier( QuantifyClient quantifyClient, string resultFile, string repoPath) { var repoRoot = LibGit2Sharp.Repository.Discover(repoPath); if (repoRoot == null) { Console.WriteLine($"No repo found at {repoPath}"); return; } using var repo = new LibGit2Sharp.Repository(repoRoot); var commits = repo.Commits.QueryBy( new CommitFilter { FirstParentOnly = true }); Console.WriteLine($"Total commits to evaluate : {commits.Count()}. Repository path {repoPath}."); var sw = new Stopwatch(); sw.Reset(); sw.Start(); var batchSize = 100; var quantifierResults = new ConcurrentDictionary <string, QuantifierResult>(); for (int page = 0; page < (commits.Count() / batchSize) + 1; page++) { var commitBatch = commits.Skip(batchSize * page).Take(batchSize); var quantifyTasks = commitBatch.Select( async commit => { try { var quantifierInput = new QuantifierInput(); var firstParent = commit.Parents.FirstOrDefault(); if (firstParent != null) { var patch = repo.Diff.Compare <Patch>(firstParent.Tree, commit.Tree); foreach (var gitFilePatch in patch.GetGitFilePatch()) { quantifierInput.Changes.Add(gitFilePatch); } } var quantifierResult = await quantifyClient.Compute(quantifierInput); quantifierResults.TryAdd(commit.Sha, quantifierResult); } catch (Exception e) { Console.WriteLine(e); } }); await Task.WhenAll(quantifyTasks); await AddResultsToFile(quantifierResults, resultFile); quantifierResults = new ConcurrentDictionary <string, QuantifierResult>(); Console.WriteLine($"{page * batchSize}/{commits.Count()} {sw.Elapsed}"); } Console.WriteLine("Completed!"); }