-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add classification/labelling of test cases
Issue 71: #71 Taken from a combination of the following PRs: - hedgehogqa/haskell-hedgehog#253 - hedgehogqa/haskell-hedgehog#262
- Loading branch information
Showing
5 changed files
with
186 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
package hedgehog.core | ||
|
||
/** Whether a test is covered by a classifier, and therefore belongs to a `Class` */ | ||
sealed trait Cover { | ||
|
||
def ++(o: Cover): Cover = | ||
this match { | ||
case Cover.NoCover => | ||
o match { | ||
case Cover.NoCover => | ||
Cover.NoCover | ||
case Cover.Cover => | ||
Cover.Cover | ||
} | ||
case Cover.Cover => | ||
Cover.Cover | ||
} | ||
} | ||
|
||
object Cover { | ||
|
||
case object NoCover extends Cover | ||
case object Cover extends Cover | ||
|
||
implicit def Boolean2Cover(b: Boolean): Cover = | ||
if (b) Cover else NoCover | ||
} | ||
|
||
/** The total number of tests which are covered by a classifier. */ | ||
case class CoverCount(toInt: Int) { | ||
|
||
def +(o: CoverCount): CoverCount = | ||
CoverCount(toInt + o.toInt) | ||
|
||
def percentage(tests: SuccessCount): CoverPercentage = | ||
CoverPercentage(((toInt.toDouble / tests.value.toDouble) * 100 * 10).round / 10) | ||
} | ||
|
||
object CoverCount { | ||
|
||
def fromCover(c: Cover): CoverCount = | ||
c match { | ||
case Cover.NoCover => | ||
CoverCount(0) | ||
case Cover.Cover => | ||
CoverCount(1) | ||
} | ||
} | ||
|
||
/** The relative number of tests which are covered by a classifier. */ | ||
case class CoverPercentage(toDouble: Double) | ||
|
||
object CoverPercentage { | ||
|
||
implicit def Double2CoveragePercentage(d: Double): CoverPercentage = | ||
CoverPercentage(d) | ||
} | ||
|
||
/** The name of a classifier. */ | ||
case class LabelName(render: String) | ||
|
||
object LabelName { | ||
|
||
implicit def String2LabelName(s: String): LabelName = | ||
LabelName(s) | ||
} | ||
|
||
/** | ||
* The extent to which a test is covered by a classifier. | ||
* | ||
* _When a classifier's coverage does not exceed the required minimum, the test will be failed._ | ||
*/ | ||
case class Label[A]( | ||
name : LabelName | ||
, minimum : CoverPercentage | ||
, annotation : A | ||
) | ||
|
||
object Label { | ||
|
||
def covered(label: Label[CoverCount], tests: SuccessCount): Boolean = | ||
label.annotation.percentage(tests).toDouble >= label.minimum.toDouble | ||
} | ||
|
||
case class Coverage[A](labels: Map[LabelName, Label[A]]) | ||
|
||
object Coverage { | ||
|
||
def empty[A]: Coverage[A] = | ||
Coverage(Map.empty[LabelName, Label[A]]) | ||
|
||
def fromLogs(logs: List[Log]): Coverage[CoverCount] = | ||
fromLabels(logs.flatMap { | ||
case Log.LabelX(l) => | ||
List(l) | ||
case _ => | ||
Nil | ||
}) | ||
|
||
def fromLabels(labels: List[Label[Cover]]): Coverage[CoverCount] = { | ||
val cv = labels.map(l => Coverage(Map(l.name -> l))) | ||
.foldLeft(Coverage.empty[Cover])(union(_, _)(_ ++ _)) | ||
cv.copy(labels = cv.labels.mapValues(l => l.copy(annotation = CoverCount.fromCover(l.annotation))).map(x => x)) | ||
} | ||
|
||
def union[A](a: Coverage[A], b: Coverage[A])(append: (A, A) => A): Coverage[A] = | ||
Coverage(b.labels.toList.foldLeft(a.labels) { case (m, (k, v)) => | ||
m + (k -> m.get(k).map(x => x.copy(annotation = append(x.annotation, v.annotation))).getOrElse(v)) | ||
}) | ||
|
||
def success(coverage: Coverage[CoverCount], tests: SuccessCount): Boolean = | ||
coverage.labels.values.filter(!Label.covered(_, tests)).isEmpty | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package hedgehog | ||
|
||
import hedgehog.core._ | ||
import hedgehog.runner._ | ||
|
||
object CoverageTest extends Properties { | ||
|
||
override def tests: List[Test] = | ||
List( | ||
example("test", testClassify) | ||
) | ||
|
||
def testClassify: Result = { | ||
val g = | ||
for { | ||
_ <- Gen.boolean.forAll | ||
.cover(50, "true", x => x) | ||
.cover(50, "false", x => !x) | ||
} yield Result.success | ||
|
||
val r = Property.checkRandom(PropertyConfig.default, g) | ||
Result.all(List( | ||
r.coverage.labels.keys.toList.sortBy(_.render) ==== Nil | ||
, r.status ==== Status.ok | ||
)) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters