@@ -32,8 +32,8 @@ public string Report(CoverageResult result)
32
32
coverage . Add ( new XAttribute ( "timestamp" , ( int ) ( DateTime . UtcNow - new DateTime ( 1970 , 1 , 1 ) ) . TotalSeconds ) ) ;
33
33
34
34
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 ) ) ) ;
37
37
38
38
XElement packages = new XElement ( "packages" ) ;
39
39
foreach ( var module in result . Modules )
@@ -51,7 +51,7 @@ public string Report(CoverageResult result)
51
51
{
52
52
XElement @class = new XElement ( "class" ) ;
53
53
@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 ) ) ) ;
55
55
@class . Add ( new XAttribute ( "line-rate" , ( summary . CalculateLineCoverage ( cls . Value ) . Percent / 100 ) . ToString ( CultureInfo . InvariantCulture ) ) ) ;
56
56
@class . Add ( new XAttribute ( "branch-rate" , ( summary . CalculateBranchCoverage ( cls . Value ) . Percent / 100 ) . ToString ( CultureInfo . InvariantCulture ) ) ) ;
57
57
@class . Add ( new XAttribute ( "complexity" , summary . CalculateCyclomaticComplexity ( cls . Value ) ) ) ;
@@ -133,28 +133,70 @@ public string Report(CoverageResult result)
133
133
return Encoding . UTF8 . GetString ( stream . ToArray ( ) ) ;
134
134
}
135
135
136
- private static IEnumerable < string > GetRootDirs ( Modules modules , bool useSourceLink )
136
+ private static IEnumerable < string > GetBasePaths ( Modules modules , bool useSourceLink )
137
137
{
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
+ */
138
161
if ( useSourceLink )
139
162
{
140
163
return new [ ] { string . Empty } ;
141
164
}
142
165
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
+ } ) ;
144
186
}
145
187
146
- private static string GetRelativePathFromBase ( IEnumerable < string > rootPaths , string path , bool useSourceLink )
188
+ private static string GetRelativePathFromBase ( IEnumerable < string > basePaths , string path , bool useSourceLink )
147
189
{
148
190
if ( useSourceLink )
149
191
{
150
192
return path ;
151
193
}
152
194
153
- foreach ( var root in rootPaths )
195
+ foreach ( var basePath in basePaths )
154
196
{
155
- if ( path . StartsWith ( root ) )
197
+ if ( path . StartsWith ( basePath ) )
156
198
{
157
- return path . Substring ( root . Length ) ;
199
+ return path . Substring ( basePath . Length ) ;
158
200
}
159
201
}
160
202
0 commit comments