-
Notifications
You must be signed in to change notification settings - Fork 27
feat: Add JSON, YAML, and Toon output options for zpm test results #979
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 2 commits
4307599
dbb7bed
cd77c52
aef4c32
d1c0dce
3b731a7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 "Package" and "Publish" lifecycles." /> | ||
|
|
||
| </command> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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() | ||
|
|
||
| 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 | ||
| } | ||
|
|
||
| } |
| 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 | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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() | ||
|
||
| } 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 | ||
| } | ||
|
|
||
| } | ||
| 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:") | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Lots of statuses that need checking here
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you. Updated the code!
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
| } | ||
|
|
||
| } | ||
| 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 | ||
| } | ||
|
|
||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
moved to
0.10.7thank you!