Skip to content

Commit dc070bc

Browse files
konardclaude
andcommitted
Implement bug reproduction simplification feature
Added comprehensive bug reproduction simplification system that analyzes repositories to identify minimal code needed to reproduce bugs: - Added SimplifyBugReproductionTrigger that responds to issue titles containing "simplify bug reproduction", "minimize reproduction", or "reduce bug example" - Implemented BugReproductionAnalyzer with static code analysis capabilities: * Identifies entry points (main files, tests, configs) * Maps dependencies between files using import/using statements * Analyzes bug reports for file/method references * Builds dependency graphs to find critical code paths * Calculates confidence scores and generates warnings - Added support for multiple programming languages (C#, JS, TS, Python, Java) - Created automated branch creation with simplified codebase - Added comprehensive test coverage with unit tests - Created detailed usage documentation and examples The system generates detailed reports showing which files were kept/removed with explanations, helping developers focus on essential bug reproduction code. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent a0db15b commit dc070bc

File tree

10 files changed

+1610
-1
lines changed

10 files changed

+1610
-1
lines changed
Lines changed: 321 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,321 @@
1+
using System.Collections.Generic;
2+
using System.Threading.Tasks;
3+
using Xunit;
4+
using Octokit;
5+
using Storage.Analysis;
6+
7+
namespace Platform.Bot.Tests
8+
{
9+
public class BugReproductionAnalyzerTests
10+
{
11+
private readonly BugReproductionAnalyzer _analyzer;
12+
13+
public BugReproductionAnalyzerTests()
14+
{
15+
_analyzer = new BugReproductionAnalyzer();
16+
}
17+
18+
[Fact]
19+
public async Task AnalyzeBugReproduction_WithMainFile_IdentifiesAsEntryPoint()
20+
{
21+
// Arrange
22+
var files = new List<RepositoryFile>
23+
{
24+
new RepositoryFile
25+
{
26+
Path = "Program.cs",
27+
Content = "using System;\n\nclass Program\n{\n static void Main(string[] args)\n {\n Console.WriteLine(\"Hello World!\");\n }\n}",
28+
IsEntryPoint = true
29+
}
30+
};
31+
32+
var issue = CreateMockIssue("Test bug", "This is a test bug description");
33+
34+
// Act
35+
var result = await _analyzer.AnalyzeBugReproduction(files, issue);
36+
37+
// Assert
38+
Assert.Contains("Program.cs", result.EntryPoints);
39+
Assert.Contains("Program.cs", result.CriticalFiles);
40+
}
41+
42+
[Fact]
43+
public async Task AnalyzeBugReproduction_WithTestFile_IdentifiesAsTestFile()
44+
{
45+
// Arrange
46+
var files = new List<RepositoryFile>
47+
{
48+
new RepositoryFile
49+
{
50+
Path = "Tests/BugTest.cs",
51+
Content = "[Test]\npublic void TestBug()\n{\n // Test code\n}",
52+
IsTestFile = true
53+
}
54+
};
55+
56+
var issue = CreateMockIssue("Test bug", "This is a test bug description");
57+
58+
// Act
59+
var result = await _analyzer.AnalyzeBugReproduction(files, issue);
60+
61+
// Assert
62+
Assert.Contains("Tests/BugTest.cs", result.TestFiles);
63+
Assert.Contains("Tests/BugTest.cs", result.CriticalFiles);
64+
}
65+
66+
[Fact]
67+
public async Task AnalyzeBugReproduction_WithFileReferencedInBugReport_IdentifiesAsCritical()
68+
{
69+
// Arrange
70+
var files = new List<RepositoryFile>
71+
{
72+
new RepositoryFile
73+
{
74+
Path = "BuggyClass.cs",
75+
Content = "public class BuggyClass\n{\n public void ProblematicMethod()\n {\n throw new Exception(\"Bug!\");\n }\n}"
76+
}
77+
};
78+
79+
var issue = CreateMockIssue("Bug in BuggyClass", "The BuggyClass.cs file contains a problematic method that throws an exception");
80+
81+
// Act
82+
var result = await _analyzer.AnalyzeBugReproduction(files, issue);
83+
84+
// Assert
85+
Assert.Contains("BuggyClass.cs", result.CriticalFiles);
86+
Assert.Contains("BuggyClass.cs", result.FileReasons.Keys);
87+
}
88+
89+
[Fact]
90+
public async Task SimplifyCodebase_RemovesNonCriticalFiles()
91+
{
92+
// Arrange
93+
var originalFiles = new List<RepositoryFile>
94+
{
95+
new RepositoryFile { Path = "Program.cs", Content = "static void Main() {}", IsEntryPoint = true },
96+
new RepositoryFile { Path = "README.md", Content = "# Documentation" },
97+
new RepositoryFile { Path = "Examples/Sample.cs", Content = "// Example code" }
98+
};
99+
100+
var analysisResult = new BugAnalysisResult();
101+
analysisResult.CriticalFiles.Add("Program.cs");
102+
analysisResult.RemovedFiles.Add("README.md");
103+
analysisResult.RemovedFiles.Add("Examples/Sample.cs");
104+
105+
// Act
106+
var simplifiedFiles = await _analyzer.SimplifyCodebase(originalFiles, analysisResult);
107+
108+
// Assert
109+
Assert.Single(simplifiedFiles);
110+
Assert.Contains(simplifiedFiles, f => f.Path == "Program.cs");
111+
Assert.DoesNotContain(simplifiedFiles, f => f.Path == "README.md");
112+
Assert.DoesNotContain(simplifiedFiles, f => f.Path == "Examples/Sample.cs");
113+
}
114+
115+
[Fact]
116+
public async Task AnalyzeBugReproduction_WithMultipleFiles_CalculatesConfidenceScore()
117+
{
118+
// Arrange
119+
var files = new List<RepositoryFile>
120+
{
121+
new RepositoryFile
122+
{
123+
Path = "Program.cs",
124+
Content = "static void Main() {}",
125+
IsEntryPoint = true
126+
},
127+
new RepositoryFile
128+
{
129+
Path = "Tests/UnitTest.cs",
130+
Content = "[Test] public void TestMethod() {}",
131+
IsTestFile = true
132+
},
133+
new RepositoryFile
134+
{
135+
Path = "README.md",
136+
Content = "# Documentation"
137+
}
138+
};
139+
140+
var issue = CreateMockIssue("Test issue", "Test description");
141+
142+
// Act
143+
var result = await _analyzer.AnalyzeBugReproduction(files, issue);
144+
145+
// Assert
146+
Assert.True(result.ConfidenceScore >= 0.0 && result.ConfidenceScore <= 1.0);
147+
Assert.True(result.ConfidenceScore > 0.8); // Should be high confidence with entry point and test file
148+
}
149+
150+
private Issue CreateMockIssue(string title, string body)
151+
{
152+
// Create a mock issue for testing
153+
// Note: In a real implementation, you'd want to use a proper mocking framework
154+
// For this simple test, we'll create a minimal implementation
155+
return new Issue(
156+
url: "https://github.com/test/repo/issues/1",
157+
htmlUrl: "https://github.com/test/repo/issues/1",
158+
commentsUrl: "https://github.com/test/repo/issues/1/comments",
159+
eventsUrl: "https://github.com/test/repo/issues/1/events",
160+
number: 1,
161+
state: ItemState.Open,
162+
title: title,
163+
body: body,
164+
user: null,
165+
labels: new List<Label>().AsReadOnly(),
166+
assignee: null,
167+
assignees: new List<User>().AsReadOnly(),
168+
milestone: null,
169+
comments: 0,
170+
pullRequest: null,
171+
closedAt: null,
172+
createdAt: System.DateTimeOffset.Now,
173+
updatedAt: System.DateTimeOffset.Now,
174+
closedBy: null,
175+
id: 1,
176+
nodeId: "node1",
177+
locked: false,
178+
repository: CreateMockRepository(),
179+
reactions: null,
180+
activeLockReason: null,
181+
stateReason: null
182+
);
183+
}
184+
185+
private Repository CreateMockRepository()
186+
{
187+
return new Repository(
188+
url: "https://github.com/test/repo",
189+
htmlUrl: "https://github.com/test/repo",
190+
cloneUrl: "https://github.com/test/repo.git",
191+
gitUrl: "git://github.com/test/repo.git",
192+
sshUrl: "[email protected]:test/repo.git",
193+
svnUrl: "https://github.com/test/repo",
194+
mirrorUrl: null,
195+
id: 1,
196+
nodeId: "node1",
197+
name: "repo",
198+
fullName: "test/repo",
199+
description: "Test repository",
200+
homepage: "",
201+
language: "C#",
202+
isPrivate: false,
203+
fork: false,
204+
forksCount: 0,
205+
stargazersCount: 0,
206+
watchersCount: 0,
207+
size: 100,
208+
defaultBranch: "main",
209+
openIssuesCount: 1,
210+
topics: new List<string>().AsReadOnly(),
211+
hasIssues: true,
212+
hasProjects: true,
213+
hasWiki: true,
214+
hasPages: false,
215+
hasDownloads: true,
216+
archived: false,
217+
disabled: false,
218+
pushedAt: System.DateTimeOffset.Now,
219+
createdAt: System.DateTimeOffset.Now,
220+
updatedAt: System.DateTimeOffset.Now,
221+
permissions: null,
222+
owner: CreateMockUser(),
223+
parent: null,
224+
source: null,
225+
templatesUrl: null,
226+
subscribersCount: 1,
227+
networkCount: 1,
228+
license: null,
229+
forksUrl: null,
230+
keysUrl: null,
231+
collaboratorsUrl: null,
232+
teamsUrl: null,
233+
hooksUrl: null,
234+
issueEventsUrl: null,
235+
eventsUrl: null,
236+
assigneesUrl: null,
237+
branchesUrl: null,
238+
tagsUrl: null,
239+
blobsUrl: null,
240+
gitTagsUrl: null,
241+
gitRefsUrl: null,
242+
treesUrl: null,
243+
statusesUrl: null,
244+
languagesUrl: null,
245+
stargazersUrl: null,
246+
contributorsUrl: null,
247+
subscribersUrl: null,
248+
commitsUrl: null,
249+
gitCommitsUrl: null,
250+
commentsUrl: null,
251+
issueCommentUrl: null,
252+
contentsUrl: null,
253+
compareUrl: null,
254+
mergesUrl: null,
255+
archiveUrl: null,
256+
downloadsUrl: null,
257+
issuesUrl: null,
258+
pullsUrl: null,
259+
milestonesUrl: null,
260+
notificationsUrl: null,
261+
labelsUrl: null,
262+
releasesUrl: null,
263+
deploymentsUrl: null,
264+
allowRebaseMerge: true,
265+
allowSquashMerge: true,
266+
allowMergeCommit: true,
267+
deleteBranchOnMerge: false,
268+
allowAutoMerge: false,
269+
allowUpdateBranch: false,
270+
useSquashPrTitleAsDefault: false,
271+
squashMergeCommitTitle: null,
272+
squashMergeCommitMessage: null,
273+
mergeCommitTitle: null,
274+
mergeCommitMessage: null,
275+
visibility: RepositoryVisibility.Public,
276+
webCommitSignoffRequired: false,
277+
securityAndAnalysis: null,
278+
customProperties: null
279+
);
280+
}
281+
282+
private User CreateMockUser()
283+
{
284+
return new User(
285+
avatarUrl: "https://github.com/images/error/octocat_happy.gif",
286+
bio: "",
287+
blog: "",
288+
collaborators: 0,
289+
company: "",
290+
createdAt: System.DateTimeOffset.Now,
291+
diskUsage: 0,
292+
293+
followers: 0,
294+
following: 0,
295+
hireable: false,
296+
htmlUrl: "https://github.com/test",
297+
totalPrivateRepos: 0,
298+
id: 1,
299+
nodeId: "node1",
300+
location: "",
301+
login: "test",
302+
name: "Test User",
303+
ownedPrivateRepos: 0,
304+
plan: null,
305+
privateGists: 0,
306+
publicGists: 0,
307+
publicRepos: 1,
308+
updatedAt: System.DateTimeOffset.Now,
309+
url: "https://api.github.com/users/test",
310+
permissions: null,
311+
siteAdmin: false,
312+
ldapDistinguishedName: null,
313+
suspendedAt: null,
314+
type: AccountType.User,
315+
facebookId: null,
316+
gravatar: null,
317+
twitterUsername: null
318+
);
319+
}
320+
}
321+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8</TargetFramework>
5+
<Nullable>enable</Nullable>
6+
<IsPackable>false</IsPackable>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
11+
<PackageReference Include="xunit" Version="2.4.2" />
12+
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
13+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
14+
<PrivateAssets>all</PrivateAssets>
15+
</PackageReference>
16+
<PackageReference Include="Moq" Version="4.20.69" />
17+
</ItemGroup>
18+
19+
<ItemGroup>
20+
<ProjectReference Include="..\Platform.Bot\Platform.Bot.csproj" />
21+
<ProjectReference Include="..\Interfaces\Interfaces.csproj" />
22+
<ProjectReference Include="..\Storage\Storage.csproj" />
23+
</ItemGroup>
24+
25+
</Project>

0 commit comments

Comments
 (0)