11// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
2- //
2+ //
33// Permission is hereby granted, free of charge, to any person obtaining a copy of this
44// software and associated documentation files (the "Software"), to deal in the Software
55// without restriction, including without limitation the rights to use, copy, modify, merge,
66// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
77// to whom the Software is furnished to do so, subject to the following conditions:
8- //
8+ //
99// The above copyright notice and this permission notice shall be included in all copies or
1010// substantial portions of the Software.
11- //
11+ //
1212// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
1313// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
1414// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
@@ -32,8 +32,8 @@ public class CodeCoverageMethodElement
3232 XElement element ;
3333 CodeCoverageResults parent ;
3434
35- public CodeCoverageMethodElement ( XElement element )
36- : this ( element , null ) { }
35+ public CodeCoverageMethodElement ( XElement element )
36+ : this ( element , null ) { }
3737 public CodeCoverageMethodElement ( XElement element , CodeCoverageResults parent )
3838 {
3939 this . parent = parent ;
@@ -67,7 +67,7 @@ public CodeCoverageMethodElement(XElement element, CodeCoverageResults parent)
6767 public bool IsProperty {
6868 get { return IsGetter || IsSetter ; }
6969 }
70-
70+
7171 void Init ( )
7272 {
7373 MethodName = GetMethodName ( ) ;
@@ -77,24 +77,24 @@ void Init()
7777 this . FileID = GetFileRef ( ) ;
7878 this . FileName = String . Empty ;
7979 if ( ! String . IsNullOrEmpty ( this . FileID ) ) {
80- if ( parent != null ) {
81- this . FileName = parent . GetFileName ( this . FileID ) ;
82- if ( File . Exists ( this . FileName ) ) {
83- if ( cacheFileName != this . FileName ) {
84- cacheFileName = this . FileName ;
85- cacheDocument = null ;
86- try {
87- using ( Stream stream = new FileStream ( this . FileName , FileMode . Open , FileAccess . Read ) ) {
88- try {
89- stream . Position = 0 ;
90- string textSource = ICSharpCode . AvalonEdit . Utils . FileReader . ReadFileContent ( stream , Encoding . Default ) ;
91- cacheDocument = new CodeCoverageStringTextSource ( textSource ) ;
92- } catch { }
93- }
94- } catch { }
95- }
96- }
97- }
80+ if ( parent != null ) {
81+ this . FileName = parent . GetFileName ( this . FileID ) ;
82+ if ( File . Exists ( this . FileName ) ) {
83+ if ( cacheFileName != this . FileName ) {
84+ cacheFileName = this . FileName ;
85+ cacheDocument = null ;
86+ try {
87+ using ( Stream stream = new FileStream ( this . FileName , FileMode . Open , FileAccess . Read ) ) {
88+ try {
89+ stream . Position = 0 ;
90+ string textSource = ICSharpCode . AvalonEdit . Utils . FileReader . ReadFileContent ( stream , Encoding . Default ) ;
91+ cacheDocument = new CodeCoverageStringTextSource ( textSource ) ;
92+ } catch { }
93+ }
94+ } catch { }
95+ }
96+ }
97+ }
9898 }
9999
100100 this . IsVisited = this . GetBooleanAttributeValue ( "visited" ) ;
@@ -104,25 +104,26 @@ void Init()
104104 this . IsConstructor = this . GetBooleanAttributeValue ( "isConstructor" ) ;
105105 this . IsStatic = this . GetBooleanAttributeValue ( "isStatic" ) ;
106106 if ( ! String . IsNullOrEmpty ( this . FileID ) ) {
107- this . SequencePoints = this . GetSequencePoints ( ) ;
108- this . BodyStartSP = getBodyStartSP ( ) ; // before OrderBy Line/Col
107+ this . GetSequencePoints ( ) ;
108+ this . GetSequencePointsContent ( ) ;
109+ this . getBodyStartSP ( ) ; // before OrderBy Line/Col
110+ this . getBodyFinalSP ( ) ; // before orderBy Line/Col
111+ this . FilterSequencePoints ( ) ; // before orderBy Line/Col
112+ this . GetBranchPoints ( ) ;
113+ this . GetBranchRatio ( ) ;
114+ this . GetBranchCoverage ( ) ;
115+
109116 // SP's are originaly ordered by CIL offset
110117 // but ccrewrite can move offset of
111118 // Contract.Requires before method signature SP { and
112119 // Contract.Ensures after method closing SP }
113120 // So sort SP's back by line/column
114121 this . SequencePoints . OrderBy ( item => item . Line ) . OrderBy ( item => item . Column ) ;
115- this . BodyFinalSP = getBodyFinalSP ( ) ; // after orderBy Line/Col
116- this . SequencePoints = this . FilterSequencePoints ( this . SequencePoints ) ;
117- this . BranchPoints = this . GetBranchPoints ( ) ;
118- this . BranchCoverageRatio = this . GetBranchRatio ( ) ;
119- this . BranchCoverage = this . GetBranchCoverage ( ) ;
120122 }
121123 }
122124
123- List < CodeCoverageSequencePoint > GetSequencePoints ( ) {
125+ void GetSequencePoints ( ) {
124126
125- List < CodeCoverageSequencePoint > sps = new List < CodeCoverageSequencePoint > ( ) ;
126127 var xSPoints = this . element
127128 . Elements ( "SequencePoints" )
128129 . Elements ( "SequencePoint" ) ;
@@ -136,52 +137,68 @@ List<CodeCoverageSequencePoint> GetSequencePoints() {
136137 sp . Column = ( int ) GetDecimalAttributeValue ( xSPoint . Attribute ( "sc" ) ) ;
137138 sp . EndColumn = ( int ) GetDecimalAttributeValue ( xSPoint . Attribute ( "ec" ) ) ;
138139 sp . VisitCount = ( int ) GetDecimalAttributeValue ( xSPoint . Attribute ( "vc" ) ) ;
139- if ( cacheFileName == sp . Document && cacheDocument != null ) {
140- sp . Content = cacheDocument . GetText ( sp ) ;
141- if ( sp . Line != sp . EndLine ) {
142- sp . Content = Regex . Replace ( sp . Content , @"\s+" , " " ) ;
143- }
144- sp . Length = Regex . Replace ( sp . Content , @"\s" , "" ) . Length ; // ignore white-space for coverage%
145- } else {
146- sp . Content = String . Empty ;
147- sp . Length = 0 ;
148- }
149140 sp . Offset = ( int ) GetDecimalAttributeValue ( xSPoint . Attribute ( "offset" ) ) ;
150141 sp . BranchCoverage = true ;
142+ sp . Content = String . Empty ;
143+ sp . Length = 0 ;
151144
152- sps . Add ( sp ) ;
145+ this . SequencePoints . Add ( sp ) ;
153146 }
154- return sps ;
155147 }
156148
157- // Find method-body start SequencePoint "{"
149+ void GetSequencePointsContent ( )
150+ {
151+ if ( cacheFileName == this . FileName && cacheDocument != null ) {
152+ foreach ( var sp in this . SequencePoints ) {
153+ GetSequencePointContent ( sp ) ;
154+ }
155+ }
156+ }
157+
158+ void GetSequencePointContent ( CodeCoverageSequencePoint sp )
159+ {
160+ // ccrewrite will cause lots of invalid calls to GetText()!
161+ if ( cacheFileName == sp . Document && cacheDocument != null ) {
162+ sp . Content = cacheDocument . GetText ( sp ) ; // never returns null
163+ if ( sp . Content != String . Empty ) {
164+ if ( sp . Line != sp . EndLine ) {
165+ // merge lines to single line
166+ sp . Content = Regex . Replace ( sp . Content , @"\s+" , " " ) ;
167+ }
168+ // SequencePoint.Length counts all but whitespace
169+ sp . Length = Regex . Replace ( sp . Content , @"\s" , "" ) . Length ;
170+ }
171+ }
172+ }
173+
174+ // Find method-body start SequencePoint "{" (sp.Content required)
158175 // Sequence points expected to be ordered by Offset
159176 // Cannot just get first one because of ccrewrite&ContractClassFor
160- public CodeCoverageSequencePoint getBodyStartSP ( ) {
177+ void getBodyStartSP ( ) {
161178 bool startPointFound = false ;
162179 CodeCoverageSequencePoint startSeqPoint = null ;
163- foreach ( CodeCoverageSequencePoint sPoint in this . SequencePoints ) {
164- if ( sPoint . Content == "{" ) {
180+ foreach ( CodeCoverageSequencePoint sp in this . SequencePoints ) {
181+ if ( sp . Content == "{" ) {
165182 if ( this . IsConstructor ) {
166183 // take previous/last one if not null
167- startSeqPoint = startSeqPoint ?? sPoint ;
184+ startSeqPoint = startSeqPoint ?? sp ;
168185 }
169186 else {
170- startSeqPoint = sPoint ;
187+ startSeqPoint = sp ;
171188 }
172189 startPointFound = true ;
173190 break ;
174191 }
175- startSeqPoint = sPoint ;
192+ startSeqPoint = sp ;
176193 }
177- return startPointFound ? startSeqPoint : null ;
194+ this . BodyStartSP = startPointFound ? startSeqPoint : null ;
178195 }
179196
180- // Find method-body final SequencePoint "}"
181- // Sequence points expected to be ordered by Line/Column
182- public CodeCoverageSequencePoint getBodyFinalSP ( ) {
197+ // Find method-body final SequencePoint "}" (sp.Content required)
198+ // Sequence points expected to be ordered by Offset
199+ void getBodyFinalSP ( ) {
183200 CodeCoverageSequencePoint finalSeqPoint = null ;
184- foreach ( CodeCoverageSequencePoint sp in this . SequencePoints ) {
201+ foreach ( CodeCoverageSequencePoint sp in ( ( IEnumerable < CodeCoverageSequencePoint > ) this . SequencePoints ) . Reverse ( ) ) {
185202 if ( sp . Content == "}" ) {
186203 if ( finalSeqPoint == null ) {
187204 finalSeqPoint = sp ;
@@ -193,20 +210,17 @@ public CodeCoverageSequencePoint getBodyFinalSP() {
193210 sp . EndColumn == finalSeqPoint . EndColumn &&
194211 sp . Offset < finalSeqPoint . Offset ) {
195212 finalSeqPoint = sp ;
196- }
197- else if ( sp . Line < finalSeqPoint . Line ) {
213+ // duplicate found, so far no reason to expect "triplicate" :)
198214 break ;
199215 }
200216 }
201217 }
202- return finalSeqPoint ;
218+ this . BodyFinalSP = finalSeqPoint ;
203219 }
204220
205- List < CodeCoverageSequencePoint > FilterSequencePoints ( List < CodeCoverageSequencePoint > sps ) {
206-
207- List < CodeCoverageSequencePoint > returnList = sps ;
221+ void FilterSequencePoints ( ) {
208222
209- if ( sps . Count > 2 &&
223+ if ( this . SequencePoints . Count != 0 &&
210224 this . BodyStartSP != null &&
211225 this . BodyFinalSP != null ) {
212226
@@ -218,31 +232,31 @@ List<CodeCoverageSequencePoint> FilterSequencePoints(List<CodeCoverageSequencePo
218232 // before method signature and after end-brackets xxx{} are removed
219233 // If ContractClassFor is in another file but interleaves this method lines
220234 // then, afaik, not much can be done to remove inserted alien SP's
221- List < CodeCoverageSequencePoint > selected = new List < CodeCoverageSequencePoint > ( ) ;
222-
223- foreach ( var point in sps ) {
224- if (
225- ( point . Line > BodyStartSP . Line || ( point . Line == BodyStartSP . Line && point . Column >= BodyStartSP . Column ) ) &&
226- ( point . Line < BodyFinalSP . Line || ( point . Line == BodyFinalSP . Line && point . Column < BodyFinalSP . Column ) )
227- ) {
235+ var selected = new List < CodeCoverageSequencePoint > ( ) ;
236+
237+ foreach ( var point in this . SequencePoints ) {
238+
239+ // if Content.Length is 0, GetText() is failed by ccrewrite inserted invalid SequencePoint
240+ if ( point . Content . Length != 0
241+ && ( point . Line > BodyStartSP . Line || ( point . Line == BodyStartSP . Line && point . Column >= BodyStartSP . Column ) )
242+ && ( point . Line < BodyFinalSP . Line || ( point . Line == BodyFinalSP . Line && point . Column < BodyFinalSP . Column ) )
243+ ) {
228244 selected . Add ( point ) ;
229245 }
230246 // After ccrewrite ContractClass/ContractClassFor
231- // duplicate method end-sequence-point (}) is added
247+ // duplicate method end-sequence-point "}" is added
232248 //
233- // Add first finalSP (can be a duplicate)
249+ // Add only first finalSP (can be a duplicate)
234250 // Note: IL.Offset of second duplicate finalSP will
235251 // extend branch coverage outside method-end "}",
236252 // and that can lead to wrong branch coverage display!
237253 if ( object . ReferenceEquals ( point , this . BodyFinalSP ) ) {
238- selected . Add ( point ) ;
254+ selected . Add ( point ) ;
239255 }
240256 }
241257
242- returnList = selected ;
258+ this . SequencePoints = selected ;
243259 }
244-
245- return returnList ;
246260 }
247261
248262 int GetSequencePointsCount ( ) {
@@ -256,9 +270,8 @@ int GetSequencePointsCount() {
256270 return 0 ;
257271 }
258272
259- List < CodeCoverageBranchPoint > GetBranchPoints ( ) {
273+ void GetBranchPoints ( ) {
260274 // get all BranchPoints
261- List < CodeCoverageBranchPoint > bps = new List < CodeCoverageBranchPoint > ( ) ;
262275 var xBPoints = this . element
263276 . Elements ( "BranchPoints" )
264277 . Elements ( "BranchPoint" ) ;
@@ -268,34 +281,34 @@ List<CodeCoverageBranchPoint> GetBranchPoints() {
268281 bp . Offset = ( int ) GetDecimalAttributeValue ( xBPoint . Attribute ( "offset" ) ) ;
269282 bp . Path = ( int ) GetDecimalAttributeValue ( xBPoint . Attribute ( "path" ) ) ;
270283 bp . OffsetEnd = ( int ) GetDecimalAttributeValue ( xBPoint . Attribute ( "offsetend" ) ) ;
271- bps . Add ( bp ) ;
284+ this . BranchPoints . Add ( bp ) ;
272285 }
273- return bps ;
274286 }
275287
276- Tuple < int , int > GetBranchRatio ( ) {
277-
278- // goal: Get branch ratio and exclude (rewriten) Code Contracts branches
288+ void GetBranchRatio ( ) {
279289
290+ // goal: Get branch ratio, merge branch-exits and exclude (rewriten) Code Contracts branches
291+ this . BranchCoverageRatio = null ;
292+
280293 if ( this . BranchPoints == null
281- || this . BranchPoints . Count ( ) == 0
294+ || this . BranchPoints . Count == 0
282295 || this . SequencePoints == null
283296 || this . SequencePoints . Count == 0
284297 )
285298 {
286- return null ;
299+ return ;
287300 }
288301
289302 // This sequence point offset is used to skip CCRewrite(n) BranchPoint's (Requires)
290303 // and '{' branches at static methods
291- if ( this . BodyStartSP == null ) { return null ; } // empty body
304+ if ( this . BodyStartSP == null ) { return ; } // empty body
292305
293306 // This sequence point offset is used to skip CCRewrite(n) BranchPoint's (Ensures)
294- if ( this . BodyFinalSP == null ) { return null ; } // empty body
307+ if ( this . BodyFinalSP == null ) { return ; } // empty body
295308
296309 // Connect Sequence & Branches
297310 IEnumerator < CodeCoverageSequencePoint > SPEnumerator = this . SequencePoints . GetEnumerator ( ) ;
298- CodeCoverageSequencePoint currSeqPoint = BodyStartSP ;
311+ CodeCoverageSequencePoint currSeqPoint = this . BodyStartSP ;
299312 int nextSeqPointOffset = BodyStartSP . Offset ;
300313
301314 foreach ( var bp in this . BranchPoints ) {
@@ -379,13 +392,13 @@ Tuple<int,int> GetBranchRatio () {
379392 sp . Branches = null ; // release memory
380393 }
381394
382- return ( totalBranchCount != 0 ) ? new Tuple < int , int > ( totalBranchVisit , totalBranchCount ) : null ;
395+ this . BranchCoverageRatio = ( totalBranchCount != 0 ) ? new Tuple < int , int > ( totalBranchVisit , totalBranchCount ) : null ;
383396
384397 }
385398
386- decimal GetBranchCoverage ( ) {
399+ void GetBranchCoverage ( ) {
387400
388- return this . BranchCoverageRatio == null ? 0m : ( ( decimal ) ( this . BranchCoverageRatio . Item1 * 100 ) ) / ( ( decimal ) this . BranchCoverageRatio . Item2 ) ;
401+ this . BranchCoverage = this . BranchCoverageRatio == null ? 0m : ( ( decimal ) ( this . BranchCoverageRatio . Item1 * 100 ) ) / ( ( decimal ) this . BranchCoverageRatio . Item2 ) ;
389402
390403 }
391404
0 commit comments