Skip to content

Commit 91ef45f

Browse files
committed
Fix CoverageService: only analyze classes present in exec data to avoid duplicate class errors
1 parent 6c65e10 commit 91ef45f

2 files changed

Lines changed: 70 additions & 10 deletions

File tree

  • plugins/com.github.gradusnikov.eclipse.plugin.assistai.main/src/com/github/gradusnikov/eclipse/assistai/mcp/services
  • tests/com.github.gradusnikov.eclipse.plugin.assistai.main.tests/src/com/github/gradusnikov/eclipse/plugin/assistai/mcp/services

plugins/com.github.gradusnikov.eclipse.plugin.assistai.main/src/com/github/gradusnikov/eclipse/assistai/mcp/services/CoverageService.java

Lines changed: 67 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@
66
import java.nio.file.Files;
77
import java.nio.file.Path;
88
import java.util.ArrayList;
9+
import java.util.HashSet;
910
import java.util.List;
11+
import java.util.Set;
12+
import java.util.stream.Collectors;
1013

1114
import org.eclipse.core.resources.IProject;
1215
import org.eclipse.core.resources.ResourcesPlugin;
@@ -188,28 +191,85 @@ private String analyzeCoverage( String execFilePath, String projectName ) throws
188191
reader.read();
189192
}
190193

194+
Set<String> executedClassNames = executionDataStore.getContents().stream()
195+
.map( data -> data.getName() )
196+
.collect( Collectors.toSet() );
197+
198+
if ( executedClassNames.isEmpty() )
199+
{
200+
return "\n--- Coverage ---\nNo execution data found in coverage file.\n";
201+
}
202+
191203
CoverageBuilder coverageBuilder = new CoverageBuilder();
192204
Analyzer analyzer = new Analyzer( executionDataStore, coverageBuilder );
205+
Set<String> analyzedClasses = new HashSet<>();
193206

194207
IProject[] allProjects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
195208
for ( IProject project : allProjects )
196209
{
197-
if ( !project.isOpen() || !project.hasNature( JavaCore.NATURE_ID ) )
210+
try
198211
{
199-
continue;
212+
if ( !project.isOpen() || !project.hasNature( JavaCore.NATURE_ID ) )
213+
{
214+
continue;
215+
}
216+
IJavaProject javaProject = JavaCore.create( project );
217+
File outputLocation = project.getLocation().append(
218+
javaProject.getOutputLocation().removeFirstSegments( 1 ) ).toFile();
219+
if ( !outputLocation.exists() )
220+
{
221+
continue;
222+
}
223+
analyzeMatchingClasses( outputLocation, outputLocation, executedClassNames, analyzedClasses, analyzer );
200224
}
201-
IJavaProject javaProject = JavaCore.create( project );
202-
File outputLocation = project.getLocation().append(
203-
javaProject.getOutputLocation().removeFirstSegments( 1 ) ).toFile();
204-
if ( outputLocation.exists() )
225+
catch ( Exception e )
205226
{
206-
analyzer.analyzeAll( outputLocation );
227+
if ( logger != null )
228+
{
229+
logger.warn( "Skipping project '" + project.getName() + "' during coverage analysis: " + e.getMessage() );
230+
}
207231
}
208232
}
209233

210234
return formatReport( coverageBuilder, projectName );
211235
}
212236

237+
private void analyzeMatchingClasses( File root, File dir, Set<String> executedClassNames,
238+
Set<String> analyzedClasses, Analyzer analyzer ) throws IOException
239+
{
240+
File[] files = dir.listFiles();
241+
if ( files == null )
242+
{
243+
return;
244+
}
245+
for ( File file : files )
246+
{
247+
if ( file.isDirectory() )
248+
{
249+
analyzeMatchingClasses( root, file, executedClassNames, analyzedClasses, analyzer );
250+
}
251+
else if ( file.getName().endsWith( ".class" ) )
252+
{
253+
String relativePath = root.toPath().relativize( file.toPath() ).toString().replace( '\\', '/' );
254+
String className = relativePath.substring( 0, relativePath.length() - 6 );
255+
if ( executedClassNames.contains( className ) && analyzedClasses.add( className ) )
256+
{
257+
try ( FileInputStream fis = new FileInputStream( file ) )
258+
{
259+
analyzer.analyzeAll( fis, className );
260+
}
261+
catch ( Exception e )
262+
{
263+
if ( logger != null )
264+
{
265+
logger.warn( "Skipping class '" + className + "': " + e.getMessage() );
266+
}
267+
}
268+
}
269+
}
270+
}
271+
}
272+
213273
private String formatReport( CoverageBuilder coverageBuilder, String projectName )
214274
{
215275
StringBuilder sb = new StringBuilder();

tests/com.github.gradusnikov.eclipse.plugin.assistai.main.tests/src/com/github/gradusnikov/eclipse/plugin/assistai/mcp/services/CoverageServiceTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ public void testFindLatestCoverageFile_returnsNullOrPath()
282282

283283
@Test
284284
@Order( 9 )
285-
public void testFormatCoverageInfo_withValidExecFile_andProject_returnsCoverageReport() throws Exception
285+
public void testFormatCoverageInfo_withEmptyExecFile_andProject_returnsNoDataMessage() throws Exception
286286
{
287287
assumeTrue( coverageService.isCoverageAvailable(),
288288
"Skipping: EclEmma/JaCoCo not installed" );
@@ -297,8 +297,8 @@ public void testFormatCoverageInfo_withValidExecFile_andProject_returnsCoverageR
297297
try
298298
{
299299
String result = coverageService.formatCoverageInfo( tempExec.toString(), TEST_PROJECT_NAME );
300-
assertTrue( result.contains( "--- Coverage Report ---" ) );
301-
assertTrue( result.contains( "All classes fully covered." ) || result.contains( "Classes with incomplete coverage:" ) );
300+
assertTrue( result.contains( "--- Coverage ---" ) );
301+
assertTrue( result.contains( "No execution data found" ) );
302302
}
303303
finally
304304
{

0 commit comments

Comments
 (0)