22// SPDX-FileCopyrightText: 2023
33
44package com.amos.pitmutationmate.pitmutationmate.reporting
5+
6+ import com.intellij.openapi.diagnostic.thisLogger
57import org.w3c.dom.Document
68import org.w3c.dom.Element
7- import org.w3c.dom.Node
89import java.io.File
910import javax.xml.parsers.DocumentBuilderFactory
1011
1112class XMLParser {
1213 fun loadResultsFromXmlReport (xmlMutationReportPath : String , xmlCoverageReportPath : String ): ResultData {
14+ thisLogger().info(" Loading XML reports from $xmlMutationReportPath and $xmlCoverageReportPath " )
1315 val resultData = ResultData ()
1416 try {
15- val documentBuilderFactory = DocumentBuilderFactory .newInstance()
16- val documentBuilder = documentBuilderFactory.newDocumentBuilder()
17- val documentMutationReport = documentBuilder.parse(File (xmlMutationReportPath))
18- val documentCoverageReport = documentBuilder.parse(File (xmlCoverageReportPath))
17+ val documentMutationReport = parseXmlDocument(File (xmlMutationReportPath))
18+ val documentCoverageReport = parseXmlDocument(File (xmlCoverageReportPath))
1919
2020 extractMutationResults(documentMutationReport, resultData)
21- extractCoverageReports(documentCoverageReport, resultData, false , - 1 )
22- extractCoverageReports (documentCoverageReport, resultData, true , resultData.coverageReports.size )
21+ extractCoverageReports(documentCoverageReport, resultData)
22+ extractCoverageReportTotals (documentCoverageReport, resultData)
2323 } catch (e: Exception ) {
24- // TODO: Handle Parser exceptions
25- e.printStackTrace()
24+ thisLogger().warn(" Error while parsing XML reports: ${e.message} " )
2625 }
2726
2827 return resultData
2928 }
3029
30+ private fun parseXmlDocument (xmlFile : File ): Document {
31+ val factory = DocumentBuilderFactory .newInstance()
32+ val builder = factory.newDocumentBuilder()
33+ return builder.parse(xmlFile)
34+ }
35+
3136 private fun extractMutationResults (document : Document , resultData : ResultData ) {
37+ thisLogger().info(" Extracting mutation results" )
3238 val mutationsNodeList = document.getElementsByTagName(" mutation" )
3339
3440 for (i in 0 until mutationsNodeList.length) {
35- val mutationNode = mutationsNodeList.item(i)
36-
37- if (mutationNode.nodeType == Node .ELEMENT_NODE ) {
38- val element = mutationNode as Element
39-
40- val detected = getAttribute(element, " detected" , false )
41- val status = getAttribute(element, " status" , " N/A" )
42- val numberOfTestsRun = getAttribute(element, " numberOfTestsRun" , - 1 )
43- val sourceFile = getTextContent(element, " sourceFile" )
44- val mutatedClass = getTextContent(element, " mutatedClass" )
45- val mutatedMethod = getTextContent(element, " mutatedMethod" )
46- val methodDescription = getTextContent(element, " methodDescription" )
47- val lineNumber = getTextContent(element, " lineNumber" ).toInt()
48- val mutator = getTextContent(element, " mutator" )
49- val indexes = getListContent(element, " index" )
50- val blocks = getListContent(element, " block" )
51- val killingTest = getTextContent(element, " killingTest" )
52- val description = getTextContent(element, " description" )
53-
54- val mutationResult = MutationResult (
55- detected,
56- status,
57- numberOfTestsRun,
58- sourceFile,
59- mutatedClass,
60- mutatedMethod,
61- methodDescription,
62- lineNumber,
63- mutator,
64- indexes,
65- blocks,
66- killingTest,
67- description
68- )
69- resultData.addMutationResult(mutationResult)
70- // display color bars for mutation result
71- resultData.displayResult(mutationResult)
72- }
41+ val element = mutationsNodeList.item(i) as ? Element ? : continue
42+
43+ val detected = getAttribute(element, " detected" , false )
44+ val status = getAttribute(element, " status" , " N/A" )
45+ val numberOfTestsRun = getAttribute(element, " numberOfTestsRun" , 0 )
46+ val sourceFile = getTextContent(element, " sourceFile" )
47+ val mutatedClass = getTextContent(element, " mutatedClass" )
48+ val mutatedMethod = getTextContent(element, " mutatedMethod" )
49+ val methodDescription = getTextContent(element, " methodDescription" )
50+ val lineNumber = getTextContentAsInt(element, " lineNumber" )
51+ val mutator = getTextContent(element, " mutator" )
52+ val indexes = getListContent(element, " index" )
53+ val blocks = getListContent(element, " block" )
54+ val killingTest = getTextContent(element, " killingTest" )
55+ val description = getTextContent(element, " description" )
56+
57+ val mutationResult = MutationResult (
58+ detected,
59+ status,
60+ numberOfTestsRun,
61+ sourceFile,
62+ mutatedClass,
63+ mutatedMethod,
64+ methodDescription,
65+ lineNumber,
66+ mutator,
67+ indexes,
68+ blocks,
69+ killingTest,
70+ description
71+ )
72+ resultData.addMutationResult(mutationResult)
7373 }
7474 }
7575
76- private fun extractCoverageReports (document : Document , resultData : ResultData , totals : Boolean , totalNumber : Int ) {
77- val mutationsNodeList = document.getElementsByTagName(if (totals) " totalMetaData" else " testMetaData" )
76+ private fun extractCoverageReports (document : Document , resultData : ResultData ) {
77+ thisLogger().info(" Extracting coverage reports" )
78+ val mutationsNodeList = document.getElementsByTagName(" testMetaData" )
7879
7980 for (i in 0 until mutationsNodeList.length) {
80- val mutationNode = mutationsNodeList.item(i)
81-
82- if (mutationNode.nodeType == Node .ELEMENT_NODE ) {
83- val element = mutationNode as Element
84-
85- val fileName = if (totals) " totals" else getTextContent(element, " FileName" )
86- val packageName = if (totals) " totals" else getTextContent(element, " PackageName" )
87- val mutatedClass = if (totals) " totals" else getTextContent(element, " MutatedClass" )
88- val lineCoveragePercentage = getTextContent(element, " LineCoveragePercentage" ).toInt()
89- val lineCoverageTextRatio = getTextContent(element, " LineCoverage" )
90- val mutationCoveragePercentage = getTextContent(element, " MutationCoveragePercentage" ).toInt()
91- val mutationCoverageTextRatio = getTextContent(element, " MutationCoverage" )
92- val testStrengthPercentage = getTextContent(element, " TestStrengthPercentage" ).toInt()
93- val testStrengthTextRatio = getTextContent(element, " TestStrength" )
94-
95- val coverageReport = CoverageReport (
96- fileName,
97- packageName,
98- mutatedClass,
99- lineCoveragePercentage,
100- lineCoverageTextRatio,
101- mutationCoveragePercentage,
102- mutationCoverageTextRatio,
103- testStrengthPercentage,
104- testStrengthTextRatio,
105- if (totals) totalNumber else 1
106- )
107- if (totals) resultData.totalResult = coverageReport else resultData.addCoverageReport(coverageReport)
108- }
81+ val element = mutationsNodeList.item(i) as ? Element ? : continue
82+
83+ val fileName = getTextContent(element, " FileName" )
84+ val packageName = getTextContent(element, " PackageName" )
85+ val mutatedClass = getTextContent(element, " MutatedClass" )
86+ val coverageReport = CoverageReport (
87+ fileName,
88+ packageName,
89+ mutatedClass,
90+ getLineCoveragePercentage(element),
91+ getLineCoverageTextRatio(element),
92+ getMutationCoveragePercentage(element),
93+ getMutationCoverageTextRatio(element),
94+ getTestStrengthPercentage(element),
95+ getTestStrengthTextRatio(element),
96+ if (fileName == " N/A" ) 0 else 1
97+ )
98+ resultData.addCoverageReport(coverageReport)
99+ }
100+ }
101+
102+ private fun extractCoverageReportTotals (document : Document , resultData : ResultData ) {
103+ thisLogger().info(" Extracting coverage report totals" )
104+ val mutationsNodeList = document.getElementsByTagName(" totalMetaData" )
105+
106+ for (i in 0 until mutationsNodeList.length) {
107+ val element = mutationsNodeList.item(i) as ? Element ? : continue
108+
109+ val coverageReport = CoverageReport (
110+ " totals" ,
111+ " totals" ,
112+ " totals" ,
113+ getLineCoveragePercentage(element),
114+ getLineCoverageTextRatio(element),
115+ getMutationCoveragePercentage(element),
116+ getMutationCoverageTextRatio(element),
117+ getTestStrengthPercentage(element),
118+ getTestStrengthTextRatio(element),
119+ 0
120+ )
121+ resultData.addCoverageReportTotals(coverageReport)
109122 }
110123 }
111124
125+ private fun getLineCoveragePercentage (element : Element ): Int {
126+ return getTextContentAsInt(element, " LineCoveragePercentage" )
127+ }
128+
129+ private fun getLineCoverageTextRatio (element : Element ): String {
130+ return getTextContent(element, " LineCoverage" )
131+ }
132+
133+ private fun getMutationCoveragePercentage (element : Element ): Int {
134+ return getTextContentAsInt(element, " MutationCoveragePercentage" )
135+ }
136+
137+ private fun getMutationCoverageTextRatio (element : Element ): String {
138+ return getTextContent(element, " MutationCoverage" )
139+ }
140+
141+ private fun getTestStrengthPercentage (element : Element ): Int {
142+ return getTextContentAsInt(element, " TestStrengthPercentage" )
143+ }
144+
145+ private fun getTestStrengthTextRatio (element : Element ): String {
146+ return getTextContent(element, " TestStrength" )
147+ }
148+
112149 private fun getTextContent (element : Element , tagName : String ): String {
113150 val nodeList = element.getElementsByTagName(tagName)
114151 return if (nodeList.length > 0 ) {
115- nodeList.item(0 ).textContent
152+ nodeList.item(0 ).textContent.trim()
116153 } else {
117154 " N/A"
118155 }
119156 }
120157
158+ private fun getTextContentAsInt (element : Element , tagName : String ): Int {
159+ val nodeList = element.getElementsByTagName(tagName)
160+ return if (nodeList.length > 0 ) {
161+ val content = nodeList.item(0 ).textContent.trim()
162+ return if (content.toIntOrNull() != null ) {
163+ content.toInt()
164+ } else {
165+ 0
166+ }
167+ } else {
168+ 0
169+ }
170+ }
171+
121172 private fun getListContent (element : Element , tagName : String ): List <Int > {
122173 val nodeList = element.getElementsByTagName(tagName)
123174 return if (nodeList.length > 0 ) {
@@ -127,14 +178,14 @@ class XMLParser {
127178 }
128179 }
129180
130- private fun <T > getAttribute (element : Element , attributeName : String , defaultValue : T ): T {
181+ private inline fun <reified T > getAttribute (element : Element , attributeName : String , defaultValue : T ): T {
131182 return try {
132183 val attributeValue = element.getAttribute(attributeName)
133184 if (attributeValue.isNotEmpty()) {
134- when (defaultValue ) {
135- is Boolean -> attributeValue.toBoolean() as T
136- is Int -> attributeValue.toInt () as T
137- is String -> attributeValue as T
185+ when (T :: class ) {
186+ Boolean :: class -> attributeValue.toBoolean() as T
187+ Int :: class -> attributeValue.toIntOrNull () as ? T ? : defaultValue
188+ String :: class -> attributeValue as T
138189 else -> defaultValue
139190 }
140191 } else {
@@ -146,7 +197,6 @@ class XMLParser {
146197 }
147198
148199 data class ResultData (
149- // placeholder field for coverage report results to be displayed in visualisation
150200 val coverageReports : MutableList <CoverageReport > = mutableListOf(),
151201 val mutationResults : MutableList <MutationResult > = mutableListOf(),
152202 var totalResult : CoverageReport ? = null
@@ -159,8 +209,11 @@ class XMLParser {
159209 coverageReports.add(coverageReport)
160210 }
161211
162- fun displayResult (mutationResult : MutationResult ) {
163- // removed in favor of MutationsAnnotator
212+ fun addCoverageReportTotals (coverageReport : CoverageReport ) {
213+ if (coverageReports.isNotEmpty()) {
214+ coverageReport.numberOfClasses = coverageReports.size
215+ }
216+ totalResult = coverageReport
164217 }
165218 }
166219
0 commit comments