Skip to content

Commit 35c1192

Browse files
committed
fix: classes with only embedded python files are now covered
Turns out there's no .INT routine generated for .CLS files with only embedded python, which means that we can no longer piggyback finding out which python files to track by using the .CLS files. I now keep track of relevant and new python coverage targets separately
1 parent 793046f commit 35c1192

File tree

2 files changed

+53
-18
lines changed

2 files changed

+53
-18
lines changed

cls/TestCoverage/Manager.cls

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ Property Run As TestCoverage.Data.Run;
6666
/// Value at subscript is set to 1 if there are executable lines of code in the target, 0 if not.
6767
Property KnownCoverageTargets [ MultiDimensional, Private ];
6868

69+
/// Known python coverage targets (already snapshotted). <br />
70+
/// Value at subscript is set to 1 if there are executable lines of code in the target, 0 if not.
71+
Property KnownPyCoverageTargets [ MultiDimensional, Private ];
72+
6973
/// Cache of (name, type) -> hash
7074
Property Hashes [ MultiDimensional ];
7175

@@ -229,34 +233,35 @@ Method StartCoverageTracking() As %Status [ Private ]
229233
Set tSC = $$$OK
230234
New $Namespace
231235
Try {
232-
If (..CoverageTargets '= "") {
236+
If ((..CoverageTargets '= "" ) || (..PyCoverageTargets '= "")) {
233237
Set $Namespace = ..SourceNamespace
234238
Set tRelevantTargets = ""
235-
Set tPyRelevantTargets = ""
236239
Set tNewTargets = ""
237240
Set tPointer = 0
238241
While $ListNext(..CoverageTargets,tPointer,tCoverageTarget) {
239242
If '$Data(..KnownCoverageTargets(tCoverageTarget),tIsRelevant)#2 {
240243
Set tNewTargets = tNewTargets_$ListBuild(tCoverageTarget)
241244
} ElseIf tIsRelevant {
242245
Set tRelevantTargets = tRelevantTargets_$ListBuild(tCoverageTarget)
243-
// check if the relevant old targets have python components
244-
Set tOther = ##class(%Library.RoutineMgr).GetOther(tCoverageTarget,"INT",-1)
245-
If (tOther '= "") && ($Piece(tOther,".",*) = "CLS") {
246-
set tName = $piece(tOther, ".", 1, *-1)
247-
If (..HasPython(tName)) {
248-
set tPyRelevantTargets = tPyRelevantTargets _ $ListBuild(tName)
249-
}
250-
}
246+
}
247+
}
248+
249+
Set tPyRelevantTargets = ""
250+
Set tNewPyTargets = ""
251+
Set tPointer = 0
252+
While $ListNext(..PyCoverageTargets,tPointer,tPyCoverageTarget) {
253+
If '$Data(..KnownPyCoverageTargets(tPyCoverageTarget),tIsRelevant)#2 {
254+
Set tNewPyTargets = tNewPyTargets_$ListBuild(tPyCoverageTarget)
255+
} ElseIf tIsRelevant {
256+
Set tPyRelevantTargets = tPyRelevantTargets_$ListBuild(tPyCoverageTarget)
251257
}
252258
}
253259

254-
If (tNewTargets '= "") {
260+
If ((tNewTargets '= "") || (tNewPyTargets '= "")) {
255261
$$$StartTimer("Taking snapshot of code and CLS/MAC/INT mappings")
256-
Set tSC = ##class(TestCoverage.Utils).Snapshot(tNewTargets, .tNewRelevantTargets, .tNewPyRelevantTargets)
262+
Set tSC = ##class(TestCoverage.Utils).Snapshot(tNewTargets, tNewPyTargets, .tNewRelevantTargets, .tNewPyRelevantTargets)
257263
$$$StopTimer
258264
$$$ThrowOnError(tSC)
259-
260265
Set tPointer = 0
261266
While $ListNext(tNewTargets,tPointer,tNewTarget) {
262267
Set ..KnownCoverageTargets(tNewTarget) = 0
@@ -268,13 +273,19 @@ Method StartCoverageTracking() As %Status [ Private ]
268273
Set tRelevantTargets = tRelevantTargets_$ListBuild(tRelevantTarget)
269274
}
270275

276+
Set tPointer = 0
277+
While $ListNext(tNewPyTargets,tPointer,tNewPyTarget) {
278+
Set ..KnownPyCoverageTargets(tNewPyTarget) = 0
279+
}
280+
271281
Set tPointer = 0
272282
While $ListNext(tNewPyRelevantTargets,tPointer,tPyRelevantTarget) {
283+
Set ..KnownPyCoverageTargets(tPyRelevantTarget) = 1
273284
Set tPyRelevantTargets = tPyRelevantTargets_$ListBuild(tPyRelevantTarget)
274285
}
275286
}
276287

277-
If (tRelevantTargets = "") {
288+
If ((tRelevantTargets = "") && (tPyRelevantTargets = "")) {
278289
Write !,"WARNING: Nothing found to monitor for routine(s): "_$ListToString(tNewTargets)
279290
}
280291

@@ -374,7 +385,6 @@ Method UpdateCoverageTargetsForTestDirectory(pDirectory As %String) As %Status [
374385
}
375386
}
376387
}
377-
378388
Set tObjectCodeList = ..GetObjectCodeForSourceNames(tCoverageTargetList)
379389
Set ..CoverageTargets = tObjectCodeList // Also restarts the monitor if it is running and updates data on covered routines/classes
380390
Set ..PyCoverageTargets = tPyCoverageTargetList // no need to get the compiled names, it's already correct
@@ -512,7 +522,7 @@ Method EndCoverageTracking(pTestSuite As %String = "", pTestClass As %String = "
512522
{
513523
Set tSC = $$$OK
514524
Try {
515-
If (..CoverageTargets '= "") {
525+
If ((..CoverageTargets '= "") || (..PyCoverageTargets '= "")) {
516526
// Pause the monitor.
517527
Set tSC = ..Monitor.Pause()
518528
Do ##class(TestCoverage.Utils.LineByLineMonitor).PyStop()

cls/TestCoverage/Utils.cls

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ ClassMethod GetTestCoverageTableList() As %List
7878
/// This is parallelized using <class>%SYSTEM.WorkMgr</class> for better performance. <br />
7979
/// <var>pRelevantRoutines</var> is a $ListBuild list of .INT routines that map back to a .CLS or .MAC
8080
/// routine with at least one executable line.
81-
ClassMethod Snapshot(pIntRoutines As %List, Output pRelevantRoutines As %List = "", Output pPyRelevantRoutines As %List = "") As %Status
81+
ClassMethod Snapshot(pIntRoutines As %List, pPyRoutines As %List, Output pRelevantRoutines As %List = "", Output pPyRelevantRoutines As %List = "") As %Status
8282
{
8383
Set tSC = $$$OK
8484
Try {
@@ -96,6 +96,7 @@ ClassMethod Snapshot(pIntRoutines As %List, Output pRelevantRoutines As %List =
9696
$$$ThrowOnError(tSC)
9797

9898

99+
99100
// See which routines are actually relevant (one or more lines mapping back to a class with 1 or more executable lines)
100101
// There's no point in optimizing out .MAC routines; they'll always have code
101102
Set tPointer = 0
@@ -106,17 +107,26 @@ ClassMethod Snapshot(pIntRoutines As %List, Output pRelevantRoutines As %List =
106107
// This also snapshots the compiled python routine with it if there is one
107108
#dim tCodeUnit As TestCoverage.Data.CodeUnit
108109
Set tSC = ##class(TestCoverage.Data.CodeUnit).GetCurrentByName(tOther,,.tCodeUnit)
110+
109111
If $$$ISERR(tSC) {
110112
Continue // Non-fatal. Just skip it.
111113
}
112114
Set tName = tCodeUnit.Name // should be the same as tOther without the .cls, but if I have it already why not
115+
Set SnapshottedClasses(tName) = 1
113116
If ##class(TestCoverage.Manager).HasPython(tName) {
114-
set pPyRelevantRoutines = pPyRelevantRoutines _ $ListBuild(tName)
115117
// take a snapshot of the compiled python file
116118
$$$ThrowOnError(##class(TestCoverage.Data.CodeUnit).GetCurrentByName(tName_".PY",,.tPyCodeUnit))
117119

118120
// update the executable lines for the .cls file's python
119121
$$$ThrowOnError(tCodeUnit.UpdatePyExecutableLines(tName, .tPyCodeUnit))
122+
123+
// update the relevant python routines
124+
If ($BitCount(tPyCodeUnit.ExecutableLines, 1)) {
125+
set pPyRelevantRoutines = pPyRelevantRoutines _ $ListBuild(tName)
126+
} ElseIf '$BitCount(tCodeUnit.ExecutableLines,1) {
127+
// if there's no executable python and no executable objectscript, skip it
128+
Continue
129+
}
120130

121131
} ElseIf '$BitCount(tCodeUnit.ExecutableLines,1) {
122132
// Skip it - no executable lines.
@@ -127,6 +137,21 @@ ClassMethod Snapshot(pIntRoutines As %List, Output pRelevantRoutines As %List =
127137
Set pRelevantRoutines = pRelevantRoutines _ $ListBuild(tIntRoutine)
128138
}
129139

140+
// Snapshot all the python routines and their corresponding classes that haven't already been snapshotted
141+
Set tPointer = 0
142+
While $ListNext(pPyRoutines, tPointer, tPyRoutine) {
143+
If ('$Data(SnapshottedClasses(tPyRoutine))) {
144+
$$$ThrowOnError(##class(TestCoverage.Data.CodeUnit).GetCurrentByName(tPyRoutine_".CLS",,.tCodeUnit))
145+
$$$ThrowOnError(##class(TestCoverage.Data.CodeUnit).GetCurrentByName(tPyRoutine_".PY",,.tPyCodeUnit))
146+
$$$ThrowOnError(tCodeUnit.UpdatePyExecutableLines(tPyRoutine, .tPyCodeUnit))
147+
148+
If ($BitCount(tPyCodeUnit.ExecutableLines, 1)) {
149+
set pPyRelevantRoutines = pPyRelevantRoutines _ $ListBuild(tPyRoutine)
150+
}
151+
Set SnapshottedClasses(tPyRoutine) = 1
152+
}
153+
}
154+
130155
Write !
131156
} Catch e {
132157
Set tSC = e.AsStatus()

0 commit comments

Comments
 (0)