Skip to content
Open
Show file tree
Hide file tree
Changes from all 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.

### 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)." />
<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.
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
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())
$$$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
{
set tSC = $$$OK
try {
set responseJson= ..JSON(pTestIndex, pCaseStatus)
write !
do responseJson.%ToJSON()
} 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:")
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( "")
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_"""")
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