Determining whether a class, which you only have the name of, is a unit test class... I know this code sucks; but how to fix it properly?
Hello,
Here's the code:
Method isTestClass(className As %String) As %Boolean
{
if (className = ..#UTCLASS) {
return 1
}
#dim c as %Dictionary.ClassDefinition
#dim status as %Status
set c = ##class(%Dictionary.ClassDefinition).%OpenId(className,,.status)
if ($$$ISERR(status)) {
throw ##class(%Exception.StatusException).CreateFromStatus(status)
}
if ('c.SuperIsDefined()) {
return 0
}
#dim children
#dim len as %Integer
#dim i as %Integer
set children = $listFromString(c.Super, ",")
set len = $listLength(children)
for i = 1:1:len {
if ..isTestClass($listGet(children, i)) {
return 1
}
}
return 0
}
One thing which bothers me here is that I do not "close" the %Dictionary.ClassDefinition object; I believe this to be a problem somewhat, is that the case?
Second, I believe my walking the list of extended classes is not optimal either... Any way to do better?
Finally, and for some reason, I get errors from this code which I can't quite figure out... "Unable to load object", #5809... But from what I see this should not happen :(
I suggest you use the %IsA classmethod that every class inherits from %Library.SystemBase.
With this I think your method becomes very simple:
quit $classmethod(className,"%IsA",..#UTCLASS)
Hmm, interesting... But does it work recursively? Ie, if I have class C inherits class B inherits class A, is $classMethod("C", "%IsA", "A") true?
Yes, it does work that way. If Cat is a subclass of Mammal, and Mammal is a subclass of Animal, then ##class(Cat).%IsA("Animal") will return 1.
In addition to %IsA (or, similarly, %Extends, which considers multiple inheritance rather than just primary superclasses), the following snippet (slightly modified from an answer I posted on one of your previous questions) may be helpful if you're looking for all of the names of unit test classes:
If you're filtering by package - and it looks like https://github.com/litesolutions/cache-utcov/blob/master/src/utcov/ClassLookup.cls does this - then you can supply a second argument to the SubclassOf query with the package name for better performance. (i.e., Set tRes = tStmt.%Execute("%UnitTest.TestCase","Some.Package.Name."))
All of these approaches work recursively. (C extends B, B extends A -> C extends A.)
Very nice!
One question: since in this code
tStmt
seems to be a prepared statement, can I just declare it as a parameter to the class and reuse it when I need it?Sure - although it'd be a property, not a parameter. Looking at utcov.ClassLookup (glad to see it's not %utcov now, by the way), this should work fine for you. Here's a sample:
Class Sample.ClassQueryProperty Extends %RegisteredObject { Property SubclassQuery As %SQL.Statement [ InitialExpression = {##class(%SQL.Statement).%New()}, Private, ReadOnly ]; Method %OnNew() As %Status [ Private, ServerOnly = 1 ] { Quit ..SubclassQuery.%PrepareClassQuery("%Dictionary.ClassDefinition","SubclassOf") } Method Demo() { Set tRes = ..SubclassQuery.%Execute("%UnitTest.TestCase") While tRes.%Next(.tSC) { $$$ThrowOnError(tSC) Write tRes.%Get("Name"),! } $$$ThrowOnError(tSC) } }
Then: