Skip to content

Commit

Permalink
new acton: loadApexCodeCoverageAggregate
Browse files Browse the repository at this point in the history
allows to query from SFDC - coverage data for specific Class/Trigger
  • Loading branch information
neowit committed Nov 5, 2018
1 parent eb9b1a7 commit a487dc5
Show file tree
Hide file tree
Showing 7 changed files with 155 additions and 7 deletions.
1 change: 1 addition & 0 deletions src/main/scala/com/neowit/apex/actions/Action.scala
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ object ActionFactory {
"saveModified" -> "com.neowit.apex.actions.tooling.SaveModified",
"saveSpecificFiles" -> "com.neowit.apex.actions.tooling.SaveSpecificFiles",
"runTestsTooling" -> "com.neowit.apex.actions.tooling.RunTests",
"loadApexCodeCoverageAggregate" -> "com.neowit.apex.actions.tooling.LoadApexCodeCoverageAggregate",
"deployModified" -> "DeployModified",
"deployAll" -> "DeployAll",
"deploySpecificFiles" -> "DeploySpecificFiles",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* Copyright (c) 2018 Andrey Gavrikov.
* this file is part of tooling-force.com application
* https://github.com/neowit/tooling-force.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package com.neowit.apex.actions.tooling

import com.neowit.apex._
import com.neowit.apex.actions._
import com.neowit.utils.FileUtils

import scala.concurrent.{ExecutionContext, Future}

/**
* Created by Andrey Gavrikov
*/
class LoadApexCodeCoverageAggregate extends ApexActionWithReadOnlySession with RunTestJsonSupport {

override def getHelp: ActionHelp = new ActionHelp {
override def getExample: String = ""

override def getParamDescription(paramName: String): String = paramName match {
case "classOrTriggerName" => "Name of Class or Trigger for which to load coverage"
case x => "Unsupported parameter: " + x
}

override def getParamNames: List[String] = List()

override def getSummary: String = "Get code coverage"

override def getName: String = "loadApexCodeCoverageAggregate"
}

override protected def act()(implicit ec: ExecutionContext): Future[ActionResult] = {

val actionResult = config.getProperty("classOrTriggerName") match {
case Some(classOrTriggerNameProvided) =>
var classOrTriggerName = FileUtils.removeExtension(classOrTriggerNameProvided)
val queryIterator = SoqlQuery.getQueryIteratorTooling(session,
s""" select ApexClassOrTrigger.Name, ApexClassOrTriggerId, NumLinesCovered, NumLinesUncovered, Coverage
| from ApexCodeCoverageAggregate
| where ApexClassOrTrigger.Name = '$classOrTriggerName'
|""".stripMargin)

var errorBuilder = Array.newBuilder[String]

val coverageResultOpt =
if (queryIterator.hasNext) {
val record = queryIterator.map(obj => obj.convertTo[RunTestJsonSupport.ApexCodeCoverageAggregate]).next()
println(record)
val coverageResult = RunTestConversions.toCodeCoverageResult(record)
Option(coverageResult)
} else {
errorBuilder += "No coverage available for " + classOrTriggerName
None
}

val runTestResult = new com.neowit.apex.RunTestsResult {
override def getCodeCoverage: Array[CodeCoverageResult] = {
coverageResultOpt match {
case Some(coverageResult) =>
List(new RunTests.CodeCoverageResultTooling(coverageResult)).toArray
case None => Array()
}
}

override def getCodeCoverageWarnings: Array[CodeCoverageWarning] = Array()

override def getFailures: Array[RunTestFailure] = Array()
}

val errors = errorBuilder.result()
if (errors.nonEmpty) {
ActionFailure(errors.mkString(";"))
} else {
ApexTestUtils.processCodeCoverage(runTestResult, session) match {
case Some(codeCoverageReport) =>
ActionSuccess(com.neowit.response.LoadApexCodeCoverageAggregateResult(codeCoverageReport))
case None =>
ActionFailure("No coverage available for " + classOrTriggerName)
}
}
case None =>
ActionFailure("Missing required parameter: classOrTriggerName")
}
Future.successful(actionResult)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,20 +42,40 @@ object RunTestConversions {
result
}

def toCodeCoverageResult(coverageAggregate: ApexCodeCoverageAggregate): com.sforce.soap.tooling.CodeCoverageResult = {
val res = new com.sforce.soap.tooling.CodeCoverageResult()
res.setId(coverageAggregate.ApexClassOrTriggerId)
res.setName(coverageAggregate.ApexClassOrTrigger.Name)
res.setNumLocations(coverageAggregate.NumLinesCovered + coverageAggregate.NumLinesUncovered )
res.setNumLocationsNotCovered(coverageAggregate.NumLinesUncovered)
res.setLocationsNotCovered(
coverageAggregate.Coverage.uncoveredLines.map{line =>
val loc = new com.sforce.soap.tooling.CodeLocation()
loc.setLine(line)
loc
}
)
res
}

private def toCodeCoverageResult(coverage: List[TestCodeCoverage]): Array[com.sforce.soap.tooling.CodeCoverageResult] = {
coverage.map{c =>
val res = new com.sforce.soap.tooling.CodeCoverageResult()
c.`type`.foreach(res.setType(_))
//c.dmlInfo
c.id.foreach(res.setId(_))
val locationsNotCovered =
c.locationsNotCovered.map{loc =>
val res = new com.sforce.soap.tooling.CodeLocation()
loc.column.foreach(res.setColumn(_))
loc.line.foreach(res.setLine(_))
loc.numExecutions.foreach(res.setNumExecutions(_))
loc.time.foreach(res.setTime(_))
res
c.locationsNotCovered match {
case Some(locations) =>
locations.map{loc =>
val res = new com.sforce.soap.tooling.CodeLocation()
loc.column.foreach(res.setColumn(_))
loc.line.foreach(res.setLine(_))
loc.numExecutions.foreach(res.setNumExecutions(_))
loc.time.foreach(res.setTime(_))
res
}
case None => Nil
}
res.setLocationsNotCovered(locationsNotCovered.toArray)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ trait RunTestJsonSupport extends JsonSupport {
implicit val TestFailureFormat = jsonFormat10(TestFailure)

implicit val RunTestsSynchronousResultFormat = jsonFormat8(RunTestsSynchronousResult)

implicit val ApexClassOrTriggerFormat = jsonFormat1(ApexClassOrTrigger)
implicit val CoverageFormat = jsonFormat2(Coverage)
implicit val ApexCodeCoverageAggregateFormat = jsonFormat5(ApexCodeCoverageAggregate)
}
object RunTestJsonSupport {

Expand Down Expand Up @@ -89,4 +93,13 @@ object RunTestJsonSupport {

case class CodeLocation(line: Option[Int], column: Option[Int], numExecutions: Option[Int], time: Option[Double])

case class ApexClassOrTrigger(Name: String)
case class Coverage(coveredLines: Array[Int], uncoveredLines: Array[Int])

case class ApexCodeCoverageAggregate(ApexClassOrTrigger: ApexClassOrTrigger,
ApexClassOrTriggerId: String,
Coverage: Coverage,
NumLinesCovered: Int,
NumLinesUncovered: Int)

}
1 change: 1 addition & 0 deletions src/main/scala/com/neowit/response/BaseResult.scala
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,5 @@ case class ListModifiedResult(modified: List[File], deleted: List[File]) extends
case class LoginOauthResult(tokens: Option[Oauth2Tokens], resultFileOpt: Option[File]) extends BaseResult
case class RefreshMetadataResult(retrieveResult: Option[UpdateFromRetrieveResult], modifiedFiles: List[File]) extends BaseResult
case class SoqlQueryResult(queryReport: SoqlQueryReport) extends BaseResult
case class LoadApexCodeCoverageAggregateResult(coverageReport: CodeCoverageReport) extends BaseResult

Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ class ResponseWriterVim(out: OutputStream, autoFlush: Boolean = true, append: Bo
case res @ RefreshMetadataResult(_, _) => new RefreshMetadata(this).send(res)
case res @ RunTestsResult(_, _, _, _, _) => new RunTests(this).send(res)
case res @ SoqlQueryResult(_) => new SoqlQuery(this).send(res)
case res @ LoadApexCodeCoverageAggregateResult(_) => new RunTests(this).send(res)
}
}

Expand Down
10 changes: 10 additions & 0 deletions src/main/scala/com/neowit/response/protocols/vim/RunTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -140,4 +140,14 @@ class RunTests(writer: ResponseWriterVim) extends VimProtocol[RunTestsResult] {
}
Unit
}
def send(result: LoadApexCodeCoverageAggregateResult): Unit = {
printCoverageReport(writer, Option(result.coverageReport))

prepareDetailedPerFileCoverage(Option(result.coverageReport)) match {
case Some(resultFile) =>
writer.send("COVERAGE_FILE=" + resultFile.getAbsolutePath)
case None =>
}
Unit
}
}

0 comments on commit a487dc5

Please sign in to comment.