Skip to content

Commit

Permalink
Coverage tool with 2 basic test cases
Browse files Browse the repository at this point in the history
  • Loading branch information
tareq97 committed Jun 5, 2020
1 parent c75c3bd commit 0e62af4
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 140 deletions.
7 changes: 6 additions & 1 deletion build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ object runtime extends SwamModule with PublishModule {

def artifactName = "swam-runtime"

def ivyDeps = Agg(ivy"com.github.pureconfig::pureconfig-enumeratum:$pureconfigVersion")
def ivyDeps = Agg(ivy"com.github.pureconfig::pureconfig-enumeratum:$pureconfigVersion", ivy"com.nrinaudo::kantan.csv:0.6.1")

def pomSettings =
PomSettings(
Expand All @@ -179,6 +179,11 @@ object runtime extends SwamModule with PublishModule {
def moduleDeps = super.moduleDeps ++ Seq(runtime.test)
def testFrameworks = Seq("swam.util.Framework")
}

object coverage extends Tests with ScalafmtModule {
def moduleDeps = super.moduleDeps ++ Seq(runtime.test)
def testFrameworks = Seq("swam.util.Framework")
}
}

}
Expand Down
2 changes: 1 addition & 1 deletion cli/src/swam/cli/Main.scala
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ object Main extends CommandIOApp(name = "swam-cli", header = "Swam from the comm
compiled <- engine.compile(module)
instance <- doRunCov(compiled, main, dirs, args, wasi, time, blocker)
//_ <- if(coverage) IO(CoverageType.instCoverage(file,instance)) else IO(None)
_ <- if(coverage) IO(CoverageType.instCoverage_updated(Option(out),file,instance,coverage)) else IO(None)
_ <- if(coverage) IO(CoverageType.instCoverage(Option(out),file,instance,coverage)) else IO(None)
} yield ExitCode.Success
case Validate(file, wat, dev) =>
val throwableFormat =
Expand Down
158 changes: 52 additions & 106 deletions runtime/src/swam/runtime/coverage/Coverage.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ package coverage
* ==Overview==
*
* */
import kantan.csv._
import kantan.csv.ops._

import scala.collection.mutable.ListBuffer

import swam.runtime._
Expand All @@ -21,108 +24,17 @@ import cats.effect.IO

import java.nio.file._
import java.util.logging._
import java.io.PrintWriter
import java.io.BufferedWriter
import java.io.FileWriter
import java.nio.file.Path
import java.io.File

class ModuleCoverageInfo{
private var _methodName: String = ""
private var _coveredInst: Long = 0
private var _totalInst: Long = 0

def getMethodName = this._methodName
def setMethodName(str:String) : Unit = {
this._methodName = str
}

def getCoveredInst = this._coveredInst
def setCoveredInst(ci:Long) : Unit = {
this._coveredInst = ci
}

def getTotalInst = this._totalInst
def setTotalInst(ti:Long) : Unit = {
this._totalInst = ti
}
}
case class ModuleCoverageInfo(methodName: String, coveredInst : Long, totalInst : Long)

/** Factory for [[swam.runtime.coverage.CoverageType]] instances. */
object CoverageType{
/** Creates a person with a given name and age.
*
* @param watOrWasm the filename with absolute path
* @param instance the compiled webassembly functions in the Instance[F] form.
*/
def instCoverage(watOrWasm: Path,instance : Instance[IO]) : Unit = {
/**
* Todo code for printing the code coverage from swam-cli in below mentioned formats.
* 1) console
* 2) csv
* 3) html
* val logger = new PrintWriter(new BufferedWriter(new FileWriter("coverage.csv", true)))
logger.println(s"\u2500"*25)
logger.println("Instruction Coverage- " + watOrWasm.getFileName())
logger.println(s"\u2500"*25)
val logger = new PrintWriter(new BufferedWriter(new FileWriter("coverage.csv")))
*/
val replaceFile = watOrWasm.getFileName.toString().replaceAll("\\.\\w+", "")
val logger = new PrintWriter(new BufferedWriter(new FileWriter(replaceFile +".ic.csv")))
logger.println("Method Name, Covered Instruction, Total Instruction")
instance.module.functions.foreach(f => {
val count = f.code
.collect {
case x: Coverage[IO] => {
x
}
}
.count(x => x.hitCount > 0)
//logger.println(s"${f.idx} -> ${100.0 * count / f.code.length}%")
instance.module.names.flatMap(_.subsections.collectFirstSome {
case FunctionNames(names) =>
//println(names.get(f.idx))
names.get(f.idx) match {
//case Some(x) => logger.println(s"${x} -----> ${100.0 * count / f.code.length}%")
case Some(x) => logger.println(s"${x}, ${count}, ${f.code.length}")
//case _ => logger.println(s"Function name does not exist -----> ${100.0 * count / f.code.length}%")
case _ => logger.println(s"N/A, ${count} , ${f.code.length}")
}
names.get(f.idx)
case _ =>
None
})
})
logger.close()
}

//logger
//logger type case classes
//instance class

def instCoverage_updated(dir: Any, watOrWasm: Path,instance : Instance[IO], logOrNot: Boolean) = {
val list = BuildCoverage(instance)
if(logOrNot)
LogCoverage(dir, watOrWasm, list)
}

def LogCoverage(dir: Any, watOrWasm: Path, list: ListBuffer[ModuleCoverageInfo]) = {
val fn = watOrWasm.getFileName.toString
val index = fn.lastIndexOf('.')
val mn : String = fn.substring(0, index)
val logger = dir match {
case Some(x) => new PrintWriter(new BufferedWriter(new FileWriter(x.toString + "/" + mn +".ic.csv")))
case _ => new PrintWriter(new BufferedWriter(new FileWriter(mn +".ic.csv")))
}

logger.println("Method Name, Covered Instruction, Total Instruction")
list.map(f => logger.println(s"${f.getMethodName}, ${f.getCoveredInst}, ${f.getTotalInst}"))
logger.close()
}

def BuildCoverage(instance : Instance[IO]) : ListBuffer[ModuleCoverageInfo]= {
def buildCoverage(instance : Instance[IO]) : ListBuffer[ModuleCoverageInfo]= {
val covList = new ListBuffer[ModuleCoverageInfo]();
println(instance.module.functions.foreach(f=>println(f)))
//println(instance.module.functions.foreach(f=>println(f)))
instance.module.functions.foreach(f => {
val count = f.code
.collect {
Expand All @@ -136,26 +48,60 @@ object CoverageType{
//println(names.get(f.idx))
names.get(f.idx) match {
case Some(x) => {
val xv = new ModuleCoverageInfo()
xv.setMethodName(x.toString)
xv.setCoveredInst(count)
xv.setTotalInst(f.code.length)
covList += xv
val cc= ModuleCoverageInfo(x.toString, count, f.code.length)
covList += cc
}
case _ => {
val xv = new ModuleCoverageInfo()
xv.setMethodName("N/A")
xv.setCoveredInst(count)
xv.setTotalInst(f.code.length)
covList += xv
val cc= ModuleCoverageInfo("N/A", count, f.code.length)
covList += cc
}
}
names.get(f.idx)
case _ =>
None
})
})
//logger.close()
covList
}
}


private def logCoverage(dir: Any, watOrWasm: Path, list: ListBuffer[ModuleCoverageInfo]) : Unit = {

implicit val modEncoder: HeaderEncoder[ModuleCoverageInfo] =
HeaderEncoder.caseEncoder("Method Name", "Covered Instruction", "Total Instruction")(ModuleCoverageInfo.unapply _)

val fn = watOrWasm.getFileName.toString
val index = fn.lastIndexOf('.')
val mn : String = fn.substring(0, index)
val logger:String = dir match {
case Some(x) => x.toString + "/" + mn +".ic.csv"
case _ => mn +".ic.csv"
}

val out = new File(logger)

val writer = out.asCsvWriter[ModuleCoverageInfo](rfc.withHeader)

/*for (l <- list) {
val ModuleCoverageInfo(methodName, coveredInst, totalInst) = l
logger.println(s"${methodName}, ${coveredInst}, ${totalInst}")
}*/

list.map(f => {
val ModuleCoverageInfo(m, c, t) = f
writer.write(ModuleCoverageInfo(m, c, t))
})

writer.close()
}

/** Creates a person with a given name and age.
* @param watOrWasm the filename with absolute path
* @param instance the compiled webassembly functions in the Instance[F] form.
*/
def instCoverage(dir: Any, watOrWasm: Path,instance : Instance[IO], logOrNot: Boolean) = {
val list = buildCoverage(instance)
if(logOrNot)
logCoverage(dir, watOrWasm, list)
}
}
Binary file added runtime/test/resources/coverage-test/1inst.wasm
Binary file not shown.
Binary file added runtime/test/resources/coverage-test/1inst_1.wasm
Binary file not shown.
7 changes: 7 additions & 0 deletions runtime/test/resources/coverage-test/add.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
(module $addt
(func $add (export "add") (param $i i32) (param $j i32) (result i32)
local.get $i
local.get $j
i32.add
)
)
71 changes: 39 additions & 32 deletions runtime/test/src/coverage/CoverageTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,62 +16,69 @@

package swam
package runtime
package trace
package coverage

import swam._
import runtime.coverage._
import text._
import runtime._
import runtime.coverage._
import cats.effect._

import utest._

import java.nio.file.Paths

import scala.collection.mutable.ListBuffer

object CoverageTests extends TestSuite {

def runLog(handler: HandlerType) = {
val conf: TraceConfiguration = new TraceConfiguration(
handler,
"\n",
"*",
"ALL",
new TracerFileHandlerCondiguration(
"log.txt",
true,
"."
),
new SocketHanndlerCondiguration("localhost", 8080),
new CustomTracerConfiguration("unknown")
)

val tracer = new JULTracer(conf)

tracer.traceEvent(EventType.SPush, List("123", "4", "123"))
}

def runCoverage(wasmFile: String) = {
def runCoverage(wasmFile: String) : Instance[IO]= {
implicit val cs = IO.contextShift(scala.concurrent.ExecutionContext.global)

def instantiate(p: String): Instance[IO] =
val instance: Instance[IO] =
Blocker[IO].use { blocker =>
for {
engine <- Engine[IO](blocker)
tcompiler <- Compiler[IO](blocker)
m <- engine.compile(tcompiler.stream(Paths.get(p), true, blocker))
m <- engine.compile(tcompiler.stream(Paths.get(wasmFile), true, blocker))
i <- m.instantiate
} yield i
}.unsafeRunSync()
instance
}

val instance = instantiate(wasmFile)

val list : ListBuffer[ModuleCoverageInfo] = BuildCoverage(instance)
def test1(wasmFile: String) = {

val instance = runCoverage(wasmFile)
val list : ListBuffer[ModuleCoverageInfo] = CoverageType.buildCoverage(instance)

for (l <- list) {
val ModuleCoverageInfo(m, c, t) = l
assert(m == "add", c==0, t==4)
}
}

def test2(wasmFile: String) = {

val instance = runCoverage(wasmFile)
val add = instance.exports.typed.function[(Int, Int), Int]("add").unsafeRunSync()
add(1,2).unsafeRunSync()
val list : ListBuffer[ModuleCoverageInfo] = CoverageType.buildCoverage(instance)

for (l <- list) {
val ModuleCoverageInfo(m, c, t) = l
assert(m == "add", c==4, t==4)
}

}

val tests = Tests {
//"console_tracer" - runLog(HandlerType.Console)
//"file_tracer" - runLog(HandlerType.File)
// "socket_tracer" - runLog(HandlerType.Socket)

// "inst 1" - runCoverage('1inst.wasm')
/**
TODO more manual test cases to be added.
*/
//"inst1" - runCoverage("runtime/test/resources/coverage-test/1_inst.wasm")
"add" - test1("runtime/test/resources/coverage-test/add.wat")
"addWithCov" - test2("runtime/test/resources/coverage-test/add.wat")
}
}

0 comments on commit 0e62af4

Please sign in to comment.