Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- #938 Added flag -export-python-deps to package command
- #462: The `repo` command for repository configuration now supports secret input terminal mode for passwords with the `-password-stdin` flag
- #935: Adding a generic JFrog Artifactory tarball resource processor for bundling artifact with a package and deploying it to a final location on install.
- #971: Adds support for JSON, YAML, and Toon formats via the -f flag and new -DUnitTest.*Output directives.
Copy link
Copy Markdown
Collaborator

@isc-dchui isc-dchui Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move this to 0.10.7 please

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved to 0.10.7 thank you!


### Changed
- #316: All parameters, except developer mode, included with a `load`, `install` or `update` command will be propagated to dependencies
Expand Down
1 change: 1 addition & 0 deletions src/cls/IPM/Main.cls
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ Can also specify desired version to update to.
<modifier name="dev" dataAlias="DeveloperMode" dataValue="1" description="Sets the DeveloperMode flag for the module's lifecycle. Key consequences of this are that ^Sources will be configured for resources in the module, and installer methods will be called with the dev mode flag set." />
<modifier name="quiet" aliases="q" dataAlias="Verbose" dataValue="0" description="Produces minimal output from the command." />
<modifier name="verbose" aliases="v" dataAlias="Verbose" dataValue="1" description="Produces verbose output from the command." />
<modifier name="output-format" aliases="output,f" dataAlias="outputformat" value="true" description="Specifies the desired output format for the command result (e.g., json, yaml, toon)." />
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might as well include "format" as an alias if "f" is an alias.

Also this looks like it only affects the test output, so you should specify that.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added format alias as well. Thank you!

<modifier name="export-deps" value="true" valueList="0,1" dataAlias="ExportDependencies" description="If specified, controls whether dependencies are exported. If omitted, defaults to the value of the #EXPORTDEPENDENCIES in lifecycle class. This modifier is only used in &quot;Package&quot; and &quot;Publish&quot; lifecycles." />

</command>
Expand Down
41 changes: 32 additions & 9 deletions src/cls/IPM/ResourceProcessor/Test.cls
Original file line number Diff line number Diff line change
Expand Up @@ -189,18 +189,41 @@ Method OnPhase(
zkill ^UnitTestRoot
$$$ThrowOnError(tSC)

if $data(pParams("UnitTest","JUnitOutput"),tJUnitFile) {
set tPostfix = "-"_$zconvert(pPhase,"L")_"-"
if (..Package '= "") {
set tPostfix = tPostfix_$replace(..Package,".","-")_"-PKG"
} elseif (..Class '= "") {
set tPostfix = tPostfix_$replace(..Class,".","-")_"-CLS"
if $data(pParams("UnitTest"))>1 {
set outputType=""
for {
set outputType = $order(pParams("UnitTest",outputType),1,fileName)
quit:outputType=""
set tPostfix = "-"_$$$lcase(pPhase)_"-"
if (..Package '= "") {
set tPostfix = tPostfix_$replace(..Package,".","-")_"-PKG"
} elseif (..Class '= "") {
set tPostfix = tPostfix_$replace(..Class,".","-")_"-CLS"
}
set outputClass = "%IPM.Test."_outputType
if '$$$defClassDefined(outputClass) {
$$$ThrowOnError($$$ERROR($$$GeneralError,"The requested "_outputType_" output format does not exist."))
}
set extension = $select(outputType="JsonOutput":".json",outputType="ToonOutput":".toon",outputType="YamlOutput":".yaml",1:".xml")
set fileName = $piece(fileName,".",1,*-1)_tPostfix_extension
set tSC = $classmethod(outputClass,"ToFile",fileName)
$$$ThrowOnError(tSC)
}
}
write !
if $data(pParams("outputformat"),outputFormat)||('tVerbose) {
write !,"Test result summary",!
// TODO: Move this default format to ^IPM.Config.Test("outputFormat") rather than keeping it hardcoded.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

%IPM.Repo.UniversalSettings would also be a good place for this. In either case, you still want to have some default if the globals aren't populated and some validation if they are.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @isc-dchui
Thank you. I've created TestReportFormat in %IPM.Repo.UniversalSettings settings class and add description as well.

set:$get(outputFormat)="" outputFormat="Toon"
set outputClass = "%IPM.Test."_$zconvert(outputFormat,"w")_"Output"
if '$$$defClassDefined(outputClass) {
$$$ThrowOnError($$$ERROR($$$GeneralError,"The requested "_outputType_" output format does not exist."))
}
set tJUnitFile = $piece(tJUnitFile,".",1,*-1)_tPostfix_".xml"
set tSC = ##class(%IPM.Test.JUnitOutput).ToFile(tJUnitFile)
set defaultTestStatus = "failed"
set tSC = $classmethod(outputClass,"OutputToDevice",,defaultTestStatus)
$$$ThrowOnError(tSC)
write !
}

// By default, detect and report unit test failures as an error from this phase
if $get(pParams("UnitTest","FailuresAreFatal"),1) {
do ##class(%IPM.Test.Manager).OutputFailures()
Expand Down
68 changes: 68 additions & 0 deletions src/cls/IPM/Test/Abstract.cls
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/// The class serves as the base class for all the unit test result formatting.
Class %IPM.Test.Abstract Extends %RegisteredObject
{

ClassMethod ToFile(
pFileName As %String,
pCaseStatus As %String = "",
pTestIndex As %Integer = {$order(^UnitTest.Result(""),-1)}) As %Status [ Abstract ]
{
}

ClassMethod OutputToDevice(
pTestIndex As %Integer = {$order(^UnitTest.Result(""),-1)},
pTestStatus As %String = "") [ Abstract ]
{
}

Query FilteredTestResults(
pInstance As %Integer,
pTestStatus) As %SQLQuery(ROWSPEC = "TotalCounts:%Integer,namespace:%String,duration:%String,testDateTime:%String,suiteName:%String,testcaseName:%String,methodName:%String,testMethod:%String,assertAction:%String,assertCounter:%Integer,assertDescription:%String,assertLocation:%String", SELECTMODE = "DISPLAY")
{
SELECT
count(*) as TotalCounts,
tinstance.Namespace AS namespace,
tinstance.Duration AS duration,
tinstance.DateTime AS testDateTime,
tsuite.Name AS suiteName,
tcase.Name AS testcaseName,
tmethod.Name AS methodName,
tassert.TestMethod AS testMethod,
tassert.Action AS assertAction,
tassert.Counter AS assertCounter,
tassert.Description AS assertDescription,
tassert.Location AS assertLocation
FROM
%UnitTest_Result.TestInstance tinstance
JOIN %UnitTest_Result.TestSuite tsuite ON tsuite.TestInstance=tinstance.ID
JOIN %UnitTest_Result.TestCase tcase ON tcase.TestSuite=tsuite.ID
JOIN %UnitTest_Result.TestMethod tmethod ON tmethod.TestCase=tcase.ID
JOIN %UnitTest_Result.TestAssert tassert ON tassert.TestMethod=tmethod.ID
WHERE tinstance.ID=:pInstance AND tassert.Status=:pTestStatus
}

Query GetAllTestResults(pInstance As %Integer) As %SQLQuery(ROWSPEC = "TotalCounts:%String,namespace:%String,duration:%String,testDateTime:%String,suiteName:%String,testcaseName:%String,methodName:%String,testMethod:%String,assertAction:%String,assertCounter:%Integer,assertDescription:%String,assertLocation:%String", SELECTMODE = "DISPLAY")
{
SELECT
count(*) as TotalCounts,
tinstance.Namespace AS namespace,
tinstance.Duration AS duration,
tinstance.DateTime AS testDateTime,
tsuite.Name AS suiteName,
tcase.Name AS testcaseName,
tmethod.Name AS methodName,
tassert.TestMethod AS testMethod,
tassert.Action AS assertAction,
tassert.Counter AS assertCounter,
tassert.Description AS assertDescription,
tassert.Location AS assertLocation
FROM
%UnitTest_Result.TestInstance tinstance
JOIN %UnitTest_Result.TestSuite tsuite ON tsuite.TestInstance=tinstance.ID
JOIN %UnitTest_Result.TestCase tcase ON tcase.TestSuite=tsuite.ID
JOIN %UnitTest_Result.TestMethod tmethod ON tmethod.TestCase=tcase.ID
JOIN %UnitTest_Result.TestAssert tassert ON tassert.TestMethod=tmethod.ID
WHERE tinstance.ID=:pInstance
}

}
89 changes: 89 additions & 0 deletions src/cls/IPM/Test/JsonOutput.cls
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
Class %IPM.Test.JsonOutput Extends %IPM.Test.Abstract
{

ClassMethod ToFile(
pFileName As %String,
pTestStatus As %String = "",
pTestIndex As %Integer = {$order(^UnitTest.Result(""),-1)}) As %Status
{
set tSC = $$$OK
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: in new code, avoid t- and p- prefixes

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed the t and p prefix Infront of variables

try {
set fileStream = ##class(%Stream.FileCharacter).%New()
set fileStream.TranslateTable="UTF8"
do fileStream.LinkToFile(pFileName)
set responseJson = ..JSON(pTestIndex, pTestStatus)
do fileStream.Write(responseJson.%ToJSON())
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sure to ThrowOnError for LinkToFile and Write here and do the same throughout for methods that return statuses

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you. Updated the code!

$$$ThrowOnError(fileStream.%Save())
} catch e {
set tSC = e.AsStatus()
}
return tSC
}

ClassMethod OutputToDevice(
pTestIndex As %Integer = {$order(^UnitTest.Result(""),-1)},
pCaseStatus As %String = "") As %Status
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: remove p- prefix and change to camelCase

{
set tSC = $$$OK
try {
set responseJson= ..JSON(pTestIndex, pCaseStatus)
write !
do responseJson.%ToJSON()
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not fully sure what's happening here. Doesn't %ToJSON return the string of the dynamic object? But it's not being written here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you. Updated the code!

} catch e {
set tSC = e.AsStatus()
}
return tSC
}

ClassMethod JSON(
pTestIndex,
pTestStatus) As %DynamicObject
{
if pTestStatus'=""{
set tResult = ..FilteredTestResultsFunc(pTestIndex,pTestStatus)
} else {
set tResult = ..GetAllTestResultsFunc(pTestIndex)
}
set unitTest = {}
set unitTest.results = []
set (previousID,currentSuite,currentTestcase,suiteObj,testcaseObj) = ""

while tResult.%Next() {
if previousID = "" {
set unitTest.id = pTestIndex
set unitTest.namespace = tResult.namespace
set unitTest.duration = tResult.duration
set unitTest.testDateTime = tResult.testDateTime
}
set previousID = pTestIndex
if tResult.suiteName '= currentSuite {
set currentSuite = tResult.suiteName
set suiteObj = {
"suiteName": (currentSuite),
"testcases": []
}
do unitTest.results.%Push(suiteObj)
set currentTestcase = ""
}
if tResult.testcaseName '= currentTestcase {
set currentTestcase = tResult.testcaseName
set testcaseObj = {
"testcaseName": (currentTestcase),
"methods": []
}
do suiteObj.testcases.%Push(testcaseObj)
}
set methodObj = {
"methodName": (tResult.methodName),
"testMethod": (tResult.testMethod),
"assertAction": (tResult.assertAction),
"assertCounter": (tResult.assertCounter),
"assertDescription": (tResult.assertDescription),
"assertLocation": (tResult.assertLocation)
}
do testcaseObj.methods.%Push(methodObj)
}
return unitTest
}

}
75 changes: 75 additions & 0 deletions src/cls/IPM/Test/ToonOutput.cls
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
Class %IPM.Test.ToonOutput Extends %IPM.Test.Abstract
{

ClassMethod ToFile(
pFileName As %String,
pTestStatus As %String = "",
pTestIndex As %Integer = {$order(^UnitTest.Result(""),-1)}) As %Status
{
set tSC = $$$OK
try {
set fileStream = ##class(%Stream.FileCharacter).%New()
set fileStream.TranslateTable="UTF8"
do fileStream.LinkToFile(pFileName)
if pTestStatus'=""{
set tResult = ..FilteredTestResultsFunc(pTestIndex, pTestStatus)
} else {
set tResult = ..GetAllTestResultsFunc(pTestIndex)
}
set currentID=""
while tResult.%Next() {
if currentID = "" {
set currentID = pTestIndex
do fileStream.WriteLine("unitTest:")
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lots of statuses that need checking here

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you. Updated the code!

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still need to check the %Status returned by the WriteLine() calls

do fileStream.WriteLine(" id: "_pTestIndex)
do fileStream.WriteLine(" namespace: "_tResult.namespace)
do fileStream.WriteLine(" duration: "_tResult.duration)
do fileStream.WriteLine(" testDateTime: "_tResult.testDateTime)
do fileStream.WriteLine()
do fileStream.WriteLine("results["_tResult.TotalCounts_"]{suiteName,testcaseName,methodName,status,assertAction,assertCounter,assertDescription,assertLocation}:")
}
set data = " "_tResult.suiteName_","_tResult.testcaseName_","_tResult.methodName_","_pTestIndex_","_
tResult.assertAction_","_tResult.assertCounter_","""_$translate(tResult.assertDescription,"""")_""","""_tResult.assertLocation_""""
do fileStream.WriteLine(data)
}
$$$ThrowOnError(fileStream.%Save())
} catch e {
set tSC = e.AsStatus()
}
return tSC
}

ClassMethod OutputToDevice(
pTestIndex As %Integer = {$order(^UnitTest.Result(""),-1)},
pTestStatus As %String = "") As %Status
{
set tSC = $$$OK
try {
if pTestStatus'=""{
set tResult = ..FilteredTestResultsFunc(pTestIndex, pTestStatus)
} else {
set tResult = ..GetAllTestResultsFunc(pTestIndex)
}
set currentID=""
while tResult.%Next() {
if currentID = "" {
set currentID = pTestIndex
write !,"unitTest:"
write !," id: "_pTestIndex
write !," namespace: "_tResult.namespace
write !," duration: "_tResult.duration
write !," testDateTime: "_tResult.testDateTime
write !
write !,"results["_tResult.TotalCounts_"]{suiteName,testcaseName,methodName,status,assertAction,assertCounter,assertDescription,assertLocation}:"
}
set data = " "_tResult.suiteName_","_tResult.testcaseName_","_tResult.methodName_","_pTestIndex_","_
tResult.assertAction_","_tResult.assertCounter_","""_$translate(tResult.assertDescription,"""")_""","""_tResult.assertLocation_""""
write !,data
}
} catch e {
set tSC = e.AsStatus()
}
return tSC
}

}
83 changes: 83 additions & 0 deletions src/cls/IPM/Test/YamlOutput.cls
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
Class %IPM.Test.YamlOutput Extends %IPM.Test.Abstract
{

ClassMethod ToFile(
pFileName As %String,
pTestStatus As %String = "",
pTestIndex As %Integer = {$order(^UnitTest.Result(""),-1)}) As %Status
{
set tSC = $$$OK
try {
set fileStream = ##class(%Stream.FileCharacter).%New()
set fileStream.TranslateTable="UTF8"
do fileStream.LinkToFile(pFileName)
do fileStream.CopyFrom(..YAML(pTestIndex, pTestStatus))
$$$ThrowOnError(fileStream.%Save())
} catch e {
set tSC = e.AsStatus()
}
return tSC
}

ClassMethod OutputToDevice(
pTestIndex As %Integer = {$order(^UnitTest.Result(""),-1)},
pTestStatus As %String = "") As %Status
{
set tSC = $$$OK
try {
set yamlStream = ..YAML(pTestIndex, pTestStatus)
write !
while 'yamlStream.AtEnd {
write yamlStream.Read()
}
} catch e {
set tSC = e.AsStatus()
}
return tSC
}

ClassMethod YAML(
pTestIndex = {$order(^UnitTest.Result(""),-1)},
pTestStatus As %String = "") As %Stream.TmpCharacter
{
if pTestStatus'=""{
set tResult= ..FilteredTestResultsFunc(pTestIndex, pTestStatus)
} else {
set tResult= ..GetAllTestResultsFunc(pTestIndex)
}
set yamlStream = ##class(%Stream.TmpCharacter).%New()
set (yaml,currentID,currentSuite,currentTestcase) = ""
while tResult.%Next() {
if currentID = "" {
set currentID = pTestIndex
do yamlStream.WriteLine("unitTest:")
do yamlStream.WriteLine(" id: "_pTestIndex)
do yamlStream.WriteLine(" namespace: """_tResult.namespace_"""")
do yamlStream.WriteLine(" duration: "_tResult.duration)
do yamlStream.WriteLine(" testDateTime: """_tResult.testDateTime_"""")
do yamlStream.WriteLine( "")
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: extra space before the ""

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed the space. thank you!

do yamlStream.WriteLine(" results:")
}
if tResult.suiteName '= currentSuite {
set currentSuite = tResult.suiteName
set currentTestcase = ""
do yamlStream.WriteLine(" - suiteName: """_tResult.suiteName_"""")
do yamlStream.WriteLine(" testcases:")
}
if tResult.testcaseName '= currentTestcase {
set currentTestcase = tResult.testcaseName
do yamlStream.WriteLine(" - testcaseName: """_tResult.testcaseName_"""")
do yamlStream.WriteLine(" methods:")
}
do yamlStream.WriteLine(" - methodName: """_tResult.methodName_"""")
//do yamlStream.WriteLine(" testMethod: """_tResult.testMethod_"""")
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any particular reason why these lines are commented out?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No Specific reason. Updated the code!

do yamlStream.WriteLine(" assertAction: """_tResult.assertAction_"""")
do yamlStream.WriteLine(" assertCounter: "_tResult.assertCounter)
//do yamlStream.WriteLine(" assertDescription: |")
//do yamlStream.WriteLine(" "_$replace(tResult.assertDescription, $c(10), $c(10)_" "))
do yamlStream.WriteLine(" assertLocation: """_tResult.assertLocation_"""")
}
return yamlStream
}

}
Loading