@@ -32,8 +32,8 @@ public string Report(CoverageResult result)
3232 coverage . Add ( new XAttribute ( "timestamp" , ( int ) ( DateTime . UtcNow - new DateTime ( 1970 , 1 , 1 ) ) . TotalSeconds ) ) ;
3333
3434 XElement sources = new XElement ( "sources" ) ;
35- var rootDirs = GetRootDirs ( result . Modules , result . UseSourceLink ) . ToList ( ) ;
36- rootDirs . ForEach ( x => sources . Add ( new XElement ( "source" , x ) ) ) ;
35+ var absolutePaths = GetBasePaths ( result . Modules , result . UseSourceLink ) . ToList ( ) ;
36+ absolutePaths . ForEach ( x => sources . Add ( new XElement ( "source" , x ) ) ) ;
3737
3838 XElement packages = new XElement ( "packages" ) ;
3939 foreach ( var module in result . Modules )
@@ -51,7 +51,7 @@ public string Report(CoverageResult result)
5151 {
5252 XElement @class = new XElement ( "class" ) ;
5353 @class . Add ( new XAttribute ( "name" , cls . Key ) ) ;
54- @class . Add ( new XAttribute ( "filename" , GetRelativePathFromBase ( rootDirs , document . Key , result . UseSourceLink ) ) ) ;
54+ @class . Add ( new XAttribute ( "filename" , GetRelativePathFromBase ( absolutePaths , document . Key , result . UseSourceLink ) ) ) ;
5555 @class . Add ( new XAttribute ( "line-rate" , ( summary . CalculateLineCoverage ( cls . Value ) . Percent / 100 ) . ToString ( CultureInfo . InvariantCulture ) ) ) ;
5656 @class . Add ( new XAttribute ( "branch-rate" , ( summary . CalculateBranchCoverage ( cls . Value ) . Percent / 100 ) . ToString ( CultureInfo . InvariantCulture ) ) ) ;
5757 @class . Add ( new XAttribute ( "complexity" , summary . CalculateCyclomaticComplexity ( cls . Value ) ) ) ;
@@ -133,28 +133,70 @@ public string Report(CoverageResult result)
133133 return Encoding . UTF8 . GetString ( stream . ToArray ( ) ) ;
134134 }
135135
136- private static IEnumerable < string > GetRootDirs ( Modules modules , bool useSourceLink )
136+ private static IEnumerable < string > GetBasePaths ( Modules modules , bool useSourceLink )
137137 {
138+ /*
139+ Workflow
140+
141+ Path1 c:\dir1\dir2\file1.cs
142+ Path2 c:\dir1\file2.cs
143+ Path3 e:\dir1\file2.cs
144+
145+ 1) Search for root dir
146+ c:\ -> c:\dir1\dir2\file1.cs
147+ c:\dir1\file2.cs
148+ e:\ -> e:\dir1\file2.cs
149+
150+ 2) Split path on directory separator i.e. for record c:\ ordered ascending by fragment elements
151+ Path1 = [c:|dir1|file2.cs]
152+ Path2 = [c:|dir1|dir2|file1.cs]
153+
154+ 3) Find longest shared path comparing indexes
155+ Path1[0] = Path2[0], ..., PathY[0] -> add to final fragment list
156+ Path1[n] = Path2[n], ..., PathY[n] -> add to final fragment list
157+ Path1[n+1] != Path2[n+1], ..., PathY[n+1] -> break, Path1[n] was last shared fragment
158+
159+ 4) Concat created fragment list
160+ */
138161 if ( useSourceLink )
139162 {
140163 return new [ ] { string . Empty } ;
141164 }
142165
143- return modules . Values . SelectMany ( k => k . Keys ) . Select ( Directory . GetDirectoryRoot ) . Distinct ( ) ;
166+ return modules . Values . SelectMany ( k => k . Keys ) . GroupBy ( Directory . GetDirectoryRoot ) . Select ( group =>
167+ {
168+ var splittedPaths = group . Select ( absolutePath => absolutePath . Split ( Path . DirectorySeparatorChar ) )
169+ . OrderBy ( absolutePath => absolutePath . Length ) . ToList ( ) ;
170+ if ( splittedPaths . Count == 1 )
171+ {
172+ return group . Key ;
173+ }
174+
175+ var basePathFragments = new List < string > ( ) ;
176+
177+ splittedPaths [ 0 ] . Select ( ( value , index ) => ( value , index ) ) . ToList ( ) . ForEach ( fragmentIndexPair =>
178+ {
179+ if ( splittedPaths . All ( sp => fragmentIndexPair . value . Equals ( sp [ fragmentIndexPair . index ] ) ) )
180+ {
181+ basePathFragments . Add ( fragmentIndexPair . value ) ;
182+ }
183+ } ) ;
184+ return string . Concat ( string . Join ( Path . DirectorySeparatorChar . ToString ( ) , basePathFragments ) , Path . DirectorySeparatorChar ) ;
185+ } ) ;
144186 }
145187
146- private static string GetRelativePathFromBase ( IEnumerable < string > rootPaths , string path , bool useSourceLink )
188+ private static string GetRelativePathFromBase ( IEnumerable < string > basePaths , string path , bool useSourceLink )
147189 {
148190 if ( useSourceLink )
149191 {
150192 return path ;
151193 }
152194
153- foreach ( var root in rootPaths )
195+ foreach ( var basePath in basePaths )
154196 {
155- if ( path . StartsWith ( root ) )
197+ if ( path . StartsWith ( basePath ) )
156198 {
157- return path . Substring ( root . Length ) ;
199+ return path . Substring ( basePath . Length ) ;
158200 }
159201 }
160202
0 commit comments