mirror of https://github.com/sbt/sbt.git
Merge pull request #1316 from sbt/wip/fix-broken-build
This doesn't quite fix the build, but gets closer
This commit is contained in:
commit
329278b531
|
|
@ -3,30 +3,29 @@ package sbt
|
|||
import java.io.File
|
||||
import Types.:+:
|
||||
|
||||
object CacheTest// extends Properties("Cache test")
|
||||
object CacheTest // extends Properties("Cache test")
|
||||
{
|
||||
val lengthCache = new File("/tmp/length-cache")
|
||||
val cCache = new File("/tmp/c-cache")
|
||||
val lengthCache = new File("/tmp/length-cache")
|
||||
val cCache = new File("/tmp/c-cache")
|
||||
|
||||
import Cache._
|
||||
import FileInfo.hash._
|
||||
import Ordering._
|
||||
import sbinary.DefaultProtocol.FileFormat
|
||||
def test
|
||||
{
|
||||
lazy val create = new File("test")
|
||||
import Cache._
|
||||
import FileInfo.hash._
|
||||
import Ordering._
|
||||
import sbinary.DefaultProtocol.FileFormat
|
||||
def test {
|
||||
lazy val create = new File("test")
|
||||
|
||||
val length = cached(lengthCache) {
|
||||
(f: File) => { println("File length: " + f.length); f.length }
|
||||
}
|
||||
val length = cached(lengthCache) {
|
||||
(f: File) => { println("File length: " + f.length); f.length }
|
||||
}
|
||||
|
||||
lazy val fileLength = length(create)
|
||||
lazy val fileLength = length(create)
|
||||
|
||||
val c = cached(cCache) { (in: (File :+: Long :+: HNil)) =>
|
||||
val file :+: len :+: HNil = in
|
||||
println("File: " + file + " (" + file.exists + "), length: " + len)
|
||||
(len+1) :+: file :+: HNil
|
||||
}
|
||||
c(create :+: fileLength :+: HNil)
|
||||
}
|
||||
val c = cached(cCache) { (in: (File :+: Long :+: HNil)) =>
|
||||
val file :+: len :+: HNil = in
|
||||
println("File: " + file + " (" + file.exists + "), length: " + len)
|
||||
(len + 1) :+: file :+: HNil
|
||||
}
|
||||
c(create :+: fileLength :+: HNil)
|
||||
}
|
||||
}
|
||||
|
|
@ -8,49 +8,43 @@ import Prop._
|
|||
|
||||
import scala.collection.mutable.HashSet
|
||||
|
||||
object DagSpecification extends Properties("Dag")
|
||||
{
|
||||
property("No repeated nodes") = forAll{ (dag: TestDag) => isSet(dag.topologicalSort) }
|
||||
property("Sort contains node") = forAll{ (dag: TestDag) => dag.topologicalSort.contains(dag) }
|
||||
property("Dependencies precede node") = forAll{ (dag: TestDag) => dependenciesPrecedeNodes(dag.topologicalSort) }
|
||||
object DagSpecification extends Properties("Dag") {
|
||||
property("No repeated nodes") = forAll { (dag: TestDag) => isSet(dag.topologicalSort) }
|
||||
property("Sort contains node") = forAll { (dag: TestDag) => dag.topologicalSort.contains(dag) }
|
||||
property("Dependencies precede node") = forAll { (dag: TestDag) => dependenciesPrecedeNodes(dag.topologicalSort) }
|
||||
|
||||
implicit lazy val arbTestDag: Arbitrary[TestDag] = Arbitrary(Gen.sized(dagGen))
|
||||
private def dagGen(nodeCount: Int): Gen[TestDag] =
|
||||
{
|
||||
val nodes = new HashSet[TestDag]
|
||||
def nonterminalGen(p: Gen.Parameters): Gen[TestDag] =
|
||||
{
|
||||
for(i <- 0 until nodeCount; nextDeps <- Gen.someOf(nodes).apply(p))
|
||||
nodes += new TestDag(i, nextDeps)
|
||||
for(nextDeps <- Gen.someOf(nodes)) yield
|
||||
new TestDag(nodeCount, nextDeps)
|
||||
}
|
||||
Gen.parameterized(nonterminalGen)
|
||||
}
|
||||
implicit lazy val arbTestDag: Arbitrary[TestDag] = Arbitrary(Gen.sized(dagGen))
|
||||
private def dagGen(nodeCount: Int): Gen[TestDag] =
|
||||
{
|
||||
val nodes = new HashSet[TestDag]
|
||||
def nonterminalGen(p: Gen.Parameters): Gen[TestDag] =
|
||||
{
|
||||
for (i <- 0 until nodeCount; nextDeps <- Gen.someOf(nodes).apply(p))
|
||||
nodes += new TestDag(i, nextDeps)
|
||||
for (nextDeps <- Gen.someOf(nodes)) yield new TestDag(nodeCount, nextDeps)
|
||||
}
|
||||
Gen.parameterized(nonterminalGen)
|
||||
}
|
||||
|
||||
private def isSet[T](c: Seq[T]) = Set(c: _*).size == c.size
|
||||
private def dependenciesPrecedeNodes(sort: List[TestDag]) =
|
||||
{
|
||||
val seen = new HashSet[TestDag]
|
||||
def iterate(remaining: List[TestDag]): Boolean =
|
||||
{
|
||||
remaining match
|
||||
{
|
||||
case Nil => true
|
||||
case node :: tail =>
|
||||
if(node.dependencies.forall(seen.contains) && !seen.contains(node))
|
||||
{
|
||||
seen += node
|
||||
iterate(tail)
|
||||
}
|
||||
else
|
||||
false
|
||||
}
|
||||
}
|
||||
iterate(sort)
|
||||
}
|
||||
private def isSet[T](c: Seq[T]) = Set(c: _*).size == c.size
|
||||
private def dependenciesPrecedeNodes(sort: List[TestDag]) =
|
||||
{
|
||||
val seen = new HashSet[TestDag]
|
||||
def iterate(remaining: List[TestDag]): Boolean =
|
||||
{
|
||||
remaining match {
|
||||
case Nil => true
|
||||
case node :: tail =>
|
||||
if (node.dependencies.forall(seen.contains) && !seen.contains(node)) {
|
||||
seen += node
|
||||
iterate(tail)
|
||||
} else
|
||||
false
|
||||
}
|
||||
}
|
||||
iterate(sort)
|
||||
}
|
||||
}
|
||||
class TestDag(id: Int, val dependencies: Iterable[TestDag]) extends Dag[TestDag]
|
||||
{
|
||||
override def toString = id + "->" + dependencies.mkString("[", ",", "]")
|
||||
class TestDag(id: Int, val dependencies: Iterable[TestDag]) extends Dag[TestDag] {
|
||||
override def toString = id + "->" + dependencies.mkString("[", ",", "]")
|
||||
}
|
||||
|
|
@ -1,35 +1,32 @@
|
|||
package sbt
|
||||
|
||||
import org.scalacheck._
|
||||
import Prop._
|
||||
import org.scalacheck._
|
||||
import Prop._
|
||||
|
||||
object KeyTest extends Properties("AttributeKey")
|
||||
{
|
||||
property("equality") = {
|
||||
compare(AttributeKey[Int]("test"), AttributeKey[Int]("test"), true) &&
|
||||
compare(AttributeKey[Int]("test"), AttributeKey[Int]("test", "description"), true) &&
|
||||
compare(AttributeKey[Int]("test", "a"), AttributeKey[Int]("test", "b"), true) &&
|
||||
compare(AttributeKey[Int]("test"), AttributeKey[Int]("tests"), false) &&
|
||||
compare(AttributeKey[Int]("test"), AttributeKey[Double]("test"), false) &&
|
||||
compare(AttributeKey[java.lang.Integer]("test"), AttributeKey[Int]("test"), false) &&
|
||||
compare(AttributeKey[Map[Int, String]]("test"), AttributeKey[Map[Int, String]]("test"), true) &&
|
||||
compare(AttributeKey[Map[Int, String]]("test"), AttributeKey[Map[Int, _]]("test"), false)
|
||||
}
|
||||
object KeyTest extends Properties("AttributeKey") {
|
||||
property("equality") = {
|
||||
compare(AttributeKey[Int]("test"), AttributeKey[Int]("test"), true) &&
|
||||
compare(AttributeKey[Int]("test"), AttributeKey[Int]("test", "description"), true) &&
|
||||
compare(AttributeKey[Int]("test", "a"), AttributeKey[Int]("test", "b"), true) &&
|
||||
compare(AttributeKey[Int]("test"), AttributeKey[Int]("tests"), false) &&
|
||||
compare(AttributeKey[Int]("test"), AttributeKey[Double]("test"), false) &&
|
||||
compare(AttributeKey[java.lang.Integer]("test"), AttributeKey[Int]("test"), false) &&
|
||||
compare(AttributeKey[Map[Int, String]]("test"), AttributeKey[Map[Int, String]]("test"), true) &&
|
||||
compare(AttributeKey[Map[Int, String]]("test"), AttributeKey[Map[Int, _]]("test"), false)
|
||||
}
|
||||
|
||||
def compare(a: AttributeKey[_], b: AttributeKey[_], same: Boolean) =
|
||||
("a.label: " + a.label) |:
|
||||
("a.manifest: " + a.manifest) |:
|
||||
("b.label: " + b.label) |:
|
||||
("b.manifest: " + b.manifest) |:
|
||||
("expected equal? " + same) |:
|
||||
compare0(a, b, same)
|
||||
def compare(a: AttributeKey[_], b: AttributeKey[_], same: Boolean) =
|
||||
("a.label: " + a.label) |:
|
||||
("a.manifest: " + a.manifest) |:
|
||||
("b.label: " + b.label) |:
|
||||
("b.manifest: " + b.manifest) |:
|
||||
("expected equal? " + same) |:
|
||||
compare0(a, b, same)
|
||||
|
||||
def compare0(a: AttributeKey[_], b: AttributeKey[_], same: Boolean) =
|
||||
if(same)
|
||||
{
|
||||
("equality" |: (a == b)) &&
|
||||
("hash" |: (a.hashCode == b.hashCode))
|
||||
}
|
||||
else
|
||||
("equality" |: (a != b))
|
||||
def compare0(a: AttributeKey[_], b: AttributeKey[_], same: Boolean) =
|
||||
if (same) {
|
||||
("equality" |: (a == b)) &&
|
||||
("hash" |: (a.hashCode == b.hashCode))
|
||||
} else
|
||||
("equality" |: (a != b))
|
||||
}
|
||||
|
|
@ -7,11 +7,11 @@ import Types._
|
|||
|
||||
// compilation test
|
||||
object LiteralTest {
|
||||
def x[A[_],B[_]](f: A ~> B) = f
|
||||
def x[A[_], B[_]](f: A ~> B) = f
|
||||
|
||||
import Param._
|
||||
val f = x { (p: Param[Option,List]) => p.ret( p.in.toList ) }
|
||||
val f = x { (p: Param[Option, List]) => p.ret(p.in.toList) }
|
||||
|
||||
val a: List[Int] = f( Some(3) )
|
||||
val b: List[String] = f( Some("aa") )
|
||||
val a: List[Int] = f(Some(3))
|
||||
val b: List[String] = f(Some("aa"))
|
||||
}
|
||||
|
|
@ -6,14 +6,13 @@ package sbt
|
|||
import Types._
|
||||
|
||||
// compilation test
|
||||
object PMapTest
|
||||
{
|
||||
val mp = new DelegatingPMap[Some, Id](new collection.mutable.HashMap)
|
||||
mp(Some("asdf")) = "a"
|
||||
mp(Some(3)) = 9
|
||||
val x = Some(3) :^: Some("asdf") :^: KNil
|
||||
val y = x.transform[Id](mp)
|
||||
assert(y.head == 9)
|
||||
assert(y.tail.head == "a")
|
||||
assert(y.tail.tail == KNil)
|
||||
object PMapTest {
|
||||
val mp = new DelegatingPMap[Some, Id](new collection.mutable.HashMap)
|
||||
mp(Some("asdf")) = "a"
|
||||
mp(Some(3)) = 9
|
||||
val x = Some(3) :^: Some("asdf") :^: KNil
|
||||
val y = x.transform[Id](mp)
|
||||
assert(y.head == 9)
|
||||
assert(y.tail.head == "a")
|
||||
assert(y.tail.tail == KNil)
|
||||
}
|
||||
|
|
@ -10,78 +10,78 @@ final case class Scope(nestIndex: Int, idAtIndex: Int = 0)
|
|||
// Lots of type constructors would become binary, which as you may know requires lots of type lambdas
|
||||
// when you want a type function with only one parameter.
|
||||
// That would be a general pain.)
|
||||
object SettingsExample extends Init[Scope]
|
||||
{
|
||||
// Provides a way of showing a Scope+AttributeKey[_]
|
||||
val showFullKey: Show[ScopedKey[_]] = new Show[ScopedKey[_]] {
|
||||
def apply(key: ScopedKey[_]) = s"${key.scope.nestIndex}(${key.scope.idAtIndex})/${key.key.label}"
|
||||
}
|
||||
object SettingsExample extends Init[Scope] {
|
||||
// Provides a way of showing a Scope+AttributeKey[_]
|
||||
val showFullKey: Show[ScopedKey[_]] = new Show[ScopedKey[_]] {
|
||||
def apply(key: ScopedKey[_]) = s"${key.scope.nestIndex}(${key.scope.idAtIndex})/${key.key.label}"
|
||||
}
|
||||
|
||||
// A sample delegation function that delegates to a Scope with a lower index.
|
||||
val delegates: Scope => Seq[Scope] = { case s @ Scope(index, proj) =>
|
||||
s +: (if(index <= 0) Nil else { (if (proj > 0) List(Scope(index)) else Nil) ++: delegates(Scope(index-1)) })
|
||||
}
|
||||
// A sample delegation function that delegates to a Scope with a lower index.
|
||||
val delegates: Scope => Seq[Scope] = {
|
||||
case s @ Scope(index, proj) =>
|
||||
s +: (if (index <= 0) Nil else { (if (proj > 0) List(Scope(index)) else Nil) ++: delegates(Scope(index - 1)) })
|
||||
}
|
||||
|
||||
// Not using this feature in this example.
|
||||
val scopeLocal: ScopeLocal = _ => Nil
|
||||
// Not using this feature in this example.
|
||||
val scopeLocal: ScopeLocal = _ => Nil
|
||||
|
||||
// These three functions + a scope (here, Scope) are sufficient for defining our settings system.
|
||||
// These three functions + a scope (here, Scope) are sufficient for defining our settings system.
|
||||
}
|
||||
|
||||
/** Usage Example **/
|
||||
|
||||
object SettingsUsage
|
||||
{
|
||||
import SettingsExample._
|
||||
import Types._
|
||||
object SettingsUsage {
|
||||
import SettingsExample._
|
||||
import Types._
|
||||
|
||||
// Define some keys
|
||||
val a = AttributeKey[Int]("a")
|
||||
val b = AttributeKey[Int]("b")
|
||||
// Define some keys
|
||||
val a = AttributeKey[Int]("a")
|
||||
val b = AttributeKey[Int]("b")
|
||||
|
||||
// Scope these keys
|
||||
val a3 = ScopedKey(Scope(3), a)
|
||||
val a4 = ScopedKey(Scope(4), a)
|
||||
val a5 = ScopedKey(Scope(5), a)
|
||||
// Scope these keys
|
||||
val a3 = ScopedKey(Scope(3), a)
|
||||
val a4 = ScopedKey(Scope(4), a)
|
||||
val a5 = ScopedKey(Scope(5), a)
|
||||
|
||||
val b4 = ScopedKey(Scope(4), b)
|
||||
val b4 = ScopedKey(Scope(4), b)
|
||||
|
||||
// Define some settings
|
||||
val mySettings: Seq[Setting[_]] = Seq(
|
||||
setting( a3, value( 3 ) ),
|
||||
setting( b4, map(a4)(_ * 3)),
|
||||
update(a5)(_ + 1)
|
||||
)
|
||||
// Define some settings
|
||||
val mySettings: Seq[Setting[_]] = Seq(
|
||||
setting(a3, value(3)),
|
||||
setting(b4, map(a4)(_ * 3)),
|
||||
update(a5)(_ + 1)
|
||||
)
|
||||
|
||||
// "compiles" and applies the settings.
|
||||
// This can be split into multiple steps to access intermediate results if desired.
|
||||
// The 'inspect' command operates on the output of 'compile', for example.
|
||||
val applied: Settings[Scope] = make(mySettings)(delegates, scopeLocal, showFullKey)
|
||||
// "compiles" and applies the settings.
|
||||
// This can be split into multiple steps to access intermediate results if desired.
|
||||
// The 'inspect' command operates on the output of 'compile', for example.
|
||||
val applied: Settings[Scope] = make(mySettings)(delegates, scopeLocal, showFullKey)
|
||||
|
||||
// Show results.
|
||||
/* for(i <- 0 to 5; k <- Seq(a, b)) {
|
||||
// Show results.
|
||||
/* for(i <- 0 to 5; k <- Seq(a, b)) {
|
||||
println( k.label + i + " = " + applied.get( Scope(i), k) )
|
||||
}*/
|
||||
|
||||
/** Output:
|
||||
* For the None results, we never defined the value and there was no value to delegate to.
|
||||
* For a3, we explicitly defined it to be 3.
|
||||
* a4 wasn't defined, so it delegates to a3 according to our delegates function.
|
||||
* b4 gets the value for a4 (which delegates to a3, so it is 3) and multiplies by 3
|
||||
* a5 is defined as the previous value of a5 + 1 and
|
||||
* since no previous value of a5 was defined, it delegates to a4, resulting in 3+1=4.
|
||||
* b5 isn't defined explicitly, so it delegates to b4 and is therefore equal to 9 as well
|
||||
a0 = None
|
||||
b0 = None
|
||||
a1 = None
|
||||
b1 = None
|
||||
a2 = None
|
||||
b2 = None
|
||||
a3 = Some(3)
|
||||
b3 = None
|
||||
a4 = Some(3)
|
||||
b4 = Some(9)
|
||||
a5 = Some(4)
|
||||
b5 = Some(9)
|
||||
**/
|
||||
/**
|
||||
* Output:
|
||||
* For the None results, we never defined the value and there was no value to delegate to.
|
||||
* For a3, we explicitly defined it to be 3.
|
||||
* a4 wasn't defined, so it delegates to a3 according to our delegates function.
|
||||
* b4 gets the value for a4 (which delegates to a3, so it is 3) and multiplies by 3
|
||||
* a5 is defined as the previous value of a5 + 1 and
|
||||
* since no previous value of a5 was defined, it delegates to a4, resulting in 3+1=4.
|
||||
* b5 isn't defined explicitly, so it delegates to b4 and is therefore equal to 9 as well
|
||||
* a0 = None
|
||||
* b0 = None
|
||||
* a1 = None
|
||||
* b1 = None
|
||||
* a2 = None
|
||||
* b2 = None
|
||||
* a3 = Some(3)
|
||||
* b3 = None
|
||||
* a4 = Some(3)
|
||||
* b4 = Some(9)
|
||||
* a5 = Some(4)
|
||||
* b5 = Some(9)
|
||||
*/
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,175 +5,174 @@ import Prop._
|
|||
import SettingsUsage._
|
||||
import SettingsExample._
|
||||
|
||||
object SettingsTest extends Properties("settings")
|
||||
{
|
||||
object SettingsTest extends Properties("settings") {
|
||||
|
||||
import scala.reflect.Manifest
|
||||
import scala.reflect.Manifest
|
||||
|
||||
final val ChainMax = 5000
|
||||
lazy val chainLengthGen = Gen.choose(1, ChainMax)
|
||||
final val ChainMax = 5000
|
||||
lazy val chainLengthGen = Gen.choose(1, ChainMax)
|
||||
|
||||
property("Basic settings test") = secure( all( tests: _*) )
|
||||
property("Basic settings test") = secure(all(tests: _*))
|
||||
|
||||
property("Basic chain") = forAll(chainLengthGen) { (i: Int) =>
|
||||
val abs = math.abs(i)
|
||||
singleIntTest( chain( abs, value(0)), abs )
|
||||
}
|
||||
property("Basic bind chain") = forAll(chainLengthGen) { (i: Int) =>
|
||||
val abs = math.abs(i)
|
||||
singleIntTest( chainBind(value(abs)), 0 )
|
||||
}
|
||||
property("Basic chain") = forAll(chainLengthGen) { (i: Int) =>
|
||||
val abs = math.abs(i)
|
||||
singleIntTest(chain(abs, value(0)), abs)
|
||||
}
|
||||
property("Basic bind chain") = forAll(chainLengthGen) { (i: Int) =>
|
||||
val abs = math.abs(i)
|
||||
singleIntTest(chainBind(value(abs)), 0)
|
||||
}
|
||||
|
||||
property("Allows references to completed settings") = forAllNoShrink(30) { allowedReference }
|
||||
final def allowedReference(intermediate: Int): Prop =
|
||||
{
|
||||
val top = value(intermediate)
|
||||
def iterate(init: Initialize[Int]): Initialize[Int] =
|
||||
bind(init) { t =>
|
||||
if(t <= 0)
|
||||
top
|
||||
else
|
||||
iterate(value(t-1) )
|
||||
}
|
||||
evaluate( setting(chk, iterate(top)) :: Nil); true
|
||||
}
|
||||
property("Allows references to completed settings") = forAllNoShrink(30) { allowedReference }
|
||||
final def allowedReference(intermediate: Int): Prop =
|
||||
{
|
||||
val top = value(intermediate)
|
||||
def iterate(init: Initialize[Int]): Initialize[Int] =
|
||||
bind(init) { t =>
|
||||
if (t <= 0)
|
||||
top
|
||||
else
|
||||
iterate(value(t - 1))
|
||||
}
|
||||
evaluate(setting(chk, iterate(top)) :: Nil); true
|
||||
}
|
||||
|
||||
property("Derived setting chain depending on (prev derived, normal setting)") = forAllNoShrink(Gen.choose(1, 100)) { derivedSettings }
|
||||
final def derivedSettings(nr: Int): Prop =
|
||||
{
|
||||
val genScopedKeys = {
|
||||
val attrKeys = mkAttrKeys[Int](nr)
|
||||
attrKeys map (_ map (ak => ScopedKey(Scope(0), ak)))
|
||||
}
|
||||
forAll(genScopedKeys) { scopedKeys =>
|
||||
val last = scopedKeys.last
|
||||
val derivedSettings: Seq[Setting[Int]] = (
|
||||
for {
|
||||
List(scoped0, scoped1) <- chk :: scopedKeys sliding 2
|
||||
nextInit = if (scoped0 == chk) chk
|
||||
else (scoped0 zipWith chk) { (p, _) => p + 1 }
|
||||
} yield derive(setting(scoped1, nextInit))
|
||||
).toSeq
|
||||
property("Derived setting chain depending on (prev derived, normal setting)") = forAllNoShrink(Gen.choose(1, 100)) { derivedSettings }
|
||||
final def derivedSettings(nr: Int): Prop =
|
||||
{
|
||||
val genScopedKeys = {
|
||||
val attrKeys = mkAttrKeys[Int](nr)
|
||||
attrKeys map (_ map (ak => ScopedKey(Scope(0), ak)))
|
||||
}
|
||||
forAll(genScopedKeys) { scopedKeys =>
|
||||
val last = scopedKeys.last
|
||||
val derivedSettings: Seq[Setting[Int]] = (
|
||||
for {
|
||||
List(scoped0, scoped1) <- chk :: scopedKeys sliding 2
|
||||
nextInit = if (scoped0 == chk) chk
|
||||
else (scoped0 zipWith chk) { (p, _) => p + 1 }
|
||||
} yield derive(setting(scoped1, nextInit))
|
||||
).toSeq
|
||||
|
||||
{ checkKey(last, Some(nr-1), evaluate(setting(chk, value(0)) +: derivedSettings)) :| "Not derived?" } &&
|
||||
{ checkKey( last, None, evaluate(derivedSettings)) :| "Should not be derived" }
|
||||
}
|
||||
}
|
||||
{ checkKey(last, Some(nr - 1), evaluate(setting(chk, value(0)) +: derivedSettings)) :| "Not derived?" } &&
|
||||
{ checkKey(last, None, evaluate(derivedSettings)) :| "Should not be derived" }
|
||||
}
|
||||
}
|
||||
|
||||
private def mkAttrKeys[T](nr: Int)(implicit mf: Manifest[T]): Gen[List[AttributeKey[T]]] =
|
||||
{
|
||||
val alphaStr = Gen.alphaStr
|
||||
for {
|
||||
list <- Gen.listOfN(nr, alphaStr) suchThat (l => l.size == l.distinct.size)
|
||||
item <- list
|
||||
} yield AttributeKey[T](item)
|
||||
}
|
||||
private def mkAttrKeys[T](nr: Int)(implicit mf: Manifest[T]): Gen[List[AttributeKey[T]]] =
|
||||
{
|
||||
val alphaStr = Gen.alphaStr
|
||||
for {
|
||||
list <- Gen.listOfN(nr, alphaStr) suchThat (l => l.size == l.distinct.size)
|
||||
item <- list
|
||||
} yield AttributeKey[T](item)
|
||||
}
|
||||
|
||||
property("Derived setting(s) replace DerivedSetting in the Seq[Setting[_]]") = derivedKeepsPosition
|
||||
final def derivedKeepsPosition: Prop =
|
||||
{
|
||||
val a: ScopedKey[Int] = ScopedKey(Scope(0), AttributeKey[Int]("a"))
|
||||
val b: ScopedKey[Int] = ScopedKey(Scope(0), AttributeKey[Int]("b"))
|
||||
val prop1 = {
|
||||
val settings: Seq[Setting[_]] = Seq(
|
||||
setting(a, value(3)),
|
||||
setting(b, value(6)),
|
||||
derive(setting(b, a)),
|
||||
setting(a, value(5)),
|
||||
setting(b, value(8))
|
||||
)
|
||||
val ev = evaluate(settings)
|
||||
checkKey(a, Some(5), ev) && checkKey(b, Some(8), ev)
|
||||
}
|
||||
val prop2 = {
|
||||
val settings: Seq[Setting[Int]] = Seq(
|
||||
setting(a, value(3)),
|
||||
setting(b, value(6)),
|
||||
derive(setting(b, a)),
|
||||
setting(a, value(5))
|
||||
)
|
||||
val ev = evaluate(settings)
|
||||
checkKey(a, Some(5), ev) && checkKey(b, Some(5), ev)
|
||||
}
|
||||
prop1 && prop2
|
||||
}
|
||||
property("Derived setting(s) replace DerivedSetting in the Seq[Setting[_]]") = derivedKeepsPosition
|
||||
final def derivedKeepsPosition: Prop =
|
||||
{
|
||||
val a: ScopedKey[Int] = ScopedKey(Scope(0), AttributeKey[Int]("a"))
|
||||
val b: ScopedKey[Int] = ScopedKey(Scope(0), AttributeKey[Int]("b"))
|
||||
val prop1 = {
|
||||
val settings: Seq[Setting[_]] = Seq(
|
||||
setting(a, value(3)),
|
||||
setting(b, value(6)),
|
||||
derive(setting(b, a)),
|
||||
setting(a, value(5)),
|
||||
setting(b, value(8))
|
||||
)
|
||||
val ev = evaluate(settings)
|
||||
checkKey(a, Some(5), ev) && checkKey(b, Some(8), ev)
|
||||
}
|
||||
val prop2 = {
|
||||
val settings: Seq[Setting[Int]] = Seq(
|
||||
setting(a, value(3)),
|
||||
setting(b, value(6)),
|
||||
derive(setting(b, a)),
|
||||
setting(a, value(5))
|
||||
)
|
||||
val ev = evaluate(settings)
|
||||
checkKey(a, Some(5), ev) && checkKey(b, Some(5), ev)
|
||||
}
|
||||
prop1 && prop2
|
||||
}
|
||||
|
||||
property("DerivedSetting in ThisBuild scopes derived settings under projects thus allowing safe +=") = forAllNoShrink(Gen.choose(1, 100)) { derivedSettingsScope }
|
||||
final def derivedSettingsScope(nrProjects: Int): Prop =
|
||||
{
|
||||
forAll(mkAttrKeys[Int](2)) { case List(key, derivedKey) =>
|
||||
val projectKeys = for { proj <- 1 to nrProjects } yield ScopedKey(Scope(1, proj), key)
|
||||
val projectDerivedKeys = for { proj <- 1 to nrProjects } yield ScopedKey(Scope(1, proj), derivedKey)
|
||||
val globalKey = ScopedKey(Scope(0), key)
|
||||
val globalDerivedKey = ScopedKey(Scope(0), derivedKey)
|
||||
// Each project defines an initial value, but the update is defined in globalKey.
|
||||
// However, the derived Settings that come from this should be scoped in each project.
|
||||
val settings: Seq[Setting[_]] =
|
||||
derive(setting(globalDerivedKey, SettingsExample.map(globalKey)(_ + 1))) +: projectKeys.map(pk => setting(pk, value(0)))
|
||||
val ev = evaluate(settings)
|
||||
// Also check that the key has no value at the "global" scope
|
||||
val props = for { pk <- projectDerivedKeys } yield checkKey(pk, Some(1), ev)
|
||||
checkKey(globalDerivedKey, None, ev) && Prop.all(props: _*)
|
||||
}
|
||||
}
|
||||
property("DerivedSetting in ThisBuild scopes derived settings under projects thus allowing safe +=") = forAllNoShrink(Gen.choose(1, 100)) { derivedSettingsScope }
|
||||
final def derivedSettingsScope(nrProjects: Int): Prop =
|
||||
{
|
||||
forAll(mkAttrKeys[Int](2)) {
|
||||
case List(key, derivedKey) =>
|
||||
val projectKeys = for { proj <- 1 to nrProjects } yield ScopedKey(Scope(1, proj), key)
|
||||
val projectDerivedKeys = for { proj <- 1 to nrProjects } yield ScopedKey(Scope(1, proj), derivedKey)
|
||||
val globalKey = ScopedKey(Scope(0), key)
|
||||
val globalDerivedKey = ScopedKey(Scope(0), derivedKey)
|
||||
// Each project defines an initial value, but the update is defined in globalKey.
|
||||
// However, the derived Settings that come from this should be scoped in each project.
|
||||
val settings: Seq[Setting[_]] =
|
||||
derive(setting(globalDerivedKey, SettingsExample.map(globalKey)(_ + 1))) +: projectKeys.map(pk => setting(pk, value(0)))
|
||||
val ev = evaluate(settings)
|
||||
// Also check that the key has no value at the "global" scope
|
||||
val props = for { pk <- projectDerivedKeys } yield checkKey(pk, Some(1), ev)
|
||||
checkKey(globalDerivedKey, None, ev) && Prop.all(props: _*)
|
||||
}
|
||||
}
|
||||
|
||||
// Circular (dynamic) references currently loop infinitely.
|
||||
// This is the expected behavior (detecting dynamic cycles is expensive),
|
||||
// but it may be necessary to provide an option to detect them (with a performance hit)
|
||||
// This would test that cycle detection.
|
||||
// property("Catches circular references") = forAll(chainLengthGen) { checkCircularReferences _ }
|
||||
final def checkCircularReferences(intermediate: Int): Prop =
|
||||
{
|
||||
val ccr = new CCR(intermediate)
|
||||
try { evaluate( setting(chk, ccr.top) :: Nil); false }
|
||||
catch { case e: java.lang.Exception => true }
|
||||
}
|
||||
// Circular (dynamic) references currently loop infinitely.
|
||||
// This is the expected behavior (detecting dynamic cycles is expensive),
|
||||
// but it may be necessary to provide an option to detect them (with a performance hit)
|
||||
// This would test that cycle detection.
|
||||
// property("Catches circular references") = forAll(chainLengthGen) { checkCircularReferences _ }
|
||||
final def checkCircularReferences(intermediate: Int): Prop =
|
||||
{
|
||||
val ccr = new CCR(intermediate)
|
||||
try { evaluate(setting(chk, ccr.top) :: Nil); false }
|
||||
catch { case e: java.lang.Exception => true }
|
||||
}
|
||||
|
||||
def tests =
|
||||
for(i <- 0 to 5; k <- Seq(a, b)) yield {
|
||||
val expected = expectedValues(2*i + (if(k == a) 0 else 1))
|
||||
checkKey[Int]( ScopedKey( Scope(i), k ), expected, applied)
|
||||
}
|
||||
def tests =
|
||||
for (i <- 0 to 5; k <- Seq(a, b)) yield {
|
||||
val expected = expectedValues(2 * i + (if (k == a) 0 else 1))
|
||||
checkKey[Int](ScopedKey(Scope(i), k), expected, applied)
|
||||
}
|
||||
|
||||
lazy val expectedValues = None :: None :: None :: None :: None :: None :: Some(3) :: None :: Some(3) :: Some(9) :: Some(4) :: Some(9) :: Nil
|
||||
lazy val expectedValues = None :: None :: None :: None :: None :: None :: Some(3) :: None :: Some(3) :: Some(9) :: Some(4) :: Some(9) :: Nil
|
||||
|
||||
lazy val ch = AttributeKey[Int]("ch")
|
||||
lazy val chk = ScopedKey( Scope(0), ch)
|
||||
def chain(i: Int, prev: Initialize[Int]): Initialize[Int] =
|
||||
if(i <= 0) prev else chain(i - 1, prev(_ + 1))
|
||||
lazy val ch = AttributeKey[Int]("ch")
|
||||
lazy val chk = ScopedKey(Scope(0), ch)
|
||||
def chain(i: Int, prev: Initialize[Int]): Initialize[Int] =
|
||||
if (i <= 0) prev else chain(i - 1, prev(_ + 1))
|
||||
|
||||
def chainBind(prev: Initialize[Int]): Initialize[Int] =
|
||||
bind(prev) { v =>
|
||||
if(v <= 0) prev else chainBind(value(v - 1) )
|
||||
}
|
||||
def singleIntTest(i: Initialize[Int], expected: Int) =
|
||||
{
|
||||
val eval = evaluate( setting( chk, i ) :: Nil )
|
||||
checkKey( chk, Some(expected), eval )
|
||||
}
|
||||
def chainBind(prev: Initialize[Int]): Initialize[Int] =
|
||||
bind(prev) { v =>
|
||||
if (v <= 0) prev else chainBind(value(v - 1))
|
||||
}
|
||||
def singleIntTest(i: Initialize[Int], expected: Int) =
|
||||
{
|
||||
val eval = evaluate(setting(chk, i) :: Nil)
|
||||
checkKey(chk, Some(expected), eval)
|
||||
}
|
||||
|
||||
def checkKey[T](key: ScopedKey[T], expected: Option[T], settings: Settings[Scope]) =
|
||||
{
|
||||
val value = settings.get( key.scope, key.key)
|
||||
("Key: " + key) |:
|
||||
("Value: " + value) |:
|
||||
("Expected: " + expected) |:
|
||||
(value == expected)
|
||||
}
|
||||
def checkKey[T](key: ScopedKey[T], expected: Option[T], settings: Settings[Scope]) =
|
||||
{
|
||||
val value = settings.get(key.scope, key.key)
|
||||
("Key: " + key) |:
|
||||
("Value: " + value) |:
|
||||
("Expected: " + expected) |:
|
||||
(value == expected)
|
||||
}
|
||||
|
||||
def evaluate(settings: Seq[Setting[_]]): Settings[Scope] =
|
||||
try { make(settings)(delegates, scopeLocal, showFullKey) }
|
||||
catch { case e: Throwable => e.printStackTrace; throw e }
|
||||
def evaluate(settings: Seq[Setting[_]]): Settings[Scope] =
|
||||
try { make(settings)(delegates, scopeLocal, showFullKey) }
|
||||
catch { case e: Throwable => e.printStackTrace; throw e }
|
||||
}
|
||||
// This setup is a workaround for module synchronization issues
|
||||
final class CCR(intermediate: Int)
|
||||
{
|
||||
lazy val top = iterate(value(intermediate), intermediate)
|
||||
def iterate(init: Initialize[Int], i: Int): Initialize[Int] =
|
||||
bind(init) { t =>
|
||||
if(t <= 0)
|
||||
top
|
||||
else
|
||||
iterate(value(t - 1), t-1)
|
||||
}
|
||||
final class CCR(intermediate: Int) {
|
||||
lazy val top = iterate(value(intermediate), intermediate)
|
||||
def iterate(init: Initialize[Int], i: Int): Initialize[Int] =
|
||||
bind(init) { t =>
|
||||
if (t <= 0)
|
||||
top
|
||||
else
|
||||
iterate(value(t - 1), t - 1)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,154 +1,148 @@
|
|||
package sbt.complete
|
||||
|
||||
object JLineTest
|
||||
{
|
||||
import DefaultParsers._
|
||||
object JLineTest {
|
||||
import DefaultParsers._
|
||||
|
||||
val one = "blue" | "green" | "black"
|
||||
val two = token("color" ~> Space) ~> token(one)
|
||||
val three = token("color" ~> Space) ~> token(ID.examples("blue", "green", "black"))
|
||||
val four = token("color" ~> Space) ~> token(ID, "<color name>")
|
||||
val one = "blue" | "green" | "black"
|
||||
val two = token("color" ~> Space) ~> token(one)
|
||||
val three = token("color" ~> Space) ~> token(ID.examples("blue", "green", "black"))
|
||||
val four = token("color" ~> Space) ~> token(ID, "<color name>")
|
||||
|
||||
val num = token(NatBasic)
|
||||
val five = (num ~ token("+" | "-") ~ num) <~ token('=') flatMap {
|
||||
case a ~ "+" ~ b => token((a+b).toString)
|
||||
case a ~ "-" ~ b => token((a-b).toString)
|
||||
}
|
||||
val num = token(NatBasic)
|
||||
val five = (num ~ token("+" | "-") ~ num) <~ token('=') flatMap {
|
||||
case a ~ "+" ~ b => token((a + b).toString)
|
||||
case a ~ "-" ~ b => token((a - b).toString)
|
||||
}
|
||||
|
||||
val parsers = Map("1" -> one, "2" -> two, "3" -> three, "4" -> four, "5" -> five)
|
||||
def main(args: Array[String])
|
||||
{
|
||||
import jline.TerminalFactory
|
||||
import jline.console.ConsoleReader
|
||||
val reader = new ConsoleReader()
|
||||
TerminalFactory.get.init
|
||||
val parsers = Map("1" -> one, "2" -> two, "3" -> three, "4" -> four, "5" -> five)
|
||||
def main(args: Array[String]) {
|
||||
import jline.TerminalFactory
|
||||
import jline.console.ConsoleReader
|
||||
val reader = new ConsoleReader()
|
||||
TerminalFactory.get.init
|
||||
|
||||
val parser = parsers(args(0))
|
||||
JLineCompletion.installCustomCompletor(reader, parser)
|
||||
def loop() {
|
||||
val line = reader.readLine("> ")
|
||||
if(line ne null) {
|
||||
println("Result: " + apply(parser)(line).resultEmpty)
|
||||
loop()
|
||||
}
|
||||
}
|
||||
loop()
|
||||
}
|
||||
val parser = parsers(args(0))
|
||||
JLineCompletion.installCustomCompletor(reader, parser)
|
||||
def loop() {
|
||||
val line = reader.readLine("> ")
|
||||
if (line ne null) {
|
||||
println("Result: " + apply(parser)(line).resultEmpty)
|
||||
loop()
|
||||
}
|
||||
}
|
||||
loop()
|
||||
}
|
||||
}
|
||||
|
||||
import Parser._
|
||||
import org.scalacheck._
|
||||
import Parser._
|
||||
import org.scalacheck._
|
||||
|
||||
object ParserTest extends Properties("Completing Parser")
|
||||
{
|
||||
import Parsers._
|
||||
import DefaultParsers.matches
|
||||
object ParserTest extends Properties("Completing Parser") {
|
||||
import Parsers._
|
||||
import DefaultParsers.matches
|
||||
|
||||
val nested = (token("a1") ~ token("b2")) ~ "c3"
|
||||
val nestedDisplay = (token("a1", "<a1>") ~ token("b2", "<b2>")) ~ "c3"
|
||||
val nested = (token("a1") ~ token("b2")) ~ "c3"
|
||||
val nestedDisplay = (token("a1", "<a1>") ~ token("b2", "<b2>")) ~ "c3"
|
||||
|
||||
val spacePort = (token(Space) ~> Port)
|
||||
val spacePort = (token(Space) ~> Port)
|
||||
|
||||
def p[T](f: T): T = { println(f); f }
|
||||
def p[T](f: T): T = { println(f); f }
|
||||
|
||||
def checkSingle(in: String, expect: Completion)(expectDisplay: Completion = expect) =
|
||||
( ("token '" + in + "'") |: checkOne(in, nested, expect)) &&
|
||||
( ("display '" + in + "'") |: checkOne(in, nestedDisplay, expectDisplay) )
|
||||
|
||||
def checkOne(in: String, parser: Parser[_], expect: Completion): Prop =
|
||||
completions(parser, in, 1) == Completions.single(expect)
|
||||
def checkSingle(in: String, expect: Completion)(expectDisplay: Completion = expect) =
|
||||
(("token '" + in + "'") |: checkOne(in, nested, expect)) &&
|
||||
(("display '" + in + "'") |: checkOne(in, nestedDisplay, expectDisplay))
|
||||
|
||||
def checkAll(in: String, parser: Parser[_], expect: Completions): Prop =
|
||||
{
|
||||
val cs = completions(parser, in, 1)
|
||||
("completions: " + cs) |: ("Expected: " + expect) |: ( (cs == expect): Prop)
|
||||
}
|
||||
|
||||
def checkInvalid(in: String) =
|
||||
( ("token '" + in + "'") |: checkInv(in, nested) ) &&
|
||||
( ("display '" + in + "'") |: checkInv(in, nestedDisplay) )
|
||||
def checkOne(in: String, parser: Parser[_], expect: Completion): Prop =
|
||||
completions(parser, in, 1) == Completions.single(expect)
|
||||
|
||||
def checkInv(in: String, parser: Parser[_]): Prop =
|
||||
{
|
||||
val cs = completions(parser, in, 1)
|
||||
("completions: " + cs) |: (( cs == Completions.nil): Prop)
|
||||
}
|
||||
|
||||
property("nested tokens a") = checkSingle("", Completion.tokenStrict("","a1") )( Completion.displayStrict("<a1>"))
|
||||
property("nested tokens a1") = checkSingle("a", Completion.tokenStrict("a","1") )( Completion.displayStrict("<a1>"))
|
||||
property("nested tokens a inv") = checkInvalid("b")
|
||||
property("nested tokens b") = checkSingle("a1", Completion.tokenStrict("","b2") )( Completion.displayStrict("<b2>"))
|
||||
property("nested tokens b2") = checkSingle("a1b", Completion.tokenStrict("b","2") )( Completion.displayStrict("<b2>"))
|
||||
property("nested tokens b inv") = checkInvalid("a1a")
|
||||
property("nested tokens c") = checkSingle("a1b2", Completion.suggestStrict("c3") )()
|
||||
property("nested tokens c3") = checkSingle("a1b2c", Completion.suggestStrict("3"))()
|
||||
property("nested tokens c inv") = checkInvalid("a1b2a")
|
||||
def checkAll(in: String, parser: Parser[_], expect: Completions): Prop =
|
||||
{
|
||||
val cs = completions(parser, in, 1)
|
||||
("completions: " + cs) |: ("Expected: " + expect) |: ((cs == expect): Prop)
|
||||
}
|
||||
|
||||
property("suggest space") = checkOne("", spacePort, Completion.tokenStrict("", " "))
|
||||
property("suggest port") = checkOne(" ", spacePort, Completion.displayStrict("<port>") )
|
||||
property("no suggest at end") = checkOne("asdf", "asdf", Completion.suggestStrict(""))
|
||||
property("no suggest at token end") = checkOne("asdf", token("asdf"), Completion.suggestStrict(""))
|
||||
property("empty suggest for examples") = checkOne("asdf", any.+.examples("asdf", "qwer"), Completion.suggestStrict(""))
|
||||
property("empty suggest for examples token") = checkOne("asdf", token(any.+.examples("asdf", "qwer")), Completion.suggestStrict(""))
|
||||
def checkInvalid(in: String) =
|
||||
(("token '" + in + "'") |: checkInv(in, nested)) &&
|
||||
(("display '" + in + "'") |: checkInv(in, nestedDisplay))
|
||||
|
||||
val colors = Set("blue", "green", "red")
|
||||
val base = (seen: Seq[String]) => token( ID examples (colors -- seen) )
|
||||
val sep = token( Space )
|
||||
val repeat = repeatDep( base, sep)
|
||||
def completionStrings(ss: Set[String]): Completions = Completions(ss.map { s => Completion.tokenStrict("", s) })
|
||||
def checkInv(in: String, parser: Parser[_]): Prop =
|
||||
{
|
||||
val cs = completions(parser, in, 1)
|
||||
("completions: " + cs) |: ((cs == Completions.nil): Prop)
|
||||
}
|
||||
|
||||
property("repeatDep no suggestions for bad input") = checkInv(".", repeat)
|
||||
property("repeatDep suggest all") = checkAll("", repeat, completionStrings(colors))
|
||||
property("repeatDep suggest remaining two") = {
|
||||
val first = colors.toSeq.head
|
||||
checkAll(first + " ", repeat, completionStrings(colors - first))
|
||||
}
|
||||
property("repeatDep suggest remaining one") = {
|
||||
val take = colors.toSeq.take(2)
|
||||
checkAll(take.mkString("", " ", " "), repeat, completionStrings(colors -- take))
|
||||
}
|
||||
property("repeatDep requires at least one token") = !matches(repeat, "")
|
||||
property("repeatDep accepts one token") = matches(repeat, colors.toSeq.head)
|
||||
property("repeatDep accepts two tokens") = matches(repeat, colors.toSeq.take(2).mkString(" "))
|
||||
property("nested tokens a") = checkSingle("", Completion.tokenStrict("", "a1"))(Completion.displayStrict("<a1>"))
|
||||
property("nested tokens a1") = checkSingle("a", Completion.tokenStrict("a", "1"))(Completion.displayStrict("<a1>"))
|
||||
property("nested tokens a inv") = checkInvalid("b")
|
||||
property("nested tokens b") = checkSingle("a1", Completion.tokenStrict("", "b2"))(Completion.displayStrict("<b2>"))
|
||||
property("nested tokens b2") = checkSingle("a1b", Completion.tokenStrict("b", "2"))(Completion.displayStrict("<b2>"))
|
||||
property("nested tokens b inv") = checkInvalid("a1a")
|
||||
property("nested tokens c") = checkSingle("a1b2", Completion.suggestStrict("c3"))()
|
||||
property("nested tokens c3") = checkSingle("a1b2c", Completion.suggestStrict("3"))()
|
||||
property("nested tokens c inv") = checkInvalid("a1b2a")
|
||||
|
||||
property("suggest space") = checkOne("", spacePort, Completion.tokenStrict("", " "))
|
||||
property("suggest port") = checkOne(" ", spacePort, Completion.displayStrict("<port>"))
|
||||
property("no suggest at end") = checkOne("asdf", "asdf", Completion.suggestStrict(""))
|
||||
property("no suggest at token end") = checkOne("asdf", token("asdf"), Completion.suggestStrict(""))
|
||||
property("empty suggest for examples") = checkOne("asdf", any.+.examples("asdf", "qwer"), Completion.suggestStrict(""))
|
||||
property("empty suggest for examples token") = checkOne("asdf", token(any.+.examples("asdf", "qwer")), Completion.suggestStrict(""))
|
||||
|
||||
val colors = Set("blue", "green", "red")
|
||||
val base = (seen: Seq[String]) => token(ID examples (colors -- seen))
|
||||
val sep = token(Space)
|
||||
val repeat = repeatDep(base, sep)
|
||||
def completionStrings(ss: Set[String]): Completions = Completions(ss.map { s => Completion.tokenStrict("", s) })
|
||||
|
||||
property("repeatDep no suggestions for bad input") = checkInv(".", repeat)
|
||||
property("repeatDep suggest all") = checkAll("", repeat, completionStrings(colors))
|
||||
property("repeatDep suggest remaining two") = {
|
||||
val first = colors.toSeq.head
|
||||
checkAll(first + " ", repeat, completionStrings(colors - first))
|
||||
}
|
||||
property("repeatDep suggest remaining one") = {
|
||||
val take = colors.toSeq.take(2)
|
||||
checkAll(take.mkString("", " ", " "), repeat, completionStrings(colors -- take))
|
||||
}
|
||||
property("repeatDep requires at least one token") = !matches(repeat, "")
|
||||
property("repeatDep accepts one token") = matches(repeat, colors.toSeq.head)
|
||||
property("repeatDep accepts two tokens") = matches(repeat, colors.toSeq.take(2).mkString(" "))
|
||||
}
|
||||
object ParserExample
|
||||
{
|
||||
val ws = charClass(_.isWhitespace)+
|
||||
val notws = charClass(!_.isWhitespace)+
|
||||
object ParserExample {
|
||||
val ws = charClass(_.isWhitespace)+
|
||||
val notws = charClass(!_.isWhitespace)+
|
||||
|
||||
val name = token("test")
|
||||
val options = (ws ~> token("quick" | "failed" | "new") )*
|
||||
val exampleSet = Set("am", "is", "are", "was", "were")
|
||||
val include = (ws ~> token(examples(notws.string, new FixedSetExamples(exampleSet), exampleSet.size, false )) )*
|
||||
val name = token("test")
|
||||
val options = (ws ~> token("quick" | "failed" | "new"))*
|
||||
val exampleSet = Set("am", "is", "are", "was", "were")
|
||||
val include = (ws ~> token(examples(notws.string, new FixedSetExamples(exampleSet), exampleSet.size, false)))*
|
||||
|
||||
val t = name ~ options ~ include
|
||||
val t = name ~ options ~ include
|
||||
|
||||
// Get completions for some different inputs
|
||||
println(completions(t, "te", 1))
|
||||
println(completions(t, "test ",1))
|
||||
println(completions(t, "test w", 1))
|
||||
// Get completions for some different inputs
|
||||
println(completions(t, "te", 1))
|
||||
println(completions(t, "test ", 1))
|
||||
println(completions(t, "test w", 1))
|
||||
|
||||
// Get the parsed result for different inputs
|
||||
println(apply(t)("te").resultEmpty)
|
||||
println(apply(t)("test").resultEmpty)
|
||||
println(apply(t)("test w").resultEmpty)
|
||||
println(apply(t)("test was were").resultEmpty)
|
||||
// Get the parsed result for different inputs
|
||||
println(apply(t)("te").resultEmpty)
|
||||
println(apply(t)("test").resultEmpty)
|
||||
println(apply(t)("test w").resultEmpty)
|
||||
println(apply(t)("test was were").resultEmpty)
|
||||
|
||||
def run(n: Int)
|
||||
{
|
||||
val a = 'a'.id
|
||||
val aq = a.?
|
||||
val aqn = repeat(aq, min = n, max = n)
|
||||
val an = repeat(a, min = n, max = n)
|
||||
val ann = aqn ~ an
|
||||
def run(n: Int) {
|
||||
val a = 'a'.id
|
||||
val aq = a.?
|
||||
val aqn = repeat(aq, min = n, max = n)
|
||||
val an = repeat(a, min = n, max = n)
|
||||
val ann = aqn ~ an
|
||||
|
||||
def r = apply(ann)("a"*(n*2)).resultEmpty
|
||||
println(r.isValid)
|
||||
}
|
||||
def run2(n: Int)
|
||||
{
|
||||
val ab = "ab".?.*
|
||||
val r = apply(ab)("a"*n).resultEmpty
|
||||
println(r)
|
||||
}
|
||||
def r = apply(ann)("a" * (n * 2)).resultEmpty
|
||||
println(r.isValid)
|
||||
}
|
||||
def run2(n: Int) {
|
||||
val ab = "ab".?.*
|
||||
val r = apply(ab)("a" * n).resultEmpty
|
||||
println(r)
|
||||
}
|
||||
}
|
||||
|
|
@ -6,87 +6,85 @@ import sbt.IO.withTemporaryDirectory
|
|||
import java.io.File
|
||||
import sbt.IO._
|
||||
|
||||
class FileExamplesTest extends Specification
|
||||
{
|
||||
class FileExamplesTest extends Specification {
|
||||
|
||||
"listing all files in an absolute base directory" should {
|
||||
"produce the entire base directory's contents" in new directoryStructure {
|
||||
fileExamples().toList should containTheSameElementsAs(allRelativizedPaths)
|
||||
}
|
||||
}
|
||||
"listing all files in an absolute base directory" should {
|
||||
"produce the entire base directory's contents" in new directoryStructure {
|
||||
fileExamples().toList should containTheSameElementsAs(allRelativizedPaths)
|
||||
}
|
||||
}
|
||||
|
||||
"listing files with a prefix that matches none" should {
|
||||
"produce an empty list" in new directoryStructure(withCompletionPrefix = "z") {
|
||||
fileExamples().toList should beEmpty
|
||||
}
|
||||
}
|
||||
"listing files with a prefix that matches none" should {
|
||||
"produce an empty list" in new directoryStructure(withCompletionPrefix = "z") {
|
||||
fileExamples().toList should beEmpty
|
||||
}
|
||||
}
|
||||
|
||||
"listing single-character prefixed files" should {
|
||||
"produce matching paths only" in new directoryStructure(withCompletionPrefix = "f") {
|
||||
fileExamples().toList should containTheSameElementsAs(prefixedPathsOnly)
|
||||
}
|
||||
}
|
||||
"listing single-character prefixed files" should {
|
||||
"produce matching paths only" in new directoryStructure(withCompletionPrefix = "f") {
|
||||
fileExamples().toList should containTheSameElementsAs(prefixedPathsOnly)
|
||||
}
|
||||
}
|
||||
|
||||
"listing directory-prefixed files" should {
|
||||
"produce matching paths only" in new directoryStructure(withCompletionPrefix = "far") {
|
||||
fileExamples().toList should containTheSameElementsAs(prefixedPathsOnly)
|
||||
}
|
||||
"listing directory-prefixed files" should {
|
||||
"produce matching paths only" in new directoryStructure(withCompletionPrefix = "far") {
|
||||
fileExamples().toList should containTheSameElementsAs(prefixedPathsOnly)
|
||||
}
|
||||
|
||||
"produce sub-dir contents only when appending a file separator to the directory" in new directoryStructure(withCompletionPrefix = "far" + File.separator) {
|
||||
fileExamples().toList should containTheSameElementsAs(prefixedPathsOnly)
|
||||
}
|
||||
}
|
||||
"produce sub-dir contents only when appending a file separator to the directory" in new directoryStructure(withCompletionPrefix = "far" + File.separator) {
|
||||
fileExamples().toList should containTheSameElementsAs(prefixedPathsOnly)
|
||||
}
|
||||
}
|
||||
|
||||
"listing files with a sub-path prefix" should {
|
||||
"produce matching paths only" in new directoryStructure(withCompletionPrefix = "far" + File.separator + "ba") {
|
||||
fileExamples().toList should containTheSameElementsAs(prefixedPathsOnly)
|
||||
}
|
||||
}
|
||||
"listing files with a sub-path prefix" should {
|
||||
"produce matching paths only" in new directoryStructure(withCompletionPrefix = "far" + File.separator + "ba") {
|
||||
fileExamples().toList should containTheSameElementsAs(prefixedPathsOnly)
|
||||
}
|
||||
}
|
||||
|
||||
"completing a full path" should {
|
||||
"produce a list with an empty string" in new directoryStructure(withCompletionPrefix = "bazaar") {
|
||||
fileExamples().toList shouldEqual List("")
|
||||
}
|
||||
}
|
||||
"completing a full path" should {
|
||||
"produce a list with an empty string" in new directoryStructure(withCompletionPrefix = "bazaar") {
|
||||
fileExamples().toList shouldEqual List("")
|
||||
}
|
||||
}
|
||||
|
||||
class directoryStructure(withCompletionPrefix: String = "") extends Scope with DelayedInit
|
||||
{
|
||||
var fileExamples: FileExamples = _
|
||||
var baseDir: File = _
|
||||
var childFiles: List[File] = _
|
||||
var childDirectories: List[File] = _
|
||||
var nestedFiles: List[File] = _
|
||||
var nestedDirectories: List[File] = _
|
||||
class directoryStructure(withCompletionPrefix: String = "") extends Scope with DelayedInit {
|
||||
var fileExamples: FileExamples = _
|
||||
var baseDir: File = _
|
||||
var childFiles: List[File] = _
|
||||
var childDirectories: List[File] = _
|
||||
var nestedFiles: List[File] = _
|
||||
var nestedDirectories: List[File] = _
|
||||
|
||||
def allRelativizedPaths: List[String] =
|
||||
(childFiles ++ childDirectories ++ nestedFiles ++ nestedDirectories).map(relativize(baseDir, _).get)
|
||||
def allRelativizedPaths: List[String] =
|
||||
(childFiles ++ childDirectories ++ nestedFiles ++ nestedDirectories).map(relativize(baseDir, _).get)
|
||||
|
||||
def prefixedPathsOnly: List[String] =
|
||||
allRelativizedPaths.filter(_ startsWith withCompletionPrefix).map(_ substring withCompletionPrefix.length)
|
||||
def prefixedPathsOnly: List[String] =
|
||||
allRelativizedPaths.filter(_ startsWith withCompletionPrefix).map(_ substring withCompletionPrefix.length)
|
||||
|
||||
override def delayedInit(testBody: => Unit): Unit = {
|
||||
withTemporaryDirectory {
|
||||
tempDir =>
|
||||
createSampleDirStructure(tempDir)
|
||||
fileExamples = new FileExamples(baseDir, withCompletionPrefix)
|
||||
testBody
|
||||
}
|
||||
}
|
||||
override def delayedInit(testBody: => Unit): Unit = {
|
||||
withTemporaryDirectory {
|
||||
tempDir =>
|
||||
createSampleDirStructure(tempDir)
|
||||
fileExamples = new FileExamples(baseDir, withCompletionPrefix)
|
||||
testBody
|
||||
}
|
||||
}
|
||||
|
||||
private def createSampleDirStructure(tempDir: File): Unit = {
|
||||
childFiles = toChildFiles(tempDir, List("foo", "bar", "bazaar"))
|
||||
childDirectories = toChildFiles(tempDir, List("moo", "far"))
|
||||
nestedFiles = toChildFiles(childDirectories(1), List("farfile1", "barfile2"))
|
||||
nestedDirectories = toChildFiles(childDirectories(1), List("fardir1", "bardir2"))
|
||||
private def createSampleDirStructure(tempDir: File): Unit = {
|
||||
childFiles = toChildFiles(tempDir, List("foo", "bar", "bazaar"))
|
||||
childDirectories = toChildFiles(tempDir, List("moo", "far"))
|
||||
nestedFiles = toChildFiles(childDirectories(1), List("farfile1", "barfile2"))
|
||||
nestedDirectories = toChildFiles(childDirectories(1), List("fardir1", "bardir2"))
|
||||
|
||||
(childDirectories ++ nestedDirectories).map(_.mkdirs())
|
||||
(childFiles ++ nestedFiles).map(_.createNewFile())
|
||||
(childDirectories ++ nestedDirectories).map(_.mkdirs())
|
||||
(childFiles ++ nestedFiles).map(_.createNewFile())
|
||||
|
||||
// NOTE: Creating a new file here because `tempDir.listFiles()` returned an empty list.
|
||||
baseDir = new File(tempDir.getCanonicalPath)
|
||||
}
|
||||
// NOTE: Creating a new file here because `tempDir.listFiles()` returned an empty list.
|
||||
baseDir = new File(tempDir.getCanonicalPath)
|
||||
}
|
||||
|
||||
private def toChildFiles(baseDir: File, files: List[String]): List[File] = files.map(new File(baseDir, _))
|
||||
}
|
||||
private def toChildFiles(baseDir: File, files: List[String]): List[File] = files.map(new File(baseDir, _))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,22 +5,22 @@ import org.specs2.specification.Scope
|
|||
|
||||
class FixedSetExamplesTest extends Specification {
|
||||
|
||||
"adding a prefix" should {
|
||||
"produce a smaller set of examples with the prefix removed" in new examples {
|
||||
fixedSetExamples.withAddedPrefix("f")() must containTheSameElementsAs(List("oo", "ool", "u"))
|
||||
fixedSetExamples.withAddedPrefix("fo")() must containTheSameElementsAs(List("o", "ol"))
|
||||
fixedSetExamples.withAddedPrefix("b")() must containTheSameElementsAs(List("ar"))
|
||||
}
|
||||
}
|
||||
"adding a prefix" should {
|
||||
"produce a smaller set of examples with the prefix removed" in new examples {
|
||||
fixedSetExamples.withAddedPrefix("f")() must containTheSameElementsAs(List("oo", "ool", "u"))
|
||||
fixedSetExamples.withAddedPrefix("fo")() must containTheSameElementsAs(List("o", "ol"))
|
||||
fixedSetExamples.withAddedPrefix("b")() must containTheSameElementsAs(List("ar"))
|
||||
}
|
||||
}
|
||||
|
||||
"without a prefix" should {
|
||||
"produce the original set" in new examples {
|
||||
fixedSetExamples() mustEqual exampleSet
|
||||
}
|
||||
}
|
||||
"without a prefix" should {
|
||||
"produce the original set" in new examples {
|
||||
fixedSetExamples() mustEqual exampleSet
|
||||
}
|
||||
}
|
||||
|
||||
trait examples extends Scope {
|
||||
val exampleSet = List("foo", "bar", "fool", "fu")
|
||||
val fixedSetExamples = FixedSetExamples(exampleSet)
|
||||
}
|
||||
trait examples extends Scope {
|
||||
val exampleSet = List("foo", "bar", "fool", "fu")
|
||||
val fixedSetExamples = FixedSetExamples(exampleSet)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,88 +6,88 @@ import Completion._
|
|||
|
||||
class ParserWithExamplesTest extends Specification {
|
||||
|
||||
"listing a limited number of completions" should {
|
||||
"grab only the needed number of elements from the iterable source of examples" in new parserWithLazyExamples {
|
||||
parserWithExamples.completions(0)
|
||||
examples.size shouldEqual maxNumberOfExamples
|
||||
}
|
||||
}
|
||||
"listing a limited number of completions" should {
|
||||
"grab only the needed number of elements from the iterable source of examples" in new parserWithLazyExamples {
|
||||
parserWithExamples.completions(0)
|
||||
examples.size shouldEqual maxNumberOfExamples
|
||||
}
|
||||
}
|
||||
|
||||
"listing only valid completions" should {
|
||||
"use the delegate parser to remove invalid examples" in new parserWithValidExamples {
|
||||
val validCompletions = Completions(Set(
|
||||
suggestion("blue"),
|
||||
suggestion("red")
|
||||
))
|
||||
parserWithExamples.completions(0) shouldEqual validCompletions
|
||||
}
|
||||
}
|
||||
"listing only valid completions" should {
|
||||
"use the delegate parser to remove invalid examples" in new parserWithValidExamples {
|
||||
val validCompletions = Completions(Set(
|
||||
suggestion("blue"),
|
||||
suggestion("red")
|
||||
))
|
||||
parserWithExamples.completions(0) shouldEqual validCompletions
|
||||
}
|
||||
}
|
||||
|
||||
"listing valid completions in a derived parser" should {
|
||||
"produce only valid examples that start with the character of the derivation" in new parserWithValidExamples {
|
||||
val derivedCompletions = Completions(Set(
|
||||
suggestion("lue")
|
||||
))
|
||||
parserWithExamples.derive('b').completions(0) shouldEqual derivedCompletions
|
||||
}
|
||||
}
|
||||
"listing valid completions in a derived parser" should {
|
||||
"produce only valid examples that start with the character of the derivation" in new parserWithValidExamples {
|
||||
val derivedCompletions = Completions(Set(
|
||||
suggestion("lue")
|
||||
))
|
||||
parserWithExamples.derive('b').completions(0) shouldEqual derivedCompletions
|
||||
}
|
||||
}
|
||||
|
||||
"listing valid and invalid completions" should {
|
||||
"produce the entire source of examples" in new parserWithAllExamples {
|
||||
val completions = Completions(examples.map(suggestion(_)).toSet)
|
||||
parserWithExamples.completions(0) shouldEqual completions
|
||||
}
|
||||
}
|
||||
"listing valid and invalid completions" should {
|
||||
"produce the entire source of examples" in new parserWithAllExamples {
|
||||
val completions = Completions(examples.map(suggestion(_)).toSet)
|
||||
parserWithExamples.completions(0) shouldEqual completions
|
||||
}
|
||||
}
|
||||
|
||||
"listing valid and invalid completions in a derived parser" should {
|
||||
"produce only examples that start with the character of the derivation" in new parserWithAllExamples {
|
||||
val derivedCompletions = Completions(Set(
|
||||
suggestion("lue"),
|
||||
suggestion("lock")
|
||||
))
|
||||
parserWithExamples.derive('b').completions(0) shouldEqual derivedCompletions
|
||||
}
|
||||
}
|
||||
"listing valid and invalid completions in a derived parser" should {
|
||||
"produce only examples that start with the character of the derivation" in new parserWithAllExamples {
|
||||
val derivedCompletions = Completions(Set(
|
||||
suggestion("lue"),
|
||||
suggestion("lock")
|
||||
))
|
||||
parserWithExamples.derive('b').completions(0) shouldEqual derivedCompletions
|
||||
}
|
||||
}
|
||||
|
||||
class parserWithLazyExamples extends parser(GrowableSourceOfExamples(), maxNumberOfExamples = 5, removeInvalidExamples = false)
|
||||
class parserWithLazyExamples extends parser(GrowableSourceOfExamples(), maxNumberOfExamples = 5, removeInvalidExamples = false)
|
||||
|
||||
class parserWithValidExamples extends parser(removeInvalidExamples = true)
|
||||
class parserWithValidExamples extends parser(removeInvalidExamples = true)
|
||||
|
||||
class parserWithAllExamples extends parser(removeInvalidExamples = false)
|
||||
class parserWithAllExamples extends parser(removeInvalidExamples = false)
|
||||
|
||||
case class parser(examples: Iterable[String] = Set("blue", "yellow", "greeen", "block", "red"),
|
||||
maxNumberOfExamples: Int = 25,
|
||||
removeInvalidExamples: Boolean) extends Scope {
|
||||
case class parser(examples: Iterable[String] = Set("blue", "yellow", "greeen", "block", "red"),
|
||||
maxNumberOfExamples: Int = 25,
|
||||
removeInvalidExamples: Boolean) extends Scope {
|
||||
|
||||
import DefaultParsers._
|
||||
import DefaultParsers._
|
||||
|
||||
val colorParser = "blue" | "green" | "black" | "red"
|
||||
val parserWithExamples: Parser[String] = new ParserWithExamples[String](
|
||||
colorParser,
|
||||
FixedSetExamples(examples),
|
||||
maxNumberOfExamples,
|
||||
removeInvalidExamples
|
||||
)
|
||||
}
|
||||
val colorParser = "blue" | "green" | "black" | "red"
|
||||
val parserWithExamples: Parser[String] = new ParserWithExamples[String](
|
||||
colorParser,
|
||||
FixedSetExamples(examples),
|
||||
maxNumberOfExamples,
|
||||
removeInvalidExamples
|
||||
)
|
||||
}
|
||||
|
||||
case class GrowableSourceOfExamples() extends Iterable[String] {
|
||||
private var numberOfIteratedElements: Int = 0
|
||||
case class GrowableSourceOfExamples() extends Iterable[String] {
|
||||
private var numberOfIteratedElements: Int = 0
|
||||
|
||||
override def iterator: Iterator[String] = {
|
||||
new Iterator[String] {
|
||||
var currentElement = 0
|
||||
override def iterator: Iterator[String] = {
|
||||
new Iterator[String] {
|
||||
var currentElement = 0
|
||||
|
||||
override def next(): String = {
|
||||
currentElement += 1
|
||||
numberOfIteratedElements = Math.max(currentElement, numberOfIteratedElements)
|
||||
numberOfIteratedElements.toString
|
||||
}
|
||||
override def next(): String = {
|
||||
currentElement += 1
|
||||
numberOfIteratedElements = Math.max(currentElement, numberOfIteratedElements)
|
||||
numberOfIteratedElements.toString
|
||||
}
|
||||
|
||||
override def hasNext: Boolean = true
|
||||
}
|
||||
}
|
||||
override def hasNext: Boolean = true
|
||||
}
|
||||
}
|
||||
|
||||
override def size: Int = numberOfIteratedElements
|
||||
}
|
||||
override def size: Int = numberOfIteratedElements
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,90 +2,85 @@ package sbt
|
|||
|
||||
import org.scalacheck._
|
||||
import Prop._
|
||||
import Gen.{listOf, oneOf}
|
||||
import Gen.{ listOf, oneOf }
|
||||
|
||||
import ConsoleLogger.{ESC, hasEscapeSequence, isEscapeTerminator, removeEscapeSequences}
|
||||
import ConsoleLogger.{ ESC, hasEscapeSequence, isEscapeTerminator, removeEscapeSequences }
|
||||
|
||||
object Escapes extends Properties("Escapes")
|
||||
{
|
||||
property("genTerminator only generates terminators") =
|
||||
forAllNoShrink(genTerminator) { (c: Char) => isEscapeTerminator(c) }
|
||||
object Escapes extends Properties("Escapes") {
|
||||
property("genTerminator only generates terminators") =
|
||||
forAllNoShrink(genTerminator) { (c: Char) => isEscapeTerminator(c) }
|
||||
|
||||
property("genWithoutTerminator only generates terminators") =
|
||||
forAllNoShrink(genWithoutTerminator) { (s: String) =>
|
||||
s.forall { c => !isEscapeTerminator(c) }
|
||||
}
|
||||
property("genWithoutTerminator only generates terminators") =
|
||||
forAllNoShrink(genWithoutTerminator) { (s: String) =>
|
||||
s.forall { c => !isEscapeTerminator(c) }
|
||||
}
|
||||
|
||||
property("hasEscapeSequence is false when no escape character is present") = forAllNoShrink(genWithoutEscape) { (s: String) =>
|
||||
!hasEscapeSequence(s)
|
||||
}
|
||||
property("hasEscapeSequence is false when no escape character is present") = forAllNoShrink(genWithoutEscape) { (s: String) =>
|
||||
!hasEscapeSequence(s)
|
||||
}
|
||||
|
||||
property("hasEscapeSequence is true when escape character is present") = forAllNoShrink(genWithRandomEscapes) { (s: String) =>
|
||||
hasEscapeSequence(s)
|
||||
}
|
||||
property("hasEscapeSequence is true when escape character is present") = forAllNoShrink(genWithRandomEscapes) { (s: String) =>
|
||||
hasEscapeSequence(s)
|
||||
}
|
||||
|
||||
property("removeEscapeSequences is the identity when no escape character is present") = forAllNoShrink(genWithoutEscape) { (s: String) =>
|
||||
val removed: String = removeEscapeSequences(s)
|
||||
("Escape sequence removed: '" + removed + "'") |:
|
||||
(removed == s)
|
||||
}
|
||||
property("removeEscapeSequences is the identity when no escape character is present") = forAllNoShrink(genWithoutEscape) { (s: String) =>
|
||||
val removed: String = removeEscapeSequences(s)
|
||||
("Escape sequence removed: '" + removed + "'") |:
|
||||
(removed == s)
|
||||
}
|
||||
|
||||
property("No escape characters remain after removeEscapeSequences") = forAll { (s: String) =>
|
||||
val removed: String = removeEscapeSequences(s)
|
||||
("Escape sequence removed: '" + removed + "'") |:
|
||||
!hasEscapeSequence(removed)
|
||||
}
|
||||
property("No escape characters remain after removeEscapeSequences") = forAll { (s: String) =>
|
||||
val removed: String = removeEscapeSequences(s)
|
||||
("Escape sequence removed: '" + removed + "'") |:
|
||||
!hasEscapeSequence(removed)
|
||||
}
|
||||
|
||||
property("removeEscapeSequences returns string without escape sequences") =
|
||||
forAllNoShrink( genWithoutEscape, genEscapePairs ) { (start: String, escapes: List[EscapeAndNot]) =>
|
||||
val withEscapes: String = start + escapes.map { ean => ean.escape.makeString + ean.notEscape }
|
||||
val removed: String = removeEscapeSequences(withEscapes)
|
||||
val original = start + escapes.map(_.notEscape)
|
||||
("Input string with escapes: '" + withEscapes + "'") |:
|
||||
("Escapes removed '" + removed + "'") |:
|
||||
(original == removed)
|
||||
}
|
||||
property("removeEscapeSequences returns string without escape sequences") =
|
||||
forAllNoShrink(genWithoutEscape, genEscapePairs) { (start: String, escapes: List[EscapeAndNot]) =>
|
||||
val withEscapes: String = start + escapes.map { ean => ean.escape.makeString + ean.notEscape }
|
||||
val removed: String = removeEscapeSequences(withEscapes)
|
||||
val original = start + escapes.map(_.notEscape)
|
||||
("Input string with escapes: '" + withEscapes + "'") |:
|
||||
("Escapes removed '" + removed + "'") |:
|
||||
(original == removed)
|
||||
}
|
||||
|
||||
final case class EscapeAndNot(escape: EscapeSequence, notEscape: String)
|
||||
final case class EscapeSequence(content: String, terminator: Char)
|
||||
{
|
||||
assert( content.forall(c => !isEscapeTerminator(c) ), "Escape sequence content contains an escape terminator: '" + content + "'" )
|
||||
assert( isEscapeTerminator(terminator) )
|
||||
def makeString: String = ESC + content + terminator
|
||||
}
|
||||
private[this] def noEscape(s: String): String = s.replace(ESC, ' ')
|
||||
final case class EscapeAndNot(escape: EscapeSequence, notEscape: String)
|
||||
final case class EscapeSequence(content: String, terminator: Char) {
|
||||
assert(content.forall(c => !isEscapeTerminator(c)), "Escape sequence content contains an escape terminator: '" + content + "'")
|
||||
assert(isEscapeTerminator(terminator))
|
||||
def makeString: String = ESC + content + terminator
|
||||
}
|
||||
private[this] def noEscape(s: String): String = s.replace(ESC, ' ')
|
||||
|
||||
lazy val genEscapeSequence: Gen[EscapeSequence] = oneOf(genKnownSequence, genArbitraryEscapeSequence)
|
||||
lazy val genEscapePair: Gen[EscapeAndNot] = for(esc <- genEscapeSequence; not <- genWithoutEscape) yield EscapeAndNot(esc, not)
|
||||
lazy val genEscapePairs: Gen[List[EscapeAndNot]] = listOf(genEscapePair)
|
||||
lazy val genEscapeSequence: Gen[EscapeSequence] = oneOf(genKnownSequence, genArbitraryEscapeSequence)
|
||||
lazy val genEscapePair: Gen[EscapeAndNot] = for (esc <- genEscapeSequence; not <- genWithoutEscape) yield EscapeAndNot(esc, not)
|
||||
lazy val genEscapePairs: Gen[List[EscapeAndNot]] = listOf(genEscapePair)
|
||||
|
||||
lazy val genArbitraryEscapeSequence: Gen[EscapeSequence] =
|
||||
for(content <- genWithoutTerminator; term <- genTerminator) yield
|
||||
new EscapeSequence(content, term)
|
||||
|
||||
lazy val genKnownSequence: Gen[EscapeSequence] =
|
||||
oneOf((misc ++ setGraphicsMode ++ setMode ++ resetMode).map(toEscapeSequence))
|
||||
|
||||
def toEscapeSequence(s: String): EscapeSequence = EscapeSequence(s.init, s.last)
|
||||
lazy val genArbitraryEscapeSequence: Gen[EscapeSequence] =
|
||||
for (content <- genWithoutTerminator; term <- genTerminator) yield new EscapeSequence(content, term)
|
||||
|
||||
lazy val misc = Seq("14;23H", "5;3f", "2A", "94B", "19C", "85D", "s", "u", "2J", "K")
|
||||
lazy val genKnownSequence: Gen[EscapeSequence] =
|
||||
oneOf((misc ++ setGraphicsMode ++ setMode ++ resetMode).map(toEscapeSequence))
|
||||
|
||||
lazy val setGraphicsMode: Seq[String] =
|
||||
for(txt <- 0 to 8; fg <- 30 to 37; bg <- 40 to 47) yield
|
||||
txt.toString + ";" + fg.toString + ";" + bg.toString + "m"
|
||||
def toEscapeSequence(s: String): EscapeSequence = EscapeSequence(s.init, s.last)
|
||||
|
||||
lazy val resetMode = setModeLike('I')
|
||||
lazy val setMode = setModeLike('h')
|
||||
def setModeLike(term: Char): Seq[String] = (0 to 19).map(i => "=" + i.toString + term)
|
||||
|
||||
lazy val genWithoutTerminator = genRawString.map( _.filter { c => !isEscapeTerminator(c) } )
|
||||
lazy val misc = Seq("14;23H", "5;3f", "2A", "94B", "19C", "85D", "s", "u", "2J", "K")
|
||||
|
||||
lazy val genTerminator: Gen[Char] = Gen.choose('@', '~')
|
||||
lazy val genWithoutEscape: Gen[String] = genRawString.map(noEscape)
|
||||
lazy val setGraphicsMode: Seq[String] =
|
||||
for (txt <- 0 to 8; fg <- 30 to 37; bg <- 40 to 47) yield txt.toString + ";" + fg.toString + ";" + bg.toString + "m"
|
||||
|
||||
def genWithRandomEscapes: Gen[String] =
|
||||
for(ls <- listOf(genRawString); end <- genRawString) yield
|
||||
ls.mkString("", ESC.toString, ESC.toString + end)
|
||||
lazy val resetMode = setModeLike('I')
|
||||
lazy val setMode = setModeLike('h')
|
||||
def setModeLike(term: Char): Seq[String] = (0 to 19).map(i => "=" + i.toString + term)
|
||||
|
||||
private def genRawString = Arbitrary.arbString.arbitrary
|
||||
lazy val genWithoutTerminator = genRawString.map(_.filter { c => !isEscapeTerminator(c) })
|
||||
|
||||
lazy val genTerminator: Gen[Char] = Gen.choose('@', '~')
|
||||
lazy val genWithoutEscape: Gen[String] = genRawString.map(noEscape)
|
||||
|
||||
def genWithRandomEscapes: Gen[String] =
|
||||
for (ls <- listOf(genRawString); end <- genRawString) yield ls.mkString("", ESC.toString, ESC.toString + end)
|
||||
|
||||
private def genRawString = Arbitrary.arbString.arbitrary
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,157 +4,147 @@
|
|||
package sbt
|
||||
|
||||
import org.scalacheck._
|
||||
import Arbitrary.{arbitrary => arb, _}
|
||||
import Gen.{listOfN, oneOf}
|
||||
import Arbitrary.{ arbitrary => arb, _ }
|
||||
import Gen.{ listOfN, oneOf }
|
||||
import Prop._
|
||||
|
||||
import java.io.Writer
|
||||
|
||||
object LogWriterTest extends Properties("Log Writer")
|
||||
{
|
||||
final val MaxLines = 100
|
||||
final val MaxSegments = 10
|
||||
object LogWriterTest extends Properties("Log Writer") {
|
||||
final val MaxLines = 100
|
||||
final val MaxSegments = 10
|
||||
|
||||
/* Tests that content written through a LoggerWriter is properly passed to the underlying Logger.
|
||||
/* Tests that content written through a LoggerWriter is properly passed to the underlying Logger.
|
||||
* Each line, determined by the specified newline separator, must be logged at the correct logging level. */
|
||||
property("properly logged") = forAll { (output: Output, newLine: NewLine) =>
|
||||
import output.{lines, level}
|
||||
val log = new RecordingLogger
|
||||
val writer = new LoggerWriter(log, Some(level), newLine.str)
|
||||
logLines(writer, lines, newLine.str)
|
||||
val events = log.getEvents
|
||||
("Recorded:\n" + events.map(show).mkString("\n")) |:
|
||||
check( toLines(lines), events, level)
|
||||
}
|
||||
|
||||
/** Displays a LogEvent in a useful format for debugging. In particular, we are only interested in `Log` types
|
||||
* and non-printable characters should be escaped*/
|
||||
def show(event: LogEvent): String =
|
||||
event match
|
||||
{
|
||||
case l: Log => "Log('" + Escape(l.msg) + "', " + l.level + ")"
|
||||
case _ => "Not Log"
|
||||
}
|
||||
/** Writes the given lines to the Writer. `lines` is taken to be a list of lines, which are
|
||||
* represented as separately written segments (ToLog instances). ToLog.`byCharacter`
|
||||
* indicates whether to write the segment by character (true) or all at once (false)*/
|
||||
def logLines(writer: Writer, lines: List[List[ToLog]], newLine: String)
|
||||
{
|
||||
for(line <- lines; section <- line)
|
||||
{
|
||||
val content = section.content
|
||||
val normalized = Escape.newline(content, newLine)
|
||||
if(section.byCharacter)
|
||||
normalized.foreach { c => writer.write(c.toInt) }
|
||||
else
|
||||
writer.write(normalized)
|
||||
}
|
||||
writer.flush()
|
||||
}
|
||||
|
||||
/** Converts the given lines in segments to lines as Strings for checking the results of the test.*/
|
||||
def toLines(lines: List[List[ToLog]]): List[String] =
|
||||
lines.map(_.map(_.contentOnly).mkString)
|
||||
/** Checks that the expected `lines` were recorded as `events` at level `Lvl`.*/
|
||||
def check(lines: List[String], events: List[LogEvent], Lvl: Level.Value): Boolean =
|
||||
(lines zip events) forall {
|
||||
case (line, log : Log) => log.level == Lvl && line == log.msg
|
||||
case _ => false
|
||||
}
|
||||
|
||||
/* The following are implicit generators to build up a write sequence.
|
||||
property("properly logged") = forAll { (output: Output, newLine: NewLine) =>
|
||||
import output.{ lines, level }
|
||||
val log = new RecordingLogger
|
||||
val writer = new LoggerWriter(log, Some(level), newLine.str)
|
||||
logLines(writer, lines, newLine.str)
|
||||
val events = log.getEvents
|
||||
("Recorded:\n" + events.map(show).mkString("\n")) |:
|
||||
check(toLines(lines), events, level)
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a LogEvent in a useful format for debugging. In particular, we are only interested in `Log` types
|
||||
* and non-printable characters should be escaped
|
||||
*/
|
||||
def show(event: LogEvent): String =
|
||||
event match {
|
||||
case l: Log => "Log('" + Escape(l.msg) + "', " + l.level + ")"
|
||||
case _ => "Not Log"
|
||||
}
|
||||
/**
|
||||
* Writes the given lines to the Writer. `lines` is taken to be a list of lines, which are
|
||||
* represented as separately written segments (ToLog instances). ToLog.`byCharacter`
|
||||
* indicates whether to write the segment by character (true) or all at once (false)
|
||||
*/
|
||||
def logLines(writer: Writer, lines: List[List[ToLog]], newLine: String) {
|
||||
for (line <- lines; section <- line) {
|
||||
val content = section.content
|
||||
val normalized = Escape.newline(content, newLine)
|
||||
if (section.byCharacter)
|
||||
normalized.foreach { c => writer.write(c.toInt) }
|
||||
else
|
||||
writer.write(normalized)
|
||||
}
|
||||
writer.flush()
|
||||
}
|
||||
|
||||
/** Converts the given lines in segments to lines as Strings for checking the results of the test.*/
|
||||
def toLines(lines: List[List[ToLog]]): List[String] =
|
||||
lines.map(_.map(_.contentOnly).mkString)
|
||||
/** Checks that the expected `lines` were recorded as `events` at level `Lvl`.*/
|
||||
def check(lines: List[String], events: List[LogEvent], Lvl: Level.Value): Boolean =
|
||||
(lines zip events) forall {
|
||||
case (line, log: Log) => log.level == Lvl && line == log.msg
|
||||
case _ => false
|
||||
}
|
||||
|
||||
/* The following are implicit generators to build up a write sequence.
|
||||
* ToLog represents a written segment. NewLine represents one of the possible
|
||||
* newline separators. A List[ToLog] represents a full line and always includes a
|
||||
* final ToLog with a trailing '\n'. Newline characters are otherwise not present in
|
||||
* the `content` of a ToLog instance.*/
|
||||
|
||||
implicit lazy val arbOut: Arbitrary[Output] = Arbitrary(genOutput)
|
||||
implicit lazy val arbLog: Arbitrary[ToLog] = Arbitrary(genLog)
|
||||
implicit lazy val arbLine: Arbitrary[List[ToLog]] = Arbitrary(genLine)
|
||||
implicit lazy val arbNewLine: Arbitrary[NewLine] = Arbitrary(genNewLine)
|
||||
implicit lazy val arbLevel : Arbitrary[Level.Value] = Arbitrary(genLevel)
|
||||
|
||||
implicit def genLine(implicit logG: Gen[ToLog]): Gen[List[ToLog]] =
|
||||
for(l <- listOf[ToLog](MaxSegments); last <- logG) yield
|
||||
(addNewline(last) :: l.filter(!_.content.isEmpty)).reverse
|
||||
|
||||
implicit def genLog(implicit content: Arbitrary[String], byChar: Arbitrary[Boolean]): Gen[ToLog] =
|
||||
for(c <- content.arbitrary; by <- byChar.arbitrary) yield
|
||||
{
|
||||
assert(c != null)
|
||||
new ToLog(removeNewlines(c), by)
|
||||
}
|
||||
|
||||
implicit lazy val genNewLine: Gen[NewLine] =
|
||||
for(str <- oneOf("\n", "\r", "\r\n")) yield
|
||||
new NewLine(str)
|
||||
|
||||
implicit lazy val genLevel: Gen[Level.Value] =
|
||||
oneOf(Level.values.toSeq)
|
||||
|
||||
implicit lazy val genOutput: Gen[Output] =
|
||||
for(ls <- listOf[List[ToLog]](MaxLines); lv <- genLevel) yield
|
||||
new Output(ls, lv)
|
||||
|
||||
def removeNewlines(s: String) = s.replaceAll("""[\n\r]+""", "")
|
||||
def addNewline(l: ToLog): ToLog =
|
||||
new ToLog(l.content + "\n", l.byCharacter) // \n will be replaced by a random line terminator for all lines
|
||||
implicit lazy val arbOut: Arbitrary[Output] = Arbitrary(genOutput)
|
||||
implicit lazy val arbLog: Arbitrary[ToLog] = Arbitrary(genLog)
|
||||
implicit lazy val arbLine: Arbitrary[List[ToLog]] = Arbitrary(genLine)
|
||||
implicit lazy val arbNewLine: Arbitrary[NewLine] = Arbitrary(genNewLine)
|
||||
implicit lazy val arbLevel: Arbitrary[Level.Value] = Arbitrary(genLevel)
|
||||
|
||||
def listOf[T](max: Int)(implicit content: Arbitrary[T]): Gen[List[T]] =
|
||||
Gen.choose(0, max) flatMap { sz => listOfN(sz, content.arbitrary) }
|
||||
implicit def genLine(implicit logG: Gen[ToLog]): Gen[List[ToLog]] =
|
||||
for (l <- listOf[ToLog](MaxSegments); last <- logG) yield (addNewline(last) :: l.filter(!_.content.isEmpty)).reverse
|
||||
|
||||
implicit def genLog(implicit content: Arbitrary[String], byChar: Arbitrary[Boolean]): Gen[ToLog] =
|
||||
for (c <- content.arbitrary; by <- byChar.arbitrary) yield {
|
||||
assert(c != null)
|
||||
new ToLog(removeNewlines(c), by)
|
||||
}
|
||||
|
||||
implicit lazy val genNewLine: Gen[NewLine] =
|
||||
for (str <- oneOf("\n", "\r", "\r\n")) yield new NewLine(str)
|
||||
|
||||
implicit lazy val genLevel: Gen[Level.Value] =
|
||||
oneOf(Level.values.toSeq)
|
||||
|
||||
implicit lazy val genOutput: Gen[Output] =
|
||||
for (ls <- listOf[List[ToLog]](MaxLines); lv <- genLevel) yield new Output(ls, lv)
|
||||
|
||||
def removeNewlines(s: String) = s.replaceAll("""[\n\r]+""", "")
|
||||
def addNewline(l: ToLog): ToLog =
|
||||
new ToLog(l.content + "\n", l.byCharacter) // \n will be replaced by a random line terminator for all lines
|
||||
|
||||
def listOf[T](max: Int)(implicit content: Arbitrary[T]): Gen[List[T]] =
|
||||
Gen.choose(0, max) flatMap { sz => listOfN(sz, content.arbitrary) }
|
||||
}
|
||||
|
||||
/* Helper classes*/
|
||||
|
||||
final class Output(val lines: List[List[ToLog]], val level: Level.Value) extends NotNull
|
||||
{
|
||||
override def toString =
|
||||
"Level: " + level + "\n" + lines.map(_.mkString).mkString("\n")
|
||||
final class Output(val lines: List[List[ToLog]], val level: Level.Value) extends NotNull {
|
||||
override def toString =
|
||||
"Level: " + level + "\n" + lines.map(_.mkString).mkString("\n")
|
||||
}
|
||||
final class NewLine(val str: String) extends NotNull
|
||||
{
|
||||
override def toString = Escape(str)
|
||||
final class NewLine(val str: String) extends NotNull {
|
||||
override def toString = Escape(str)
|
||||
}
|
||||
final class ToLog(val content: String, val byCharacter: Boolean) extends NotNull
|
||||
{
|
||||
def contentOnly = Escape.newline(content, "")
|
||||
override def toString = if(content.isEmpty) "" else "ToLog('" + Escape(contentOnly) + "', " + byCharacter + ")"
|
||||
final class ToLog(val content: String, val byCharacter: Boolean) extends NotNull {
|
||||
def contentOnly = Escape.newline(content, "")
|
||||
override def toString = if (content.isEmpty) "" else "ToLog('" + Escape(contentOnly) + "', " + byCharacter + ")"
|
||||
}
|
||||
/** Defines some utility methods for escaping unprintable characters.*/
|
||||
object Escape
|
||||
{
|
||||
/** Escapes characters with code less than 20 by printing them as unicode escapes.*/
|
||||
def apply(s: String): String =
|
||||
{
|
||||
val builder = new StringBuilder(s.length)
|
||||
for(c <- s)
|
||||
{
|
||||
def escaped = pad(c.toInt.toHexString.toUpperCase, 4, '0')
|
||||
if(c < 20) builder.append("\\u").append(escaped) else builder.append(c)
|
||||
}
|
||||
builder.toString
|
||||
}
|
||||
def pad(s: String, minLength: Int, extra: Char) =
|
||||
{
|
||||
val diff = minLength - s.length
|
||||
if(diff <= 0) s else List.make(diff, extra).mkString("", "", s)
|
||||
}
|
||||
/** Replaces a \n character at the end of a string `s` with `nl`.*/
|
||||
def newline(s: String, nl: String): String =
|
||||
if(s.endsWith("\n")) s.substring(0, s.length - 1) + nl else s
|
||||
object Escape {
|
||||
/** Escapes characters with code less than 20 by printing them as unicode escapes.*/
|
||||
def apply(s: String): String =
|
||||
{
|
||||
val builder = new StringBuilder(s.length)
|
||||
for (c <- s) {
|
||||
def escaped = pad(c.toInt.toHexString.toUpperCase, 4, '0')
|
||||
if (c < 20) builder.append("\\u").append(escaped) else builder.append(c)
|
||||
}
|
||||
builder.toString
|
||||
}
|
||||
def pad(s: String, minLength: Int, extra: Char) =
|
||||
{
|
||||
val diff = minLength - s.length
|
||||
if (diff <= 0) s else List.make(diff, extra).mkString("", "", s)
|
||||
}
|
||||
/** Replaces a \n character at the end of a string `s` with `nl`.*/
|
||||
def newline(s: String, nl: String): String =
|
||||
if (s.endsWith("\n")) s.substring(0, s.length - 1) + nl else s
|
||||
}
|
||||
/** Records logging events for later retrieval.*/
|
||||
final class RecordingLogger extends BasicLogger
|
||||
{
|
||||
private var events: List[LogEvent] = Nil
|
||||
|
||||
def getEvents = events.reverse
|
||||
|
||||
override def ansiCodesSupported = true
|
||||
def trace(t: => Throwable) { events ::= new Trace(t) }
|
||||
def log(level: Level.Value, message: => String) { events ::= new Log(level, message) }
|
||||
def success(message: => String) { events ::= new Success(message) }
|
||||
def logAll(es: Seq[LogEvent]) { events :::= es.toList }
|
||||
def control(event: ControlEvent.Value, message: => String) { events ::= new ControlEvent(event, message) }
|
||||
|
||||
final class RecordingLogger extends BasicLogger {
|
||||
private var events: List[LogEvent] = Nil
|
||||
|
||||
def getEvents = events.reverse
|
||||
|
||||
override def ansiCodesSupported = true
|
||||
def trace(t: => Throwable) { events ::= new Trace(t) }
|
||||
def log(level: Level.Value, message: => String) { events ::= new Log(level, message) }
|
||||
def success(message: => String) { events ::= new Success(message) }
|
||||
def logAll(es: Seq[LogEvent]) { events :::= es.toList }
|
||||
def control(event: ControlEvent.Value, message: => String) { events ::= new ControlEvent(event, message) }
|
||||
|
||||
}
|
||||
|
|
@ -1,11 +1,10 @@
|
|||
package sbt
|
||||
|
||||
object TestLogger
|
||||
{
|
||||
def apply[T](f: Logger => T): T =
|
||||
{
|
||||
val log = new BufferedLogger(ConsoleLogger())
|
||||
log.setLevel(Level.Debug)
|
||||
log.bufferQuietly(f(log))
|
||||
}
|
||||
object TestLogger {
|
||||
def apply[T](f: Logger => T): T =
|
||||
{
|
||||
val log = new BufferedLogger(ConsoleLogger())
|
||||
log.setLevel(Level.Debug)
|
||||
log.bufferQuietly(f(log))
|
||||
}
|
||||
}
|
||||
|
|
@ -1,117 +1,115 @@
|
|||
package sbt
|
||||
package logic
|
||||
|
||||
import org.scalacheck._
|
||||
import Prop.secure
|
||||
import Logic.{LogicException, Matched}
|
||||
import org.scalacheck._
|
||||
import Prop.secure
|
||||
import Logic.{ LogicException, Matched }
|
||||
|
||||
object LogicTest extends Properties("Logic")
|
||||
{
|
||||
import TestClauses._
|
||||
object LogicTest extends Properties("Logic") {
|
||||
import TestClauses._
|
||||
|
||||
property("Handles trivial resolution.") = secure( expect(trivial, Set(A) ) )
|
||||
property("Handles less trivial resolution.") = secure( expect(lessTrivial, Set(B,A,D)) )
|
||||
property("Handles cycles without negation") = secure( expect(cycles, Set(F,A,B)) )
|
||||
property("Handles basic exclusion.") = secure( expect(excludedPos, Set()) )
|
||||
property("Handles exclusion of head proved by negation.") = secure( expect(excludedNeg, Set()) )
|
||||
// TODO: actually check ordering, probably as part of a check that dependencies are satisifed
|
||||
property("Properly orders results.") = secure( expect(ordering, Set(B,A,C,E,F)))
|
||||
property("Detects cyclic negation") = secure(
|
||||
Logic.reduceAll(badClauses, Set()) match {
|
||||
case Right(res) => false
|
||||
case Left(err: Logic.CyclicNegation) => true
|
||||
case Left(err) => error(s"Expected cyclic error, got: $err")
|
||||
}
|
||||
)
|
||||
property("Handles trivial resolution.") = secure(expect(trivial, Set(A)))
|
||||
property("Handles less trivial resolution.") = secure(expect(lessTrivial, Set(B, A, D)))
|
||||
property("Handles cycles without negation") = secure(expect(cycles, Set(F, A, B)))
|
||||
property("Handles basic exclusion.") = secure(expect(excludedPos, Set()))
|
||||
property("Handles exclusion of head proved by negation.") = secure(expect(excludedNeg, Set()))
|
||||
// TODO: actually check ordering, probably as part of a check that dependencies are satisifed
|
||||
property("Properly orders results.") = secure(expect(ordering, Set(B, A, C, E, F)))
|
||||
property("Detects cyclic negation") = secure(
|
||||
Logic.reduceAll(badClauses, Set()) match {
|
||||
case Right(res) => false
|
||||
case Left(err: Logic.CyclicNegation) => true
|
||||
case Left(err) => error(s"Expected cyclic error, got: $err")
|
||||
}
|
||||
)
|
||||
|
||||
def expect(result: Either[LogicException, Matched], expected: Set[Atom]) = result match {
|
||||
case Left(err) => false
|
||||
case Right(res) =>
|
||||
val actual = res.provenSet
|
||||
(actual == expected) || error(s"Expected to prove $expected, but actually proved $actual")
|
||||
}
|
||||
def expect(result: Either[LogicException, Matched], expected: Set[Atom]) = result match {
|
||||
case Left(err) => false
|
||||
case Right(res) =>
|
||||
val actual = res.provenSet
|
||||
(actual == expected) || error(s"Expected to prove $expected, but actually proved $actual")
|
||||
}
|
||||
}
|
||||
|
||||
object TestClauses
|
||||
{
|
||||
object TestClauses {
|
||||
|
||||
val A = Atom("A")
|
||||
val B = Atom("B")
|
||||
val C = Atom("C")
|
||||
val D = Atom("D")
|
||||
val E = Atom("E")
|
||||
val F = Atom("F")
|
||||
val G = Atom("G")
|
||||
val A = Atom("A")
|
||||
val B = Atom("B")
|
||||
val C = Atom("C")
|
||||
val D = Atom("D")
|
||||
val E = Atom("E")
|
||||
val F = Atom("F")
|
||||
val G = Atom("G")
|
||||
|
||||
val clauses =
|
||||
A.proves(B) ::
|
||||
A.proves(F) ::
|
||||
B.proves(F) ::
|
||||
F.proves(A) ::
|
||||
(!C).proves(F) ::
|
||||
D.proves(C) ::
|
||||
C.proves(D) ::
|
||||
Nil
|
||||
val clauses =
|
||||
A.proves(B) ::
|
||||
A.proves(F) ::
|
||||
B.proves(F) ::
|
||||
F.proves(A) ::
|
||||
(!C).proves(F) ::
|
||||
D.proves(C) ::
|
||||
C.proves(D) ::
|
||||
Nil
|
||||
|
||||
val cycles = Logic.reduceAll(clauses, Set())
|
||||
val cycles = Logic.reduceAll(clauses, Set())
|
||||
|
||||
val badClauses =
|
||||
A.proves(D) ::
|
||||
clauses
|
||||
val badClauses =
|
||||
A.proves(D) ::
|
||||
clauses
|
||||
|
||||
val excludedNeg = {
|
||||
val cs =
|
||||
(!A).proves(B) ::
|
||||
Nil
|
||||
val init =
|
||||
(!A) ::
|
||||
(!B) ::
|
||||
Nil
|
||||
Logic.reduceAll(cs, init.toSet)
|
||||
}
|
||||
val excludedNeg = {
|
||||
val cs =
|
||||
(!A).proves(B) ::
|
||||
Nil
|
||||
val init =
|
||||
(!A) ::
|
||||
(!B) ::
|
||||
Nil
|
||||
Logic.reduceAll(cs, init.toSet)
|
||||
}
|
||||
|
||||
val excludedPos = {
|
||||
val cs =
|
||||
A.proves(B) ::
|
||||
Nil
|
||||
val init =
|
||||
A ::
|
||||
(!B) ::
|
||||
Nil
|
||||
Logic.reduceAll(cs, init.toSet)
|
||||
}
|
||||
val excludedPos = {
|
||||
val cs =
|
||||
A.proves(B) ::
|
||||
Nil
|
||||
val init =
|
||||
A ::
|
||||
(!B) ::
|
||||
Nil
|
||||
Logic.reduceAll(cs, init.toSet)
|
||||
}
|
||||
|
||||
val trivial = {
|
||||
val cs =
|
||||
Formula.True.proves(A) ::
|
||||
Nil
|
||||
Logic.reduceAll(cs, Set.empty)
|
||||
}
|
||||
val trivial = {
|
||||
val cs =
|
||||
Formula.True.proves(A) ::
|
||||
Nil
|
||||
Logic.reduceAll(cs, Set.empty)
|
||||
}
|
||||
|
||||
val lessTrivial = {
|
||||
val cs =
|
||||
Formula.True.proves(A) ::
|
||||
Formula.True.proves(B) ::
|
||||
(A && B && (!C)).proves(D) ::
|
||||
Nil
|
||||
Logic.reduceAll(cs, Set())
|
||||
}
|
||||
val lessTrivial = {
|
||||
val cs =
|
||||
Formula.True.proves(A) ::
|
||||
Formula.True.proves(B) ::
|
||||
(A && B && (!C)).proves(D) ::
|
||||
Nil
|
||||
Logic.reduceAll(cs, Set())
|
||||
}
|
||||
|
||||
val ordering = {
|
||||
val cs =
|
||||
E.proves(F) ::
|
||||
(C && !D).proves(E) ::
|
||||
(A && B).proves(C) ::
|
||||
Nil
|
||||
Logic.reduceAll(cs, Set(A,B))
|
||||
}
|
||||
val ordering = {
|
||||
val cs =
|
||||
E.proves(F) ::
|
||||
(C && !D).proves(E) ::
|
||||
(A && B).proves(C) ::
|
||||
Nil
|
||||
Logic.reduceAll(cs, Set(A, B))
|
||||
}
|
||||
|
||||
def all {
|
||||
println(s"Cycles: $cycles")
|
||||
println(s"xNeg: $excludedNeg")
|
||||
println(s"xPos: $excludedPos")
|
||||
println(s"trivial: $trivial")
|
||||
println(s"lessTrivial: $lessTrivial")
|
||||
println(s"ordering: $ordering")
|
||||
}
|
||||
def all {
|
||||
println(s"Cycles: $cycles")
|
||||
println(s"xNeg: $excludedNeg")
|
||||
println(s"xPos: $excludedPos")
|
||||
println(s"trivial: $trivial")
|
||||
println(s"lessTrivial: $lessTrivial")
|
||||
println(s"ordering: $ordering")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,133 +1,131 @@
|
|||
package sbt
|
||||
|
||||
import java.io.File
|
||||
import org.scalacheck.{Arbitrary, Gen, Prop, Properties}
|
||||
import org.scalacheck.{ Arbitrary, Gen, Prop, Properties }
|
||||
import Prop._
|
||||
|
||||
import Process._
|
||||
|
||||
object ProcessSpecification extends Properties("Process I/O")
|
||||
{
|
||||
implicit val exitCodeArb: Arbitrary[Array[Byte]] = Arbitrary(
|
||||
for(size <- Gen.choose(0, 10);
|
||||
l <- Gen.listOfN[Byte](size, Arbitrary.arbByte.arbitrary))
|
||||
yield
|
||||
l.toArray
|
||||
)
|
||||
object ProcessSpecification extends Properties("Process I/O") {
|
||||
implicit val exitCodeArb: Arbitrary[Array[Byte]] = Arbitrary(
|
||||
for (
|
||||
size <- Gen.choose(0, 10);
|
||||
l <- Gen.listOfN[Byte](size, Arbitrary.arbByte.arbitrary)
|
||||
) yield l.toArray
|
||||
)
|
||||
|
||||
/*property("Correct exit code") = forAll( (exitCode: Byte) => checkExit(exitCode))
|
||||
/*property("Correct exit code") = forAll( (exitCode: Byte) => checkExit(exitCode))
|
||||
property("#&& correct") = forAll( (exitCodes: Array[Byte]) => checkBinary(exitCodes)(_ #&& _)(_ && _))
|
||||
property("#|| correct") = forAll( (exitCodes: Array[Byte]) => checkBinary(exitCodes)(_ #|| _)(_ || _))
|
||||
property("### correct") = forAll( (exitCodes: Array[Byte]) => checkBinary(exitCodes)(_ ### _)( (x,latest) => latest))*/
|
||||
property("Pipe to output file") = forAll( (data: Array[Byte]) => checkFileOut(data))
|
||||
property("Pipe from input file") = forAll( (data: Array[Byte]) => checkFileIn(data))
|
||||
property("Pipe to process") = forAll( (data: Array[Byte]) => checkPipe(data))
|
||||
property("Pipe to process ignores input exit code") = forAll( (data: Array[Byte], code: Byte) => checkPipeExit(data, code))
|
||||
property("Pipe from input file to bad process preserves correct exit code.") = forAll( (data: Array[Byte], code: Byte) => checkFileInExit(data, code))
|
||||
property("Pipe to output file from bad process preserves correct exit code.") = forAll( (data: Array[Byte], code: Byte) => checkFileOutExit(data, code))
|
||||
property("Pipe to output file") = forAll((data: Array[Byte]) => checkFileOut(data))
|
||||
property("Pipe from input file") = forAll((data: Array[Byte]) => checkFileIn(data))
|
||||
property("Pipe to process") = forAll((data: Array[Byte]) => checkPipe(data))
|
||||
property("Pipe to process ignores input exit code") = forAll((data: Array[Byte], code: Byte) => checkPipeExit(data, code))
|
||||
property("Pipe from input file to bad process preserves correct exit code.") = forAll((data: Array[Byte], code: Byte) => checkFileInExit(data, code))
|
||||
property("Pipe to output file from bad process preserves correct exit code.") = forAll((data: Array[Byte], code: Byte) => checkFileOutExit(data, code))
|
||||
|
||||
private def checkBinary(codes: Array[Byte])(reduceProcesses: (ProcessBuilder, ProcessBuilder) => ProcessBuilder)(reduceExit: (Boolean, Boolean) => Boolean) =
|
||||
{
|
||||
(codes.length > 1) ==>
|
||||
{
|
||||
val unsignedCodes = codes.map(unsigned)
|
||||
val exitCode = unsignedCodes.map(code => Process(process("sbt.exit " + code))).reduceLeft(reduceProcesses) !
|
||||
val expectedExitCode = unsignedCodes.map(toBoolean).reduceLeft(reduceExit)
|
||||
toBoolean(exitCode) == expectedExitCode
|
||||
}
|
||||
}
|
||||
private def toBoolean(exitCode: Int) = exitCode == 0
|
||||
private def checkExit(code: Byte) =
|
||||
{
|
||||
val exitCode = unsigned(code)
|
||||
(process("sbt.exit " + exitCode) !) == exitCode
|
||||
}
|
||||
private def checkFileOut(data: Array[Byte]) =
|
||||
{
|
||||
withData(data) { (temporaryFile, temporaryFile2) =>
|
||||
val catCommand = process("sbt.cat " + temporaryFile.getAbsolutePath)
|
||||
catCommand #> temporaryFile2
|
||||
}
|
||||
}
|
||||
private def checkFileIn(data: Array[Byte]) =
|
||||
{
|
||||
withData(data) { (temporaryFile, temporaryFile2) =>
|
||||
val catCommand = process("sbt.cat")
|
||||
temporaryFile #> catCommand #> temporaryFile2
|
||||
}
|
||||
}
|
||||
private def checkPipe(data: Array[Byte]) =
|
||||
{
|
||||
withData(data) { (temporaryFile, temporaryFile2) =>
|
||||
val catCommand = process("sbt.cat")
|
||||
temporaryFile #> catCommand #| catCommand #> temporaryFile2
|
||||
}
|
||||
}
|
||||
private def checkPipeExit(data: Array[Byte], code: Byte) =
|
||||
withTempFiles { (a,b) =>
|
||||
IO.write(a, data)
|
||||
val catCommand = process("sbt.cat")
|
||||
val exitCommand = process(s"sbt.exit $code")
|
||||
val exit = (a #> exitCommand #| catCommand #> b).!
|
||||
(s"Exit code: $exit") |:
|
||||
(s"Output file length: ${b.length}") |:
|
||||
(exit == 0) &&
|
||||
(b.length == 0)
|
||||
}
|
||||
private def checkBinary(codes: Array[Byte])(reduceProcesses: (ProcessBuilder, ProcessBuilder) => ProcessBuilder)(reduceExit: (Boolean, Boolean) => Boolean) =
|
||||
{
|
||||
(codes.length > 1) ==>
|
||||
{
|
||||
val unsignedCodes = codes.map(unsigned)
|
||||
val exitCode = unsignedCodes.map(code => Process(process("sbt.exit " + code))).reduceLeft(reduceProcesses) !
|
||||
val expectedExitCode = unsignedCodes.map(toBoolean).reduceLeft(reduceExit)
|
||||
toBoolean(exitCode) == expectedExitCode
|
||||
}
|
||||
}
|
||||
private def toBoolean(exitCode: Int) = exitCode == 0
|
||||
private def checkExit(code: Byte) =
|
||||
{
|
||||
val exitCode = unsigned(code)
|
||||
(process("sbt.exit " + exitCode) !) == exitCode
|
||||
}
|
||||
private def checkFileOut(data: Array[Byte]) =
|
||||
{
|
||||
withData(data) { (temporaryFile, temporaryFile2) =>
|
||||
val catCommand = process("sbt.cat " + temporaryFile.getAbsolutePath)
|
||||
catCommand #> temporaryFile2
|
||||
}
|
||||
}
|
||||
private def checkFileIn(data: Array[Byte]) =
|
||||
{
|
||||
withData(data) { (temporaryFile, temporaryFile2) =>
|
||||
val catCommand = process("sbt.cat")
|
||||
temporaryFile #> catCommand #> temporaryFile2
|
||||
}
|
||||
}
|
||||
private def checkPipe(data: Array[Byte]) =
|
||||
{
|
||||
withData(data) { (temporaryFile, temporaryFile2) =>
|
||||
val catCommand = process("sbt.cat")
|
||||
temporaryFile #> catCommand #| catCommand #> temporaryFile2
|
||||
}
|
||||
}
|
||||
private def checkPipeExit(data: Array[Byte], code: Byte) =
|
||||
withTempFiles { (a, b) =>
|
||||
IO.write(a, data)
|
||||
val catCommand = process("sbt.cat")
|
||||
val exitCommand = process(s"sbt.exit $code")
|
||||
val exit = (a #> exitCommand #| catCommand #> b).!
|
||||
(s"Exit code: $exit") |:
|
||||
(s"Output file length: ${b.length}") |:
|
||||
(exit == 0) &&
|
||||
(b.length == 0)
|
||||
}
|
||||
|
||||
private def checkFileOutExit(data: Array[Byte], exitCode: Byte) =
|
||||
withTempFiles { (a,b) =>
|
||||
IO.write(a, data)
|
||||
val code = unsigned(exitCode)
|
||||
val command = process(s"sbt.exit $code")
|
||||
val exit = (a #> command #> b).!
|
||||
(s"Exit code: $exit, expected: $code") |:
|
||||
(s"Output file length: ${b.length}") |:
|
||||
(exit == code) &&
|
||||
(b.length == 0)
|
||||
}
|
||||
private def checkFileOutExit(data: Array[Byte], exitCode: Byte) =
|
||||
withTempFiles { (a, b) =>
|
||||
IO.write(a, data)
|
||||
val code = unsigned(exitCode)
|
||||
val command = process(s"sbt.exit $code")
|
||||
val exit = (a #> command #> b).!
|
||||
(s"Exit code: $exit, expected: $code") |:
|
||||
(s"Output file length: ${b.length}") |:
|
||||
(exit == code) &&
|
||||
(b.length == 0)
|
||||
}
|
||||
|
||||
private def checkFileInExit(data: Array[Byte], exitCode: Byte) =
|
||||
withTempFiles { (a,b) =>
|
||||
IO.write(a, data)
|
||||
val code = unsigned(exitCode)
|
||||
val command = process(s"sbt.exit $code")
|
||||
val exit = (a #> command).!
|
||||
(s"Exit code: $exit, expected: $code") |:
|
||||
(exit == code)
|
||||
}
|
||||
private def checkFileInExit(data: Array[Byte], exitCode: Byte) =
|
||||
withTempFiles { (a, b) =>
|
||||
IO.write(a, data)
|
||||
val code = unsigned(exitCode)
|
||||
val command = process(s"sbt.exit $code")
|
||||
val exit = (a #> command).!
|
||||
(s"Exit code: $exit, expected: $code") |:
|
||||
(exit == code)
|
||||
}
|
||||
|
||||
private def temp() = File.createTempFile("sbt", "")
|
||||
private def withData(data: Array[Byte])(f: (File, File) => ProcessBuilder) =
|
||||
withTempFiles { (a, b) =>
|
||||
IO.write(a, data)
|
||||
val process = f(a, b)
|
||||
( process ! ) == 0 && sameFiles(a, b)
|
||||
}
|
||||
private def sameFiles(a: File, b: File) =
|
||||
IO.readBytes(a) sameElements IO.readBytes(b)
|
||||
private def temp() = File.createTempFile("sbt", "")
|
||||
private def withData(data: Array[Byte])(f: (File, File) => ProcessBuilder) =
|
||||
withTempFiles { (a, b) =>
|
||||
IO.write(a, data)
|
||||
val process = f(a, b)
|
||||
(process !) == 0 && sameFiles(a, b)
|
||||
}
|
||||
private def sameFiles(a: File, b: File) =
|
||||
IO.readBytes(a) sameElements IO.readBytes(b)
|
||||
|
||||
private def withTempFiles[T](f: (File, File) => T): T =
|
||||
{
|
||||
val temporaryFile1 = temp()
|
||||
val temporaryFile2 = temp()
|
||||
try f(temporaryFile1, temporaryFile2)
|
||||
finally
|
||||
{
|
||||
temporaryFile1.delete()
|
||||
temporaryFile2.delete()
|
||||
}
|
||||
}
|
||||
private def unsigned(b: Int): Int = ((b: Int) +256) % 256
|
||||
private def unsigned(b: Byte): Int = unsigned(b: Int)
|
||||
private def process(command: String) =
|
||||
{
|
||||
val ignore = echo // just for the compile dependency so that this test is rerun when TestedProcess.scala changes, not used otherwise
|
||||
private def withTempFiles[T](f: (File, File) => T): T =
|
||||
{
|
||||
val temporaryFile1 = temp()
|
||||
val temporaryFile2 = temp()
|
||||
try f(temporaryFile1, temporaryFile2)
|
||||
finally {
|
||||
temporaryFile1.delete()
|
||||
temporaryFile2.delete()
|
||||
}
|
||||
}
|
||||
private def unsigned(b: Int): Int = ((b: Int) + 256) % 256
|
||||
private def unsigned(b: Byte): Int = unsigned(b: Int)
|
||||
private def process(command: String) =
|
||||
{
|
||||
val ignore = echo // just for the compile dependency so that this test is rerun when TestedProcess.scala changes, not used otherwise
|
||||
|
||||
val thisClasspath = List(getSource[Product], getSource[IO.type], getSource[SourceTag]).mkString(File.pathSeparator)
|
||||
"java -cp " + thisClasspath + " " + command
|
||||
}
|
||||
private def getSource[T : Manifest]: String =
|
||||
IO.classLocationFile[T].getAbsolutePath
|
||||
val thisClasspath = List(getSource[Product], getSource[IO.type], getSource[SourceTag]).mkString(File.pathSeparator)
|
||||
"java -cp " + thisClasspath + " " + command
|
||||
}
|
||||
private def getSource[T: Manifest]: String =
|
||||
IO.classLocationFile[T].getAbsolutePath
|
||||
}
|
||||
private trait SourceTag
|
||||
|
|
|
|||
|
|
@ -1,56 +1,47 @@
|
|||
package sbt
|
||||
|
||||
import java.io.{File, FileNotFoundException, IOException}
|
||||
import java.io.{ File, FileNotFoundException, IOException }
|
||||
|
||||
object exit
|
||||
{
|
||||
def main(args: Array[String])
|
||||
{
|
||||
System.exit(java.lang.Integer.parseInt(args(0)))
|
||||
}
|
||||
object exit {
|
||||
def main(args: Array[String]) {
|
||||
System.exit(java.lang.Integer.parseInt(args(0)))
|
||||
}
|
||||
}
|
||||
object cat
|
||||
{
|
||||
def main(args: Array[String])
|
||||
{
|
||||
try {
|
||||
if(args.length == 0)
|
||||
IO.transfer(System.in, System.out)
|
||||
else
|
||||
catFiles(args.toList)
|
||||
System.exit(0)
|
||||
} catch {
|
||||
case e =>
|
||||
e.printStackTrace()
|
||||
System.err.println("Error: " + e.toString)
|
||||
System.exit(1)
|
||||
}
|
||||
}
|
||||
private def catFiles(filenames: List[String]): Option[String] =
|
||||
{
|
||||
filenames match
|
||||
{
|
||||
case head :: tail =>
|
||||
val file = new File(head)
|
||||
if(file.isDirectory)
|
||||
throw new IOException("Is directory: " + file)
|
||||
else if(file.exists)
|
||||
{
|
||||
Using.fileInputStream(file) { stream =>
|
||||
IO.transfer(stream, System.out)
|
||||
}
|
||||
catFiles(tail)
|
||||
}
|
||||
else
|
||||
throw new FileNotFoundException("No such file or directory: " + file)
|
||||
case Nil => None
|
||||
}
|
||||
}
|
||||
object cat {
|
||||
def main(args: Array[String]) {
|
||||
try {
|
||||
if (args.length == 0)
|
||||
IO.transfer(System.in, System.out)
|
||||
else
|
||||
catFiles(args.toList)
|
||||
System.exit(0)
|
||||
} catch {
|
||||
case e =>
|
||||
e.printStackTrace()
|
||||
System.err.println("Error: " + e.toString)
|
||||
System.exit(1)
|
||||
}
|
||||
}
|
||||
private def catFiles(filenames: List[String]): Option[String] =
|
||||
{
|
||||
filenames match {
|
||||
case head :: tail =>
|
||||
val file = new File(head)
|
||||
if (file.isDirectory)
|
||||
throw new IOException("Is directory: " + file)
|
||||
else if (file.exists) {
|
||||
Using.fileInputStream(file) { stream =>
|
||||
IO.transfer(stream, System.out)
|
||||
}
|
||||
catFiles(tail)
|
||||
} else
|
||||
throw new FileNotFoundException("No such file or directory: " + file)
|
||||
case Nil => None
|
||||
}
|
||||
}
|
||||
}
|
||||
object echo
|
||||
{
|
||||
def main(args: Array[String])
|
||||
{
|
||||
System.out.println(args.mkString(" "))
|
||||
}
|
||||
object echo {
|
||||
def main(args: Array[String]) {
|
||||
System.out.println(args.mkString(" "))
|
||||
}
|
||||
}
|
||||
|
|
@ -6,79 +6,79 @@ package sbt
|
|||
import org.scalacheck._
|
||||
import Prop._
|
||||
|
||||
object RelationTest extends Properties("Relation")
|
||||
{
|
||||
property("Added entry check") = forAll { (pairs: List[(Int, Double)]) =>
|
||||
val r = Relation.empty[Int, Double] ++ pairs
|
||||
check(r, pairs)
|
||||
}
|
||||
def check(r: Relation[Int, Double], pairs: Seq[(Int, Double)]) =
|
||||
{
|
||||
val _1s = pairs.map(_._1).toSet
|
||||
val _2s = pairs.map(_._2).toSet
|
||||
|
||||
r._1s == _1s && r.forwardMap.keySet == _1s &&
|
||||
r._2s == _2s && r.reverseMap.keySet == _2s &&
|
||||
pairs.forall { case (a, b) =>
|
||||
(r.forward(a) contains b) &&
|
||||
(r.reverse(b) contains a) &&
|
||||
(r.forwardMap(a) contains b) &&
|
||||
(r.reverseMap(b) contains a)
|
||||
}
|
||||
}
|
||||
|
||||
property("Does not contain removed entries") = forAll { (pairs: List[(Int, Double, Boolean)]) =>
|
||||
val add = pairs.map { case (a,b,c) => (a,b) }
|
||||
val added = Relation.empty[Int, Double] ++ add
|
||||
|
||||
val removeFine = pairs.collect { case (a,b,true) => (a,b) }
|
||||
val removeCoarse = removeFine.map(_._1)
|
||||
val r = added -- removeCoarse
|
||||
|
||||
def notIn[X,Y](map: Map[X, Set[Y]], a: X, b: Y) = map.get(a).forall(set => ! (set contains b) )
|
||||
|
||||
all(removeCoarse) { rem =>
|
||||
("_1s does not contain removed" |: (!r._1s.contains(rem)) ) &&
|
||||
("Forward does not contain removed" |: r.forward(rem).isEmpty ) &&
|
||||
("Forward map does not contain removed" |: !r.forwardMap.contains(rem) ) &&
|
||||
("Removed is not a value in reverse map" |: !r.reverseMap.values.toSet.contains(rem) )
|
||||
} &&
|
||||
all(removeFine) { case (a, b) =>
|
||||
("Forward does not contain removed" |: ( !r.forward(a).contains(b) ) ) &&
|
||||
("Reverse does not contain removed" |: ( !r.reverse(b).contains(a) ) ) &&
|
||||
("Forward map does not contain removed" |: ( notIn(r.forwardMap, a, b) ) ) &&
|
||||
("Reverse map does not contain removed" |: ( notIn(r.reverseMap, b, a) ) )
|
||||
}
|
||||
}
|
||||
object RelationTest extends Properties("Relation") {
|
||||
property("Added entry check") = forAll { (pairs: List[(Int, Double)]) =>
|
||||
val r = Relation.empty[Int, Double] ++ pairs
|
||||
check(r, pairs)
|
||||
}
|
||||
def check(r: Relation[Int, Double], pairs: Seq[(Int, Double)]) =
|
||||
{
|
||||
val _1s = pairs.map(_._1).toSet
|
||||
val _2s = pairs.map(_._2).toSet
|
||||
|
||||
property("Groups correctly") = forAll { (entries: List[(Int, Double)], randomInt: Int) =>
|
||||
val splitInto = math.abs(randomInt) % 10 + 1 // Split into 1-10 groups.
|
||||
val rel = Relation.empty[Int, Double] ++ entries
|
||||
val grouped = rel groupBy (_._1 % splitInto)
|
||||
all(grouped.toSeq) {
|
||||
case (k, rel_k) => rel_k._1s forall { _ % splitInto == k }
|
||||
}
|
||||
}
|
||||
r._1s == _1s && r.forwardMap.keySet == _1s &&
|
||||
r._2s == _2s && r.reverseMap.keySet == _2s &&
|
||||
pairs.forall {
|
||||
case (a, b) =>
|
||||
(r.forward(a) contains b) &&
|
||||
(r.reverse(b) contains a) &&
|
||||
(r.forwardMap(a) contains b) &&
|
||||
(r.reverseMap(b) contains a)
|
||||
}
|
||||
}
|
||||
|
||||
property("Does not contain removed entries") = forAll { (pairs: List[(Int, Double, Boolean)]) =>
|
||||
val add = pairs.map { case (a, b, c) => (a, b) }
|
||||
val added = Relation.empty[Int, Double] ++ add
|
||||
|
||||
val removeFine = pairs.collect { case (a, b, true) => (a, b) }
|
||||
val removeCoarse = removeFine.map(_._1)
|
||||
val r = added -- removeCoarse
|
||||
|
||||
def notIn[X, Y](map: Map[X, Set[Y]], a: X, b: Y) = map.get(a).forall(set => !(set contains b))
|
||||
|
||||
all(removeCoarse) { rem =>
|
||||
("_1s does not contain removed" |: (!r._1s.contains(rem))) &&
|
||||
("Forward does not contain removed" |: r.forward(rem).isEmpty) &&
|
||||
("Forward map does not contain removed" |: !r.forwardMap.contains(rem)) &&
|
||||
("Removed is not a value in reverse map" |: !r.reverseMap.values.toSet.contains(rem))
|
||||
} &&
|
||||
all(removeFine) {
|
||||
case (a, b) =>
|
||||
("Forward does not contain removed" |: (!r.forward(a).contains(b))) &&
|
||||
("Reverse does not contain removed" |: (!r.reverse(b).contains(a))) &&
|
||||
("Forward map does not contain removed" |: (notIn(r.forwardMap, a, b))) &&
|
||||
("Reverse map does not contain removed" |: (notIn(r.reverseMap, b, a)))
|
||||
}
|
||||
}
|
||||
|
||||
property("Groups correctly") = forAll { (entries: List[(Int, Double)], randomInt: Int) =>
|
||||
val splitInto = math.abs(randomInt) % 10 + 1 // Split into 1-10 groups.
|
||||
val rel = Relation.empty[Int, Double] ++ entries
|
||||
val grouped = rel groupBy (_._1 % splitInto)
|
||||
all(grouped.toSeq) {
|
||||
case (k, rel_k) => rel_k._1s forall { _ % splitInto == k }
|
||||
}
|
||||
}
|
||||
|
||||
property("Computes size correctly") = forAll { (entries: List[(Int, Double)]) =>
|
||||
val rel = Relation.empty[Int, Double] ++ entries
|
||||
val expected = rel.all.size // Note: not entries.length, as entries may have duplicates.
|
||||
val expected = rel.all.size // Note: not entries.length, as entries may have duplicates.
|
||||
val computed = rel.size
|
||||
"Expected size: %d. Computed size: %d.".format(expected, computed) |: expected == computed
|
||||
}
|
||||
|
||||
def all[T](s: Seq[T])(p: T => Prop): Prop =
|
||||
if(s.isEmpty) true else s.map(p).reduceLeft(_ && _)
|
||||
def all[T](s: Seq[T])(p: T => Prop): Prop =
|
||||
if (s.isEmpty) true else s.map(p).reduceLeft(_ && _)
|
||||
}
|
||||
|
||||
object EmptyRelationTest extends Properties("Empty relation")
|
||||
{
|
||||
lazy val e = Relation.empty[Int, Double]
|
||||
object EmptyRelationTest extends Properties("Empty relation") {
|
||||
lazy val e = Relation.empty[Int, Double]
|
||||
|
||||
property("Forward empty") = forAll { (i: Int) => e.forward(i).isEmpty }
|
||||
property("Reverse empty") = forAll { (i: Double) => e.reverse(i).isEmpty }
|
||||
property("Forward map empty") = e.forwardMap.isEmpty
|
||||
property("Reverse map empty") = e.reverseMap.isEmpty
|
||||
property("_1 empty") = e._1s.isEmpty
|
||||
property("_2 empty") = e._2s.isEmpty
|
||||
property("Forward empty") = forAll { (i: Int) => e.forward(i).isEmpty }
|
||||
property("Reverse empty") = forAll { (i: Double) => e.reverse(i).isEmpty }
|
||||
property("Forward map empty") = e.forwardMap.isEmpty
|
||||
property("Reverse map empty") = e.reverseMap.isEmpty
|
||||
property("_1 empty") = e._1s.isEmpty
|
||||
property("_2 empty") = e._2s.isEmpty
|
||||
}
|
||||
Loading…
Reference in New Issue