@@ -45,6 +45,9 @@ public CodeCoverageMethodElement(XElement element, CodeCoverageResults parent)
4545 private static string cacheFileName = String . Empty ;
4646 private static CodeCoverageStringTextSource cacheDocument = null ;
4747
48+ private static string cache2FileName = String . Empty ;
49+ private static CodeCoverageStringTextSource cache2Document = null ;
50+
4851 public string FileID { get ; private set ; }
4952 public string FileName { get ; private set ; }
5053 public bool IsVisited { get ; private set ; }
@@ -56,9 +59,9 @@ public CodeCoverageMethodElement(XElement element, CodeCoverageResults parent)
5659 public bool IsConstructor { get ; private set ; }
5760 public bool IsStatic { get ; private set ; }
5861 public List < CodeCoverageSequencePoint > SequencePoints { get ; private set ; }
62+ public List < CodeCoverageBranchPoint > BranchPoints { get ; private set ; }
5963 public CodeCoverageSequencePoint BodyStartSP { get ; private set ; }
6064 public CodeCoverageSequencePoint BodyFinalSP { get ; private set ; }
61- public List < CodeCoverageBranchPoint > BranchPoints { get ; private set ; }
6265
6366 public bool IsGetter { get ; private set ; }
6467 public bool IsSetter { get ; private set ; }
@@ -79,20 +82,9 @@ void Init()
7982 if ( ! String . IsNullOrEmpty ( this . FileID ) ) {
8083 if ( parent != null ) {
8184 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- }
85+ if ( cacheFileName != this . FileName ) {
86+ cacheFileName = this . FileName ;
87+ cacheDocument = GetSource ( cacheFileName ) ;
9688 }
9789 }
9890 }
@@ -108,8 +100,7 @@ void Init()
108100 this . GetSequencePointsContent ( ) ;
109101 this . getBodyStartSP ( ) ; // before OrderBy Line/Col
110102 this . getBodyFinalSP ( ) ; // before orderBy Line/Col
111- this . FilterSequencePoints ( ) ; // before orderBy Line/Col
112- this . GetBranchPoints ( ) ;
103+ //this.FilterSequencePoints(); // before orderBy Line/Col
113104 this . GetBranchRatio ( ) ;
114105 this . GetBranchCoverage ( ) ;
115106
@@ -122,16 +113,37 @@ void Init()
122113 }
123114 }
124115
116+ private static var cacheGetSource_LastFileName = ( string ) null ;
117+ private static var cacheGetSource_LastSource = ( CodeCoverageStringTextSource ) null ;
118+
119+ static CodeCoverageStringTextSource GetSource ( string filename ) {
120+
121+ if ( filename == cacheGetSource_LastFileName ) return cacheGetSource_LastSource ;
122+
123+ var retSource = ( CodeCoverageStringTextSource ) null ;
124+ try {
125+ using ( Stream stream = new FileStream ( filename , FileMode . Open , FileAccess . Read ) ) {
126+ try {
127+ stream . Position = 0 ;
128+ string textSource = ICSharpCode . AvalonEdit . Utils . FileReader . ReadFileContent ( stream , Encoding . Default ) ;
129+ retSource = new CodeCoverageStringTextSource ( textSource ) ;
130+ } catch ( Exception e ) { Debug . Fail ( e . Message ) ; }
131+ }
132+ } catch ( Exception e ) { Debug . Fail ( e . Message ) ; }
133+
134+ cacheGetSource_LastFileName = filename ;
135+ cacheGetSource_LastSource = retSource ;
136+ return retSource ;
137+ }
138+
125139 void GetSequencePoints ( ) {
126140
127141 var xSPoints = this . element
128142 . Elements ( "SequencePoints" )
129143 . Elements ( "SequencePoint" ) ;
130144
131145 foreach ( XElement xSPoint in xSPoints ) {
132- CodeCoverageSequencePoint sp = new CodeCoverageSequencePoint ( ) ;
133- sp . FileID = this . FileID ;
134- sp . Document = this . FileName ;
146+ var sp = new CodeCoverageSequencePoint ( ) ;
135147 sp . Line = ( int ) GetDecimalAttributeValue ( xSPoint . Attribute ( "sl" ) ) ;
136148 sp . EndLine = ( int ) GetDecimalAttributeValue ( xSPoint . Attribute ( "el" ) ) ;
137149 sp . Column = ( int ) GetDecimalAttributeValue ( xSPoint . Attribute ( "sc" ) ) ;
@@ -140,6 +152,22 @@ void GetSequencePoints() {
140152 sp . Offset = ( int ) GetDecimalAttributeValue ( xSPoint . Attribute ( "offset" ) ) ;
141153 sp . BranchExitsCount = ( int ) GetDecimalAttributeValue ( xSPoint . Attribute ( "bec" ) ) ;
142154 sp . BranchExitsVisit = ( int ) GetDecimalAttributeValue ( xSPoint . Attribute ( "bev" ) ) ;
155+ sp . FileID = xSPoint . Attribute ( "fileid" ) . Value ?? "0" ;
156+ if ( sp . FileID == "0" ) {
157+ // SequencePoint from not covered (not runnable) file
158+ // ie: interface with CodeContractClass/CodeContractClassFor
159+ sp . Document = xSPoint . Attribute ( "fileid" ) . Value ?? "" ;
160+ }
161+ else if ( sp . FileID == this . FileID ) {
162+ // This method SequencePoint (from this.FileName)
163+ sp . Document = this . FileName ;
164+ }
165+ else {
166+ // SequencePoint from another method/file
167+ // ie: ccrewriten CodeContractClass/CodeContractClassFor
168+ // [or dependency-injected or fody-weaved???]
169+ sp . Document = parent . GetFileName ( sp . FileID ) ;
170+ }
143171 sp . BranchCoverage = ( sp . BranchExitsCount == sp . BranchExitsVisit ) ;
144172 sp . Content = String . Empty ;
145173 sp . Length = 0 ;
@@ -150,27 +178,36 @@ void GetSequencePoints() {
150178
151179 void GetSequencePointsContent ( )
152180 {
153- if ( cacheFileName == this . FileName && cacheDocument != null ) {
154- foreach ( var sp in this . SequencePoints ) {
155- GetSequencePointContent ( sp ) ;
156- }
181+ foreach ( var sp in this . SequencePoints ) {
182+ GetSequencePointContent ( sp ) ;
157183 }
158184 }
159185
160186 void GetSequencePointContent ( CodeCoverageSequencePoint sp )
161187 {
162- // ccrewrite will cause lots of invalid calls to GetText()!
163- if ( cacheFileName == sp . Document && cacheDocument != null ) {
164- sp . Content = cacheDocument . GetText ( sp ) ; // never returns null
165- if ( sp . Content != String . Empty ) {
166- if ( sp . Line != sp . EndLine ) {
167- // merge lines to single line
168- sp . Content = Regex . Replace ( sp . Content , @"\s+" , " " ) ;
169- }
170- // SequencePoint.Length counts all but whitespace
171- sp . Length = Regex . Replace ( sp . Content , @"\s" , "" ) . Length ;
188+ if ( cacheFileName == sp . Document ) {
189+ // check primary cache (this.Filename)
190+ sp . Content = cacheDocument == null ? "" : cacheDocument . GetText ( sp ) ;
191+ }
192+ else {
193+ // check & update secondary cache
194+ if ( cache2FileName == sp . Document ) {
195+ sp . Content = cache2Document == null ? "" : cache2Document . GetText ( sp ) ;
196+ }
197+ else {
198+ cache2FileName = sp . Document ;
199+ cache2Document = GetSource ( cache2FileName ) ;
200+ sp . Content = cache2Document == null ? "" : cache2Document . GetText ( sp ) ;
172201 }
173202 }
203+ if ( sp . Content != String . Empty ) {
204+ if ( sp . Line != sp . EndLine ) {
205+ // merge multiple lines to single line
206+ sp . Content = Regex . Replace ( sp . Content , @"\s+" , " " ) ;
207+ }
208+ // SequencePoint.Length counts all but whitespace
209+ sp . Length = Regex . Replace ( sp . Content , @"\s" , "" ) . Length ;
210+ }
174211 }
175212
176213 // Find method-body start SequencePoint "{" (sp.Content required)
@@ -289,32 +326,25 @@ void GetBranchPoints() {
289326
290327 void GetBranchRatio ( ) {
291328
292- // goal: Get branch ratio, merge branch-exits and exclude (rewriten) Code Contracts branches
293329 this . BranchCoverageRatio = null ;
294330
295- if ( this . BranchPoints == null
296- || this . BranchPoints . Count == 0
297- || this . SequencePoints == null
298- || this . SequencePoints . Count == 0
299- )
300- {
301- return ;
302- }
331+ Debug . Assert ( this . SequencePoints != null ) ;
332+ if ( this . SequencePoints . Count == 0 ) return ;
303333
304334 // This sequence point offset is used to skip CCRewrite(n) BranchPoint's (Requires)
305335 // and '{' branches at static methods
306- if ( this . BodyStartSP == null ) { return ; } // empty body
336+ if ( this . BodyStartSP == null ) return ; // empty body
307337
308338 // This sequence point offset is used to skip CCRewrite(n) BranchPoint's (Ensures)
309- if ( this . BodyFinalSP == null ) { return ; } // empty body
339+ if ( this . BodyFinalSP == null ) return ; // empty body
310340
311341 // Calculate Method Branch coverage
312342 int totalBranchVisit = 0 ;
313343 int totalBranchCount = 0 ;
314344 foreach ( var sp in this . SequencePoints ) {
315345
316- // SequencePoint is visited?
317- if ( sp . VisitCount != 0 ) {
346+ // SequencePoint is visited and belongs to this method ?
347+ if ( sp . VisitCount != 0 && sp . Document == this . FileName ) {
318348
319349 // Don't want branch coverage of ccrewrite(n)
320350 // SequencePoint's with offset before and after method body
0 commit comments