@@ -58,6 +58,7 @@ public CodeCoverageMethodElement(XElement element, CodeCoverageResults results)
5858
5959 public string FileID { get ; private set ; }
6060 public string FileName { get ; private set ; }
61+ public string FileNameExt { get ; private set ; }
6162 public bool IsVisited { get ; private set ; }
6263 public int CyclomaticComplexity { get ; private set ; }
6364 public decimal SequenceCoverage { get ; private set ; }
@@ -86,9 +87,14 @@ void Init()
8687
8788 this . FileID = GetFileRef ( ) ;
8889 this . FileName = String . Empty ;
90+ this . FileNameExt = String . Empty ;
8991 if ( ! String . IsNullOrEmpty ( this . FileID ) ) {
9092 if ( results != null ) {
9193 this . FileName = results . GetFileName ( this . FileID ) ;
94+ try {
95+ this . FileNameExt = Path . GetExtension ( this . FileName ) . ToLowerInvariant ( ) ;
96+ }
97+ catch { }
9298 if ( cacheFileName != this . FileName ) {
9399 cacheFileName = this . FileName ;
94100 cacheDocument = GetSource ( cacheFileName ) ;
@@ -220,14 +226,19 @@ void GetSequencePointContent(CodeCoverageSequencePoint sp)
220226 // -> this method SP with lowest Line/Column
221227 void getBodyStartSP ( ) {
222228 if ( this . SequencePoints . Count != 0 ) {
223- foreach ( CodeCoverageSequencePoint sp in this . SequencePoints ) {
224- if ( sp . FileID != this . FileID ) continue ;
225- if ( this . BodyStartSP == null || ( sp . Line < this . BodyStartSP . Line ) ||
226- ( sp . Line == this . BodyStartSP . Line && sp . Column < this . BodyStartSP . Column )
227- ) {
228- this . BodyStartSP = sp ;
229+ if ( this . FileNameExt == ".cs" ) {
230+ foreach ( CodeCoverageSequencePoint sp in this . SequencePoints ) {
231+ if ( sp . FileID != this . FileID ) continue ;
232+ if ( this . BodyStartSP == null || ( sp . Line < this . BodyStartSP . Line ) ||
233+ ( sp . Line == this . BodyStartSP . Line && sp . Column < this . BodyStartSP . Column )
234+ ) {
235+ this . BodyStartSP = sp ;
236+ }
229237 }
230238 }
239+ else {
240+ this . BodyStartSP = this . SequencePoints . First ( ) ;
241+ }
231242 }
232243 }
233244
@@ -236,29 +247,34 @@ void getBodyStartSP() {
236247 // and lowest Offset (when duplicated bw ccrewrite)
237248 void getBodyFinalSP ( ) {
238249 if ( this . SequencePoints . Count != 0 ) {
239- for ( int i = this . SequencePoints . Count - 1 ; i > 0 ; i -- ) {
240- var sp = this . SequencePoints [ i ] ;
241- if ( sp . FileID != this . FileID ) continue ;
242- if ( sp . Content != "}" ) continue ;
243- if ( this . BodyFinalSP == null || ( sp . Line > this . BodyFinalSP . Line ) ||
244- ( sp . Line == this . BodyFinalSP . Line && sp . Column >= this . BodyFinalSP . Column )
245- ) {
246- // ccrewrite ContractClass/ContractClassFor
247- // adds duplicate method end-sequence-point "}"
248- //
249- // Take duplicate BodyFinalSP with lower Offset
250- // Because IL.Offset of second duplicate
251- // will extend branch coverage of this method
252- // by coverage of ContractClassFor inserted SequencePoint!
253- if ( this . BodyFinalSP != null &&
254- sp . Line == this . BodyFinalSP . Line &&
255- sp . Column == this . BodyFinalSP . Column &&
256- sp . Offset < this . BodyFinalSP . Offset ) {
257- this . SequencePoints . Remove ( this . BodyFinalSP ) ; // remove duplicate
250+ if ( this . FileNameExt == ".cs" ) {
251+ for ( int i = this . SequencePoints . Count - 1 ; i > 0 ; i -- ) {
252+ var sp = this . SequencePoints [ i ] ;
253+ if ( sp . FileID != this . FileID ) continue ;
254+ if ( sp . Content != "}" ) continue ;
255+ if ( this . BodyFinalSP == null || ( sp . Line > this . BodyFinalSP . Line ) ||
256+ ( sp . Line == this . BodyFinalSP . Line && sp . Column >= this . BodyFinalSP . Column )
257+ ) {
258+ // ccrewrite ContractClass/ContractClassFor
259+ // adds duplicate method end-sequence-point "}"
260+ //
261+ // Take duplicate BodyFinalSP with lower Offset
262+ // Because IL.Offset of second duplicate
263+ // will extend branch coverage of this method
264+ // by coverage of ContractClassFor inserted SequencePoint!
265+ if ( this . BodyFinalSP != null &&
266+ sp . Line == this . BodyFinalSP . Line &&
267+ sp . Column == this . BodyFinalSP . Column &&
268+ sp . Offset < this . BodyFinalSP . Offset ) {
269+ this . SequencePoints . Remove ( this . BodyFinalSP ) ; // remove duplicate
270+ }
271+ this . BodyFinalSP = sp ;
258272 }
259- this . BodyFinalSP = sp ;
260273 }
261274 }
275+ else {
276+ this . BodyFinalSP = this . SequencePoints . Last ( ) ;
277+ }
262278 }
263279 }
264280
@@ -273,6 +289,9 @@ int GetSequencePointsCount() {
273289 return 0 ;
274290 }
275291
292+ const string @assert = "Assert" ;
293+ const string @contract = "Contract" ;
294+
276295 void GetBranchRatio ( ) {
277296
278297 this . BranchCoverageRatio = null ;
@@ -295,31 +314,33 @@ void GetBranchRatio () {
295314 // SequencePoint is visited and belongs to this method?
296315 if ( sp . VisitCount != 0 && sp . FileID == this . FileID ) {
297316
298- // Don't want branch coverage of ccrewrite(n)
299- // SequencePoint's with offset before and after method body
300- if ( sp . Offset < BodyStartSP . Offset ||
301- sp . Offset > BodyFinalSP . Offset ) {
302- sp . BranchCoverage = true ;
303- continue ; // skip
304- }
317+ if ( this . FileNameExt == ".cs" ) {
318+ // Only for C#
305319
306- // 1) Generated "in" code for IEnumerables contains hidden "try/catch/finally" branches that
307- // one do not want or cannot cover by test-case because is handled earlier at same method.
308- // ie: NullReferenceException in foreach loop is pre-handled at method entry, ie. by Contract.Require(items!=null)
309- // 2) Branches within sequence points "{" and "}" are not source branches but compiler generated branches
310- // ie: static methods start sequence point "{" contains compiler generated branches
311- // 3) Exclude Contract class (EnsuresOnThrow/Assert/Assume is inside method body)
312- // 4) Exclude NUnit Assert(.Throws) class
313- const string assert = "Assert" ;
314- const string contract = "Contract" ;
315- if ( sp . Content == "in" || sp . Content == "{" || sp . Content == "}" ||
316- sp . Content . StartsWith ( assert + "." , StringComparison . Ordinal ) ||
317- sp . Content . StartsWith ( assert + " " , StringComparison . Ordinal ) ||
318- sp . Content . StartsWith ( contract + "." , StringComparison . Ordinal ) ||
319- sp . Content . StartsWith ( contract + " " , StringComparison . Ordinal )
320- ) {
321- sp . BranchCoverage = true ;
322- continue ; // skip
320+ // Don't want branch coverage of ccrewrite(n)
321+ // SequencePoint(s) with offset before and after method body
322+ if ( sp . Offset < BodyStartSP . Offset ||
323+ sp . Offset > BodyFinalSP . Offset ) {
324+ sp . BranchCoverage = true ;
325+ continue ; // skip
326+ }
327+
328+ // 1) Generated "in" code for IEnumerables contains hidden "try/catch/finally" branches that
329+ // one do not want or cannot cover by test-case because is handled earlier at same method.
330+ // ie: NullReferenceException in foreach loop is pre-handled at method entry, ie. by Contract.Require(items!=null)
331+ // 2) Branches within sequence points "{" and "}" are not source branches but compiler generated branches
332+ // ie: static methods start sequence point "{" contains compiler generated branches
333+ // 3) Exclude Contract class (EnsuresOnThrow/Assert/Assume is inside method body)
334+ // 4) Exclude NUnit Assert(.Throws) class
335+ if ( sp . Content == "in" || sp . Content == "{" || sp . Content == "}" ||
336+ sp . Content . StartsWith ( @assert + "." , StringComparison . Ordinal ) ||
337+ sp . Content . StartsWith ( @assert + " " , StringComparison . Ordinal ) ||
338+ sp . Content . StartsWith ( @contract + "." , StringComparison . Ordinal ) ||
339+ sp . Content . StartsWith ( @contract + " " , StringComparison . Ordinal )
340+ ) {
341+ sp . BranchCoverage = true ;
342+ continue ; // skip
343+ }
323344 }
324345
325346 totalBranchCount += sp . BranchExitsCount ;
0 commit comments