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:
eugene yokota 2014-05-07 12:23:09 -04:00
commit 329278b531
18 changed files with 1080 additions and 1129 deletions

View File

@ -3,30 +3,29 @@ package sbt
import java.io.File import java.io.File
import Types.:+: import Types.:+:
object CacheTest// extends Properties("Cache test") object CacheTest // extends Properties("Cache test")
{ {
val lengthCache = new File("/tmp/length-cache") val lengthCache = new File("/tmp/length-cache")
val cCache = new File("/tmp/c-cache") val cCache = new File("/tmp/c-cache")
import Cache._ import Cache._
import FileInfo.hash._ import FileInfo.hash._
import Ordering._ import Ordering._
import sbinary.DefaultProtocol.FileFormat import sbinary.DefaultProtocol.FileFormat
def test def test {
{ lazy val create = new File("test")
lazy val create = new File("test")
val length = cached(lengthCache) { val length = cached(lengthCache) {
(f: File) => { println("File length: " + f.length); f.length } (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 c = cached(cCache) { (in: (File :+: Long :+: HNil)) =>
val file :+: len :+: HNil = in val file :+: len :+: HNil = in
println("File: " + file + " (" + file.exists + "), length: " + len) println("File: " + file + " (" + file.exists + "), length: " + len)
(len+1) :+: file :+: HNil (len + 1) :+: file :+: HNil
} }
c(create :+: fileLength :+: HNil) c(create :+: fileLength :+: HNil)
} }
} }

View File

@ -8,49 +8,43 @@ import Prop._
import scala.collection.mutable.HashSet import scala.collection.mutable.HashSet
object DagSpecification extends Properties("Dag") object DagSpecification extends Properties("Dag") {
{ property("No repeated nodes") = forAll { (dag: TestDag) => isSet(dag.topologicalSort) }
property("No repeated nodes") = forAll{ (dag: TestDag) => isSet(dag.topologicalSort) } property("Sort contains node") = forAll { (dag: TestDag) => dag.topologicalSort.contains(dag) }
property("Sort contains node") = forAll{ (dag: TestDag) => dag.topologicalSort.contains(dag) } property("Dependencies precede node") = forAll { (dag: TestDag) => dependenciesPrecedeNodes(dag.topologicalSort) }
property("Dependencies precede node") = forAll{ (dag: TestDag) => dependenciesPrecedeNodes(dag.topologicalSort) }
implicit lazy val arbTestDag: Arbitrary[TestDag] = Arbitrary(Gen.sized(dagGen)) implicit lazy val arbTestDag: Arbitrary[TestDag] = Arbitrary(Gen.sized(dagGen))
private def dagGen(nodeCount: Int): Gen[TestDag] = private def dagGen(nodeCount: Int): Gen[TestDag] =
{ {
val nodes = new HashSet[TestDag] val nodes = new HashSet[TestDag]
def nonterminalGen(p: Gen.Parameters): Gen[TestDag] = def nonterminalGen(p: Gen.Parameters): Gen[TestDag] =
{ {
for(i <- 0 until nodeCount; nextDeps <- Gen.someOf(nodes).apply(p)) for (i <- 0 until nodeCount; nextDeps <- Gen.someOf(nodes).apply(p))
nodes += new TestDag(i, nextDeps) nodes += new TestDag(i, nextDeps)
for(nextDeps <- Gen.someOf(nodes)) yield for (nextDeps <- Gen.someOf(nodes)) yield new TestDag(nodeCount, nextDeps)
new TestDag(nodeCount, nextDeps) }
} Gen.parameterized(nonterminalGen)
Gen.parameterized(nonterminalGen) }
}
private def isSet[T](c: Seq[T]) = Set(c: _*).size == c.size private def isSet[T](c: Seq[T]) = Set(c: _*).size == c.size
private def dependenciesPrecedeNodes(sort: List[TestDag]) = private def dependenciesPrecedeNodes(sort: List[TestDag]) =
{ {
val seen = new HashSet[TestDag] val seen = new HashSet[TestDag]
def iterate(remaining: List[TestDag]): Boolean = def iterate(remaining: List[TestDag]): Boolean =
{ {
remaining match remaining match {
{ case Nil => true
case Nil => true case node :: tail =>
case node :: tail => if (node.dependencies.forall(seen.contains) && !seen.contains(node)) {
if(node.dependencies.forall(seen.contains) && !seen.contains(node)) seen += node
{ iterate(tail)
seen += node } else
iterate(tail) false
} }
else }
false iterate(sort)
} }
}
iterate(sort)
}
} }
class TestDag(id: Int, val dependencies: Iterable[TestDag]) extends Dag[TestDag] class TestDag(id: Int, val dependencies: Iterable[TestDag]) extends Dag[TestDag] {
{ override def toString = id + "->" + dependencies.mkString("[", ",", "]")
override def toString = id + "->" + dependencies.mkString("[", ",", "]")
} }

View File

@ -1,35 +1,32 @@
package sbt package sbt
import org.scalacheck._ import org.scalacheck._
import Prop._ import Prop._
object KeyTest extends Properties("AttributeKey") object KeyTest extends Properties("AttributeKey") {
{ property("equality") = {
property("equality") = { compare(AttributeKey[Int]("test"), AttributeKey[Int]("test"), true) &&
compare(AttributeKey[Int]("test"), AttributeKey[Int]("test"), true) && compare(AttributeKey[Int]("test"), AttributeKey[Int]("test", "description"), true) &&
compare(AttributeKey[Int]("test"), AttributeKey[Int]("test", "description"), true) && compare(AttributeKey[Int]("test", "a"), AttributeKey[Int]("test", "b"), true) &&
compare(AttributeKey[Int]("test", "a"), AttributeKey[Int]("test", "b"), true) && compare(AttributeKey[Int]("test"), AttributeKey[Int]("tests"), false) &&
compare(AttributeKey[Int]("test"), AttributeKey[Int]("tests"), false) && compare(AttributeKey[Int]("test"), AttributeKey[Double]("test"), false) &&
compare(AttributeKey[Int]("test"), AttributeKey[Double]("test"), false) && compare(AttributeKey[java.lang.Integer]("test"), AttributeKey[Int]("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, String]]("test"), true) && compare(AttributeKey[Map[Int, String]]("test"), AttributeKey[Map[Int, _]]("test"), false)
compare(AttributeKey[Map[Int, String]]("test"), AttributeKey[Map[Int, _]]("test"), false) }
}
def compare(a: AttributeKey[_], b: AttributeKey[_], same: Boolean) = def compare(a: AttributeKey[_], b: AttributeKey[_], same: Boolean) =
("a.label: " + a.label) |: ("a.label: " + a.label) |:
("a.manifest: " + a.manifest) |: ("a.manifest: " + a.manifest) |:
("b.label: " + b.label) |: ("b.label: " + b.label) |:
("b.manifest: " + b.manifest) |: ("b.manifest: " + b.manifest) |:
("expected equal? " + same) |: ("expected equal? " + same) |:
compare0(a, b, same) compare0(a, b, same)
def compare0(a: AttributeKey[_], b: AttributeKey[_], same: Boolean) = def compare0(a: AttributeKey[_], b: AttributeKey[_], same: Boolean) =
if(same) if (same) {
{ ("equality" |: (a == b)) &&
("equality" |: (a == b)) && ("hash" |: (a.hashCode == b.hashCode))
("hash" |: (a.hashCode == b.hashCode)) } else
} ("equality" |: (a != b))
else
("equality" |: (a != b))
} }

View File

@ -7,11 +7,11 @@ import Types._
// compilation test // compilation test
object LiteralTest { object LiteralTest {
def x[A[_],B[_]](f: A ~> B) = f def x[A[_], B[_]](f: A ~> B) = f
import Param._ 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 a: List[Int] = f(Some(3))
val b: List[String] = f( Some("aa") ) val b: List[String] = f(Some("aa"))
} }

View File

@ -6,14 +6,13 @@ package sbt
import Types._ import Types._
// compilation test // compilation test
object PMapTest object PMapTest {
{ val mp = new DelegatingPMap[Some, Id](new collection.mutable.HashMap)
val mp = new DelegatingPMap[Some, Id](new collection.mutable.HashMap) mp(Some("asdf")) = "a"
mp(Some("asdf")) = "a" mp(Some(3)) = 9
mp(Some(3)) = 9 val x = Some(3) :^: Some("asdf") :^: KNil
val x = Some(3) :^: Some("asdf") :^: KNil val y = x.transform[Id](mp)
val y = x.transform[Id](mp) assert(y.head == 9)
assert(y.head == 9) assert(y.tail.head == "a")
assert(y.tail.head == "a") assert(y.tail.tail == KNil)
assert(y.tail.tail == KNil)
} }

View File

@ -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 // 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. // when you want a type function with only one parameter.
// That would be a general pain.) // That would be a general pain.)
object SettingsExample extends Init[Scope] object SettingsExample extends Init[Scope] {
{ // Provides a way of showing a Scope+AttributeKey[_]
// Provides a way of showing a Scope+AttributeKey[_] val showFullKey: Show[ScopedKey[_]] = new Show[ScopedKey[_]] {
val showFullKey: Show[ScopedKey[_]] = new Show[ScopedKey[_]] { def apply(key: ScopedKey[_]) = s"${key.scope.nestIndex}(${key.scope.idAtIndex})/${key.key.label}"
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. // A sample delegation function that delegates to a Scope with a lower index.
val delegates: Scope => Seq[Scope] = { case s @ Scope(index, proj) => val delegates: Scope => Seq[Scope] = {
s +: (if(index <= 0) Nil else { (if (proj > 0) List(Scope(index)) else Nil) ++: delegates(Scope(index-1)) }) 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. // Not using this feature in this example.
val scopeLocal: ScopeLocal = _ => Nil 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 **/ /** Usage Example **/
object SettingsUsage object SettingsUsage {
{ import SettingsExample._
import SettingsExample._ import Types._
import Types._
// Define some keys // Define some keys
val a = AttributeKey[Int]("a") val a = AttributeKey[Int]("a")
val b = AttributeKey[Int]("b") val b = AttributeKey[Int]("b")
// Scope these keys // Scope these keys
val a3 = ScopedKey(Scope(3), a) val a3 = ScopedKey(Scope(3), a)
val a4 = ScopedKey(Scope(4), a) val a4 = ScopedKey(Scope(4), a)
val a5 = ScopedKey(Scope(5), a) val a5 = ScopedKey(Scope(5), a)
val b4 = ScopedKey(Scope(4), b) val b4 = ScopedKey(Scope(4), b)
// Define some settings // Define some settings
val mySettings: Seq[Setting[_]] = Seq( val mySettings: Seq[Setting[_]] = Seq(
setting( a3, value( 3 ) ), setting(a3, value(3)),
setting( b4, map(a4)(_ * 3)), setting(b4, map(a4)(_ * 3)),
update(a5)(_ + 1) update(a5)(_ + 1)
) )
// "compiles" and applies the settings. // "compiles" and applies the settings.
// This can be split into multiple steps to access intermediate results if desired. // This can be split into multiple steps to access intermediate results if desired.
// The 'inspect' command operates on the output of 'compile', for example. // The 'inspect' command operates on the output of 'compile', for example.
val applied: Settings[Scope] = make(mySettings)(delegates, scopeLocal, showFullKey) val applied: Settings[Scope] = make(mySettings)(delegates, scopeLocal, showFullKey)
// Show results. // Show results.
/* for(i <- 0 to 5; k <- Seq(a, b)) { /* for(i <- 0 to 5; k <- Seq(a, b)) {
println( k.label + i + " = " + applied.get( Scope(i), k) ) 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. * Output:
* For a3, we explicitly defined it to be 3. * For the None results, we never defined the value and there was no value to delegate to.
* a4 wasn't defined, so it delegates to a3 according to our delegates function. * For a3, we explicitly defined it to be 3.
* b4 gets the value for a4 (which delegates to a3, so it is 3) and multiplies by 3 * a4 wasn't defined, so it delegates to a3 according to our delegates function.
* a5 is defined as the previous value of a5 + 1 and * b4 gets the value for a4 (which delegates to a3, so it is 3) and multiplies by 3
* since no previous value of a5 was defined, it delegates to a4, resulting in 3+1=4. * a5 is defined as the previous value of a5 + 1 and
* b5 isn't defined explicitly, so it delegates to b4 and is therefore equal to 9 as well * since no previous value of a5 was defined, it delegates to a4, resulting in 3+1=4.
a0 = None * b5 isn't defined explicitly, so it delegates to b4 and is therefore equal to 9 as well
b0 = None * a0 = None
a1 = None * b0 = None
b1 = None * a1 = None
a2 = None * b1 = None
b2 = None * a2 = None
a3 = Some(3) * b2 = None
b3 = None * a3 = Some(3)
a4 = Some(3) * b3 = None
b4 = Some(9) * a4 = Some(3)
a5 = Some(4) * b4 = Some(9)
b5 = Some(9) * a5 = Some(4)
**/ * b5 = Some(9)
*/
} }

View File

@ -5,175 +5,174 @@ import Prop._
import SettingsUsage._ import SettingsUsage._
import SettingsExample._ import SettingsExample._
object SettingsTest extends Properties("settings") object SettingsTest extends Properties("settings") {
{
import scala.reflect.Manifest import scala.reflect.Manifest
final val ChainMax = 5000 final val ChainMax = 5000
lazy val chainLengthGen = Gen.choose(1, ChainMax) 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) => property("Basic chain") = forAll(chainLengthGen) { (i: Int) =>
val abs = math.abs(i) val abs = math.abs(i)
singleIntTest( chain( abs, value(0)), abs ) singleIntTest(chain(abs, value(0)), abs)
} }
property("Basic bind chain") = forAll(chainLengthGen) { (i: Int) => property("Basic bind chain") = forAll(chainLengthGen) { (i: Int) =>
val abs = math.abs(i) val abs = math.abs(i)
singleIntTest( chainBind(value(abs)), 0 ) singleIntTest(chainBind(value(abs)), 0)
} }
property("Allows references to completed settings") = forAllNoShrink(30) { allowedReference } property("Allows references to completed settings") = forAllNoShrink(30) { allowedReference }
final def allowedReference(intermediate: Int): Prop = final def allowedReference(intermediate: Int): Prop =
{ {
val top = value(intermediate) val top = value(intermediate)
def iterate(init: Initialize[Int]): Initialize[Int] = def iterate(init: Initialize[Int]): Initialize[Int] =
bind(init) { t => bind(init) { t =>
if(t <= 0) if (t <= 0)
top top
else else
iterate(value(t-1) ) iterate(value(t - 1))
} }
evaluate( setting(chk, iterate(top)) :: Nil); true evaluate(setting(chk, iterate(top)) :: Nil); true
} }
property("Derived setting chain depending on (prev derived, normal setting)") = forAllNoShrink(Gen.choose(1, 100)) { derivedSettings } property("Derived setting chain depending on (prev derived, normal setting)") = forAllNoShrink(Gen.choose(1, 100)) { derivedSettings }
final def derivedSettings(nr: Int): Prop = final def derivedSettings(nr: Int): Prop =
{ {
val genScopedKeys = { val genScopedKeys = {
val attrKeys = mkAttrKeys[Int](nr) val attrKeys = mkAttrKeys[Int](nr)
attrKeys map (_ map (ak => ScopedKey(Scope(0), ak))) attrKeys map (_ map (ak => ScopedKey(Scope(0), ak)))
} }
forAll(genScopedKeys) { scopedKeys => forAll(genScopedKeys) { scopedKeys =>
val last = scopedKeys.last val last = scopedKeys.last
val derivedSettings: Seq[Setting[Int]] = ( val derivedSettings: Seq[Setting[Int]] = (
for { for {
List(scoped0, scoped1) <- chk :: scopedKeys sliding 2 List(scoped0, scoped1) <- chk :: scopedKeys sliding 2
nextInit = if (scoped0 == chk) chk nextInit = if (scoped0 == chk) chk
else (scoped0 zipWith chk) { (p, _) => p + 1 } else (scoped0 zipWith chk) { (p, _) => p + 1 }
} yield derive(setting(scoped1, nextInit)) } yield derive(setting(scoped1, nextInit))
).toSeq ).toSeq
{ checkKey(last, Some(nr-1), evaluate(setting(chk, value(0)) +: derivedSettings)) :| "Not derived?" } && { checkKey(last, Some(nr - 1), evaluate(setting(chk, value(0)) +: derivedSettings)) :| "Not derived?" } &&
{ checkKey( last, None, evaluate(derivedSettings)) :| "Should not be derived" } { checkKey(last, None, evaluate(derivedSettings)) :| "Should not be derived" }
} }
} }
private def mkAttrKeys[T](nr: Int)(implicit mf: Manifest[T]): Gen[List[AttributeKey[T]]] = private def mkAttrKeys[T](nr: Int)(implicit mf: Manifest[T]): Gen[List[AttributeKey[T]]] =
{ {
val alphaStr = Gen.alphaStr val alphaStr = Gen.alphaStr
for { for {
list <- Gen.listOfN(nr, alphaStr) suchThat (l => l.size == l.distinct.size) list <- Gen.listOfN(nr, alphaStr) suchThat (l => l.size == l.distinct.size)
item <- list item <- list
} yield AttributeKey[T](item) } yield AttributeKey[T](item)
} }
property("Derived setting(s) replace DerivedSetting in the Seq[Setting[_]]") = derivedKeepsPosition property("Derived setting(s) replace DerivedSetting in the Seq[Setting[_]]") = derivedKeepsPosition
final def derivedKeepsPosition: Prop = final def derivedKeepsPosition: Prop =
{ {
val a: ScopedKey[Int] = ScopedKey(Scope(0), AttributeKey[Int]("a")) val a: ScopedKey[Int] = ScopedKey(Scope(0), AttributeKey[Int]("a"))
val b: ScopedKey[Int] = ScopedKey(Scope(0), AttributeKey[Int]("b")) val b: ScopedKey[Int] = ScopedKey(Scope(0), AttributeKey[Int]("b"))
val prop1 = { val prop1 = {
val settings: Seq[Setting[_]] = Seq( val settings: Seq[Setting[_]] = Seq(
setting(a, value(3)), setting(a, value(3)),
setting(b, value(6)), setting(b, value(6)),
derive(setting(b, a)), derive(setting(b, a)),
setting(a, value(5)), setting(a, value(5)),
setting(b, value(8)) setting(b, value(8))
) )
val ev = evaluate(settings) val ev = evaluate(settings)
checkKey(a, Some(5), ev) && checkKey(b, Some(8), ev) checkKey(a, Some(5), ev) && checkKey(b, Some(8), ev)
} }
val prop2 = { val prop2 = {
val settings: Seq[Setting[Int]] = Seq( val settings: Seq[Setting[Int]] = Seq(
setting(a, value(3)), setting(a, value(3)),
setting(b, value(6)), setting(b, value(6)),
derive(setting(b, a)), derive(setting(b, a)),
setting(a, value(5)) setting(a, value(5))
) )
val ev = evaluate(settings) val ev = evaluate(settings)
checkKey(a, Some(5), ev) && checkKey(b, Some(5), ev) checkKey(a, Some(5), ev) && checkKey(b, Some(5), ev)
} }
prop1 && prop2 prop1 && prop2
} }
property("DerivedSetting in ThisBuild scopes derived settings under projects thus allowing safe +=") = forAllNoShrink(Gen.choose(1, 100)) { derivedSettingsScope } property("DerivedSetting in ThisBuild scopes derived settings under projects thus allowing safe +=") = forAllNoShrink(Gen.choose(1, 100)) { derivedSettingsScope }
final def derivedSettingsScope(nrProjects: Int): Prop = final def derivedSettingsScope(nrProjects: Int): Prop =
{ {
forAll(mkAttrKeys[Int](2)) { case List(key, derivedKey) => forAll(mkAttrKeys[Int](2)) {
val projectKeys = for { proj <- 1 to nrProjects } yield ScopedKey(Scope(1, proj), key) case List(key, derivedKey) =>
val projectDerivedKeys = for { proj <- 1 to nrProjects } yield ScopedKey(Scope(1, proj), derivedKey) val projectKeys = for { proj <- 1 to nrProjects } yield ScopedKey(Scope(1, proj), key)
val globalKey = ScopedKey(Scope(0), key) val projectDerivedKeys = for { proj <- 1 to nrProjects } yield ScopedKey(Scope(1, proj), derivedKey)
val globalDerivedKey = ScopedKey(Scope(0), derivedKey) val globalKey = ScopedKey(Scope(0), key)
// Each project defines an initial value, but the update is defined in globalKey. val globalDerivedKey = ScopedKey(Scope(0), derivedKey)
// However, the derived Settings that come from this should be scoped in each project. // Each project defines an initial value, but the update is defined in globalKey.
val settings: Seq[Setting[_]] = // However, the derived Settings that come from this should be scoped in each project.
derive(setting(globalDerivedKey, SettingsExample.map(globalKey)(_ + 1))) +: projectKeys.map(pk => setting(pk, value(0))) val settings: Seq[Setting[_]] =
val ev = evaluate(settings) derive(setting(globalDerivedKey, SettingsExample.map(globalKey)(_ + 1))) +: projectKeys.map(pk => setting(pk, value(0)))
// Also check that the key has no value at the "global" scope val ev = evaluate(settings)
val props = for { pk <- projectDerivedKeys } yield checkKey(pk, Some(1), ev) // Also check that the key has no value at the "global" scope
checkKey(globalDerivedKey, None, ev) && Prop.all(props: _*) val props = for { pk <- projectDerivedKeys } yield checkKey(pk, Some(1), ev)
} checkKey(globalDerivedKey, None, ev) && Prop.all(props: _*)
} }
}
// Circular (dynamic) references currently loop infinitely. // Circular (dynamic) references currently loop infinitely.
// This is the expected behavior (detecting dynamic cycles is expensive), // 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) // but it may be necessary to provide an option to detect them (with a performance hit)
// This would test that cycle detection. // This would test that cycle detection.
// property("Catches circular references") = forAll(chainLengthGen) { checkCircularReferences _ } // property("Catches circular references") = forAll(chainLengthGen) { checkCircularReferences _ }
final def checkCircularReferences(intermediate: Int): Prop = final def checkCircularReferences(intermediate: Int): Prop =
{ {
val ccr = new CCR(intermediate) val ccr = new CCR(intermediate)
try { evaluate( setting(chk, ccr.top) :: Nil); false } try { evaluate(setting(chk, ccr.top) :: Nil); false }
catch { case e: java.lang.Exception => true } catch { case e: java.lang.Exception => true }
} }
def tests = def tests =
for(i <- 0 to 5; k <- Seq(a, b)) yield { for (i <- 0 to 5; k <- Seq(a, b)) yield {
val expected = expectedValues(2*i + (if(k == a) 0 else 1)) val expected = expectedValues(2 * i + (if (k == a) 0 else 1))
checkKey[Int]( ScopedKey( Scope(i), k ), expected, applied) 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 ch = AttributeKey[Int]("ch")
lazy val chk = ScopedKey( Scope(0), ch) lazy val chk = ScopedKey(Scope(0), ch)
def chain(i: Int, prev: Initialize[Int]): Initialize[Int] = def chain(i: Int, prev: Initialize[Int]): Initialize[Int] =
if(i <= 0) prev else chain(i - 1, prev(_ + 1)) if (i <= 0) prev else chain(i - 1, prev(_ + 1))
def chainBind(prev: Initialize[Int]): Initialize[Int] = def chainBind(prev: Initialize[Int]): Initialize[Int] =
bind(prev) { v => bind(prev) { v =>
if(v <= 0) prev else chainBind(value(v - 1) ) if (v <= 0) prev else chainBind(value(v - 1))
} }
def singleIntTest(i: Initialize[Int], expected: Int) = def singleIntTest(i: Initialize[Int], expected: Int) =
{ {
val eval = evaluate( setting( chk, i ) :: Nil ) val eval = evaluate(setting(chk, i) :: Nil)
checkKey( chk, Some(expected), eval ) checkKey(chk, Some(expected), eval)
} }
def checkKey[T](key: ScopedKey[T], expected: Option[T], settings: Settings[Scope]) = def checkKey[T](key: ScopedKey[T], expected: Option[T], settings: Settings[Scope]) =
{ {
val value = settings.get( key.scope, key.key) val value = settings.get(key.scope, key.key)
("Key: " + key) |: ("Key: " + key) |:
("Value: " + value) |: ("Value: " + value) |:
("Expected: " + expected) |: ("Expected: " + expected) |:
(value == expected) (value == expected)
} }
def evaluate(settings: Seq[Setting[_]]): Settings[Scope] = def evaluate(settings: Seq[Setting[_]]): Settings[Scope] =
try { make(settings)(delegates, scopeLocal, showFullKey) } try { make(settings)(delegates, scopeLocal, showFullKey) }
catch { case e: Throwable => e.printStackTrace; throw e } catch { case e: Throwable => e.printStackTrace; throw e }
} }
// This setup is a workaround for module synchronization issues // This setup is a workaround for module synchronization issues
final class CCR(intermediate: Int) final class CCR(intermediate: Int) {
{ lazy val top = iterate(value(intermediate), intermediate)
lazy val top = iterate(value(intermediate), intermediate) def iterate(init: Initialize[Int], i: Int): Initialize[Int] =
def iterate(init: Initialize[Int], i: Int): Initialize[Int] = bind(init) { t =>
bind(init) { t => if (t <= 0)
if(t <= 0) top
top else
else iterate(value(t - 1), t - 1)
iterate(value(t - 1), t-1) }
}
} }

View File

@ -1,154 +1,148 @@
package sbt.complete package sbt.complete
object JLineTest object JLineTest {
{ import DefaultParsers._
import DefaultParsers._
val one = "blue" | "green" | "black" val one = "blue" | "green" | "black"
val two = token("color" ~> Space) ~> token(one) val two = token("color" ~> Space) ~> token(one)
val three = token("color" ~> Space) ~> token(ID.examples("blue", "green", "black")) val three = token("color" ~> Space) ~> token(ID.examples("blue", "green", "black"))
val four = token("color" ~> Space) ~> token(ID, "<color name>") val four = token("color" ~> Space) ~> token(ID, "<color name>")
val num = token(NatBasic) val num = token(NatBasic)
val five = (num ~ token("+" | "-") ~ num) <~ token('=') flatMap { val five = (num ~ token("+" | "-") ~ num) <~ token('=') flatMap {
case a ~ "+" ~ b => token((a+b).toString) case a ~ "+" ~ b => token((a + b).toString)
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) val parsers = Map("1" -> one, "2" -> two, "3" -> three, "4" -> four, "5" -> five)
def main(args: Array[String]) def main(args: Array[String]) {
{ import jline.TerminalFactory
import jline.TerminalFactory import jline.console.ConsoleReader
import jline.console.ConsoleReader val reader = new ConsoleReader()
val reader = new ConsoleReader() TerminalFactory.get.init
TerminalFactory.get.init
val parser = parsers(args(0)) val parser = parsers(args(0))
JLineCompletion.installCustomCompletor(reader, parser) JLineCompletion.installCustomCompletor(reader, parser)
def loop() { def loop() {
val line = reader.readLine("> ") val line = reader.readLine("> ")
if(line ne null) { if (line ne null) {
println("Result: " + apply(parser)(line).resultEmpty) println("Result: " + apply(parser)(line).resultEmpty)
loop() loop()
} }
} }
loop() loop()
} }
} }
import Parser._ import Parser._
import org.scalacheck._ import org.scalacheck._
object ParserTest extends Properties("Completing Parser") object ParserTest extends Properties("Completing Parser") {
{ import Parsers._
import Parsers._ import DefaultParsers.matches
import DefaultParsers.matches
val nested = (token("a1") ~ token("b2")) ~ "c3" val nested = (token("a1") ~ token("b2")) ~ "c3"
val nestedDisplay = (token("a1", "<a1>") ~ token("b2", "<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) = def checkSingle(in: String, expect: Completion)(expectDisplay: Completion = expect) =
( ("token '" + in + "'") |: checkOne(in, nested, expect)) && (("token '" + in + "'") |: checkOne(in, nested, expect)) &&
( ("display '" + in + "'") |: checkOne(in, nestedDisplay, expectDisplay) ) (("display '" + in + "'") |: checkOne(in, nestedDisplay, expectDisplay))
def checkOne(in: String, parser: Parser[_], expect: Completion): Prop =
completions(parser, in, 1) == Completions.single(expect)
def checkAll(in: String, parser: Parser[_], expect: Completions): Prop = def checkOne(in: String, parser: Parser[_], expect: Completion): Prop =
{ completions(parser, in, 1) == Completions.single(expect)
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 checkInv(in: String, parser: Parser[_]): Prop = def checkAll(in: String, parser: Parser[_], expect: Completions): Prop =
{ {
val cs = completions(parser, in, 1) val cs = completions(parser, in, 1)
("completions: " + cs) |: (( cs == Completions.nil): Prop) ("completions: " + cs) |: ("Expected: " + expect) |: ((cs == expect): 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")
property("suggest space") = checkOne("", spacePort, Completion.tokenStrict("", " ")) def checkInvalid(in: String) =
property("suggest port") = checkOne(" ", spacePort, Completion.displayStrict("<port>") ) (("token '" + in + "'") |: checkInv(in, nested)) &&
property("no suggest at end") = checkOne("asdf", "asdf", Completion.suggestStrict("")) (("display '" + in + "'") |: checkInv(in, nestedDisplay))
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") def checkInv(in: String, parser: Parser[_]): Prop =
val base = (seen: Seq[String]) => token( ID examples (colors -- seen) ) {
val sep = token( Space ) val cs = completions(parser, in, 1)
val repeat = repeatDep( base, sep) ("completions: " + cs) |: ((cs == Completions.nil): Prop)
def completionStrings(ss: Set[String]): Completions = Completions(ss.map { s => Completion.tokenStrict("", s) }) }
property("repeatDep no suggestions for bad input") = checkInv(".", repeat) property("nested tokens a") = checkSingle("", Completion.tokenStrict("", "a1"))(Completion.displayStrict("<a1>"))
property("repeatDep suggest all") = checkAll("", repeat, completionStrings(colors)) property("nested tokens a1") = checkSingle("a", Completion.tokenStrict("a", "1"))(Completion.displayStrict("<a1>"))
property("repeatDep suggest remaining two") = { property("nested tokens a inv") = checkInvalid("b")
val first = colors.toSeq.head property("nested tokens b") = checkSingle("a1", Completion.tokenStrict("", "b2"))(Completion.displayStrict("<b2>"))
checkAll(first + " ", repeat, completionStrings(colors - first)) property("nested tokens b2") = checkSingle("a1b", Completion.tokenStrict("b", "2"))(Completion.displayStrict("<b2>"))
} property("nested tokens b inv") = checkInvalid("a1a")
property("repeatDep suggest remaining one") = { property("nested tokens c") = checkSingle("a1b2", Completion.suggestStrict("c3"))()
val take = colors.toSeq.take(2) property("nested tokens c3") = checkSingle("a1b2c", Completion.suggestStrict("3"))()
checkAll(take.mkString("", " ", " "), repeat, completionStrings(colors -- take)) property("nested tokens c inv") = checkInvalid("a1b2a")
}
property("repeatDep requires at least one token") = !matches(repeat, "") property("suggest space") = checkOne("", spacePort, Completion.tokenStrict("", " "))
property("repeatDep accepts one token") = matches(repeat, colors.toSeq.head) property("suggest port") = checkOne(" ", spacePort, Completion.displayStrict("<port>"))
property("repeatDep accepts two tokens") = matches(repeat, colors.toSeq.take(2).mkString(" ")) 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 object ParserExample {
{ val ws = charClass(_.isWhitespace)+
val ws = charClass(_.isWhitespace)+ val notws = charClass(!_.isWhitespace)+
val notws = charClass(!_.isWhitespace)+
val name = token("test") val name = token("test")
val options = (ws ~> token("quick" | "failed" | "new") )* val options = (ws ~> token("quick" | "failed" | "new"))*
val exampleSet = Set("am", "is", "are", "was", "were") val exampleSet = Set("am", "is", "are", "was", "were")
val include = (ws ~> token(examples(notws.string, new FixedSetExamples(exampleSet), exampleSet.size, false )) )* 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 // Get completions for some different inputs
println(completions(t, "te", 1)) println(completions(t, "te", 1))
println(completions(t, "test ",1)) println(completions(t, "test ", 1))
println(completions(t, "test w", 1)) println(completions(t, "test w", 1))
// Get the parsed result for different inputs // Get the parsed result for different inputs
println(apply(t)("te").resultEmpty) println(apply(t)("te").resultEmpty)
println(apply(t)("test").resultEmpty) println(apply(t)("test").resultEmpty)
println(apply(t)("test w").resultEmpty) println(apply(t)("test w").resultEmpty)
println(apply(t)("test was were").resultEmpty) println(apply(t)("test was were").resultEmpty)
def run(n: Int) def run(n: Int) {
{ val a = 'a'.id
val a = 'a'.id val aq = a.?
val aq = a.? val aqn = repeat(aq, min = n, max = n)
val aqn = repeat(aq, min = n, max = n) val an = repeat(a, min = n, max = n)
val an = repeat(a, min = n, max = n) val ann = aqn ~ an
val ann = aqn ~ an
def r = apply(ann)("a"*(n*2)).resultEmpty def r = apply(ann)("a" * (n * 2)).resultEmpty
println(r.isValid) println(r.isValid)
} }
def run2(n: Int) def run2(n: Int) {
{ val ab = "ab".?.*
val ab = "ab".?.* val r = apply(ab)("a" * n).resultEmpty
val r = apply(ab)("a"*n).resultEmpty println(r)
println(r) }
}
} }

View File

@ -6,87 +6,85 @@ import sbt.IO.withTemporaryDirectory
import java.io.File import java.io.File
import sbt.IO._ import sbt.IO._
class FileExamplesTest extends Specification class FileExamplesTest extends Specification {
{
"listing all files in an absolute base directory" should { "listing all files in an absolute base directory" should {
"produce the entire base directory's contents" in new directoryStructure { "produce the entire base directory's contents" in new directoryStructure {
fileExamples().toList should containTheSameElementsAs(allRelativizedPaths) fileExamples().toList should containTheSameElementsAs(allRelativizedPaths)
} }
} }
"listing files with a prefix that matches none" should { "listing files with a prefix that matches none" should {
"produce an empty list" in new directoryStructure(withCompletionPrefix = "z") { "produce an empty list" in new directoryStructure(withCompletionPrefix = "z") {
fileExamples().toList should beEmpty fileExamples().toList should beEmpty
} }
} }
"listing single-character prefixed files" should { "listing single-character prefixed files" should {
"produce matching paths only" in new directoryStructure(withCompletionPrefix = "f") { "produce matching paths only" in new directoryStructure(withCompletionPrefix = "f") {
fileExamples().toList should containTheSameElementsAs(prefixedPathsOnly) fileExamples().toList should containTheSameElementsAs(prefixedPathsOnly)
} }
} }
"listing directory-prefixed files" should { "listing directory-prefixed files" should {
"produce matching paths only" in new directoryStructure(withCompletionPrefix = "far") { "produce matching paths only" in new directoryStructure(withCompletionPrefix = "far") {
fileExamples().toList should containTheSameElementsAs(prefixedPathsOnly) 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) { "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) fileExamples().toList should containTheSameElementsAs(prefixedPathsOnly)
} }
} }
"listing files with a sub-path prefix" should { "listing files with a sub-path prefix" should {
"produce matching paths only" in new directoryStructure(withCompletionPrefix = "far" + File.separator + "ba") { "produce matching paths only" in new directoryStructure(withCompletionPrefix = "far" + File.separator + "ba") {
fileExamples().toList should containTheSameElementsAs(prefixedPathsOnly) fileExamples().toList should containTheSameElementsAs(prefixedPathsOnly)
} }
} }
"completing a full path" should { "completing a full path" should {
"produce a list with an empty string" in new directoryStructure(withCompletionPrefix = "bazaar") { "produce a list with an empty string" in new directoryStructure(withCompletionPrefix = "bazaar") {
fileExamples().toList shouldEqual List("") fileExamples().toList shouldEqual List("")
} }
} }
class directoryStructure(withCompletionPrefix: String = "") extends Scope with DelayedInit class directoryStructure(withCompletionPrefix: String = "") extends Scope with DelayedInit {
{ var fileExamples: FileExamples = _
var fileExamples: FileExamples = _ var baseDir: File = _
var baseDir: File = _ var childFiles: List[File] = _
var childFiles: List[File] = _ var childDirectories: List[File] = _
var childDirectories: List[File] = _ var nestedFiles: List[File] = _
var nestedFiles: List[File] = _ var nestedDirectories: List[File] = _
var nestedDirectories: List[File] = _
def allRelativizedPaths: List[String] = def allRelativizedPaths: List[String] =
(childFiles ++ childDirectories ++ nestedFiles ++ nestedDirectories).map(relativize(baseDir, _).get) (childFiles ++ childDirectories ++ nestedFiles ++ nestedDirectories).map(relativize(baseDir, _).get)
def prefixedPathsOnly: List[String] = def prefixedPathsOnly: List[String] =
allRelativizedPaths.filter(_ startsWith withCompletionPrefix).map(_ substring withCompletionPrefix.length) allRelativizedPaths.filter(_ startsWith withCompletionPrefix).map(_ substring withCompletionPrefix.length)
override def delayedInit(testBody: => Unit): Unit = { override def delayedInit(testBody: => Unit): Unit = {
withTemporaryDirectory { withTemporaryDirectory {
tempDir => tempDir =>
createSampleDirStructure(tempDir) createSampleDirStructure(tempDir)
fileExamples = new FileExamples(baseDir, withCompletionPrefix) fileExamples = new FileExamples(baseDir, withCompletionPrefix)
testBody testBody
} }
} }
private def createSampleDirStructure(tempDir: File): Unit = { private def createSampleDirStructure(tempDir: File): Unit = {
childFiles = toChildFiles(tempDir, List("foo", "bar", "bazaar")) childFiles = toChildFiles(tempDir, List("foo", "bar", "bazaar"))
childDirectories = toChildFiles(tempDir, List("moo", "far")) childDirectories = toChildFiles(tempDir, List("moo", "far"))
nestedFiles = toChildFiles(childDirectories(1), List("farfile1", "barfile2")) nestedFiles = toChildFiles(childDirectories(1), List("farfile1", "barfile2"))
nestedDirectories = toChildFiles(childDirectories(1), List("fardir1", "bardir2")) nestedDirectories = toChildFiles(childDirectories(1), List("fardir1", "bardir2"))
(childDirectories ++ nestedDirectories).map(_.mkdirs()) (childDirectories ++ nestedDirectories).map(_.mkdirs())
(childFiles ++ nestedFiles).map(_.createNewFile()) (childFiles ++ nestedFiles).map(_.createNewFile())
// NOTE: Creating a new file here because `tempDir.listFiles()` returned an empty list. // NOTE: Creating a new file here because `tempDir.listFiles()` returned an empty list.
baseDir = new File(tempDir.getCanonicalPath) 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, _))
} }
} }

View File

@ -5,22 +5,22 @@ import org.specs2.specification.Scope
class FixedSetExamplesTest extends Specification { class FixedSetExamplesTest extends Specification {
"adding a prefix" should { "adding a prefix" should {
"produce a smaller set of examples with the prefix removed" in new examples { "produce a smaller set of examples with the prefix removed" in new examples {
fixedSetExamples.withAddedPrefix("f")() must containTheSameElementsAs(List("oo", "ool", "u")) fixedSetExamples.withAddedPrefix("f")() must containTheSameElementsAs(List("oo", "ool", "u"))
fixedSetExamples.withAddedPrefix("fo")() must containTheSameElementsAs(List("o", "ol")) fixedSetExamples.withAddedPrefix("fo")() must containTheSameElementsAs(List("o", "ol"))
fixedSetExamples.withAddedPrefix("b")() must containTheSameElementsAs(List("ar")) fixedSetExamples.withAddedPrefix("b")() must containTheSameElementsAs(List("ar"))
} }
} }
"without a prefix" should { "without a prefix" should {
"produce the original set" in new examples { "produce the original set" in new examples {
fixedSetExamples() mustEqual exampleSet fixedSetExamples() mustEqual exampleSet
} }
} }
trait examples extends Scope { trait examples extends Scope {
val exampleSet = List("foo", "bar", "fool", "fu") val exampleSet = List("foo", "bar", "fool", "fu")
val fixedSetExamples = FixedSetExamples(exampleSet) val fixedSetExamples = FixedSetExamples(exampleSet)
} }
} }

View File

@ -6,88 +6,88 @@ import Completion._
class ParserWithExamplesTest extends Specification { class ParserWithExamplesTest extends Specification {
"listing a limited number of completions" should { "listing a limited number of completions" should {
"grab only the needed number of elements from the iterable source of examples" in new parserWithLazyExamples { "grab only the needed number of elements from the iterable source of examples" in new parserWithLazyExamples {
parserWithExamples.completions(0) parserWithExamples.completions(0)
examples.size shouldEqual maxNumberOfExamples examples.size shouldEqual maxNumberOfExamples
} }
} }
"listing only valid completions" should { "listing only valid completions" should {
"use the delegate parser to remove invalid examples" in new parserWithValidExamples { "use the delegate parser to remove invalid examples" in new parserWithValidExamples {
val validCompletions = Completions(Set( val validCompletions = Completions(Set(
suggestion("blue"), suggestion("blue"),
suggestion("red") suggestion("red")
)) ))
parserWithExamples.completions(0) shouldEqual validCompletions parserWithExamples.completions(0) shouldEqual validCompletions
} }
} }
"listing valid completions in a derived parser" should { "listing valid completions in a derived parser" should {
"produce only valid examples that start with the character of the derivation" in new parserWithValidExamples { "produce only valid examples that start with the character of the derivation" in new parserWithValidExamples {
val derivedCompletions = Completions(Set( val derivedCompletions = Completions(Set(
suggestion("lue") suggestion("lue")
)) ))
parserWithExamples.derive('b').completions(0) shouldEqual derivedCompletions parserWithExamples.derive('b').completions(0) shouldEqual derivedCompletions
} }
} }
"listing valid and invalid completions" should { "listing valid and invalid completions" should {
"produce the entire source of examples" in new parserWithAllExamples { "produce the entire source of examples" in new parserWithAllExamples {
val completions = Completions(examples.map(suggestion(_)).toSet) val completions = Completions(examples.map(suggestion(_)).toSet)
parserWithExamples.completions(0) shouldEqual completions parserWithExamples.completions(0) shouldEqual completions
} }
} }
"listing valid and invalid completions in a derived parser" should { "listing valid and invalid completions in a derived parser" should {
"produce only examples that start with the character of the derivation" in new parserWithAllExamples { "produce only examples that start with the character of the derivation" in new parserWithAllExamples {
val derivedCompletions = Completions(Set( val derivedCompletions = Completions(Set(
suggestion("lue"), suggestion("lue"),
suggestion("lock") suggestion("lock")
)) ))
parserWithExamples.derive('b').completions(0) shouldEqual derivedCompletions 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"), case class parser(examples: Iterable[String] = Set("blue", "yellow", "greeen", "block", "red"),
maxNumberOfExamples: Int = 25, maxNumberOfExamples: Int = 25,
removeInvalidExamples: Boolean) extends Scope { removeInvalidExamples: Boolean) extends Scope {
import DefaultParsers._ import DefaultParsers._
val colorParser = "blue" | "green" | "black" | "red" val colorParser = "blue" | "green" | "black" | "red"
val parserWithExamples: Parser[String] = new ParserWithExamples[String]( val parserWithExamples: Parser[String] = new ParserWithExamples[String](
colorParser, colorParser,
FixedSetExamples(examples), FixedSetExamples(examples),
maxNumberOfExamples, maxNumberOfExamples,
removeInvalidExamples removeInvalidExamples
) )
} }
case class GrowableSourceOfExamples() extends Iterable[String] { case class GrowableSourceOfExamples() extends Iterable[String] {
private var numberOfIteratedElements: Int = 0 private var numberOfIteratedElements: Int = 0
override def iterator: Iterator[String] = { override def iterator: Iterator[String] = {
new Iterator[String] { new Iterator[String] {
var currentElement = 0 var currentElement = 0
override def next(): String = { override def next(): String = {
currentElement += 1 currentElement += 1
numberOfIteratedElements = Math.max(currentElement, numberOfIteratedElements) numberOfIteratedElements = Math.max(currentElement, numberOfIteratedElements)
numberOfIteratedElements.toString numberOfIteratedElements.toString
} }
override def hasNext: Boolean = true override def hasNext: Boolean = true
} }
} }
override def size: Int = numberOfIteratedElements override def size: Int = numberOfIteratedElements
} }
} }

View File

@ -2,90 +2,85 @@ package sbt
import org.scalacheck._ import org.scalacheck._
import Prop._ 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") object Escapes extends Properties("Escapes") {
{ property("genTerminator only generates terminators") =
property("genTerminator only generates terminators") = forAllNoShrink(genTerminator) { (c: Char) => isEscapeTerminator(c) }
forAllNoShrink(genTerminator) { (c: Char) => isEscapeTerminator(c) }
property("genWithoutTerminator only generates terminators") = property("genWithoutTerminator only generates terminators") =
forAllNoShrink(genWithoutTerminator) { (s: String) => forAllNoShrink(genWithoutTerminator) { (s: String) =>
s.forall { c => !isEscapeTerminator(c) } s.forall { c => !isEscapeTerminator(c) }
} }
property("hasEscapeSequence is false when no escape character is present") = forAllNoShrink(genWithoutEscape) { (s: String) => property("hasEscapeSequence is false when no escape character is present") = forAllNoShrink(genWithoutEscape) { (s: String) =>
!hasEscapeSequence(s) !hasEscapeSequence(s)
} }
property("hasEscapeSequence is true when escape character is present") = forAllNoShrink(genWithRandomEscapes) { (s: String) => property("hasEscapeSequence is true when escape character is present") = forAllNoShrink(genWithRandomEscapes) { (s: String) =>
hasEscapeSequence(s) hasEscapeSequence(s)
} }
property("removeEscapeSequences is the identity when no escape character is present") = forAllNoShrink(genWithoutEscape) { (s: String) => property("removeEscapeSequences is the identity when no escape character is present") = forAllNoShrink(genWithoutEscape) { (s: String) =>
val removed: String = removeEscapeSequences(s) val removed: String = removeEscapeSequences(s)
("Escape sequence removed: '" + removed + "'") |: ("Escape sequence removed: '" + removed + "'") |:
(removed == s) (removed == s)
} }
property("No escape characters remain after removeEscapeSequences") = forAll { (s: String) => property("No escape characters remain after removeEscapeSequences") = forAll { (s: String) =>
val removed: String = removeEscapeSequences(s) val removed: String = removeEscapeSequences(s)
("Escape sequence removed: '" + removed + "'") |: ("Escape sequence removed: '" + removed + "'") |:
!hasEscapeSequence(removed) !hasEscapeSequence(removed)
} }
property("removeEscapeSequences returns string without escape sequences") = property("removeEscapeSequences returns string without escape sequences") =
forAllNoShrink( genWithoutEscape, genEscapePairs ) { (start: String, escapes: List[EscapeAndNot]) => forAllNoShrink(genWithoutEscape, genEscapePairs) { (start: String, escapes: List[EscapeAndNot]) =>
val withEscapes: String = start + escapes.map { ean => ean.escape.makeString + ean.notEscape } val withEscapes: String = start + escapes.map { ean => ean.escape.makeString + ean.notEscape }
val removed: String = removeEscapeSequences(withEscapes) val removed: String = removeEscapeSequences(withEscapes)
val original = start + escapes.map(_.notEscape) val original = start + escapes.map(_.notEscape)
("Input string with escapes: '" + withEscapes + "'") |: ("Input string with escapes: '" + withEscapes + "'") |:
("Escapes removed '" + removed + "'") |: ("Escapes removed '" + removed + "'") |:
(original == removed) (original == removed)
} }
final case class EscapeAndNot(escape: EscapeSequence, notEscape: String) final case class EscapeAndNot(escape: EscapeSequence, notEscape: String)
final case class EscapeSequence(content: String, terminator: Char) final case class EscapeSequence(content: String, terminator: Char) {
{ assert(content.forall(c => !isEscapeTerminator(c)), "Escape sequence content contains an escape terminator: '" + content + "'")
assert( content.forall(c => !isEscapeTerminator(c) ), "Escape sequence content contains an escape terminator: '" + content + "'" ) assert(isEscapeTerminator(terminator))
assert( isEscapeTerminator(terminator) ) def makeString: String = ESC + content + terminator
def makeString: String = ESC + content + terminator }
} private[this] def noEscape(s: String): String = s.replace(ESC, ' ')
private[this] def noEscape(s: String): String = s.replace(ESC, ' ')
lazy val genEscapeSequence: Gen[EscapeSequence] = oneOf(genKnownSequence, genArbitraryEscapeSequence) lazy val genEscapeSequence: Gen[EscapeSequence] = oneOf(genKnownSequence, genArbitraryEscapeSequence)
lazy val genEscapePair: Gen[EscapeAndNot] = for(esc <- genEscapeSequence; not <- genWithoutEscape) yield EscapeAndNot(esc, not) lazy val genEscapePair: Gen[EscapeAndNot] = for (esc <- genEscapeSequence; not <- genWithoutEscape) yield EscapeAndNot(esc, not)
lazy val genEscapePairs: Gen[List[EscapeAndNot]] = listOf(genEscapePair) lazy val genEscapePairs: Gen[List[EscapeAndNot]] = listOf(genEscapePair)
lazy val genArbitraryEscapeSequence: Gen[EscapeSequence] = lazy val genArbitraryEscapeSequence: Gen[EscapeSequence] =
for(content <- genWithoutTerminator; term <- genTerminator) yield for (content <- genWithoutTerminator; term <- genTerminator) yield new EscapeSequence(content, term)
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 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] = def toEscapeSequence(s: String): EscapeSequence = EscapeSequence(s.init, s.last)
for(txt <- 0 to 8; fg <- 30 to 37; bg <- 40 to 47) yield
txt.toString + ";" + fg.toString + ";" + bg.toString + "m"
lazy val resetMode = setModeLike('I') lazy val misc = Seq("14;23H", "5;3f", "2A", "94B", "19C", "85D", "s", "u", "2J", "K")
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 genTerminator: Gen[Char] = Gen.choose('@', '~') lazy val setGraphicsMode: Seq[String] =
lazy val genWithoutEscape: Gen[String] = genRawString.map(noEscape) 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] = lazy val resetMode = setModeLike('I')
for(ls <- listOf(genRawString); end <- genRawString) yield lazy val setMode = setModeLike('h')
ls.mkString("", ESC.toString, ESC.toString + end) 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
} }

View File

@ -4,157 +4,147 @@
package sbt package sbt
import org.scalacheck._ import org.scalacheck._
import Arbitrary.{arbitrary => arb, _} import Arbitrary.{ arbitrary => arb, _ }
import Gen.{listOfN, oneOf} import Gen.{ listOfN, oneOf }
import Prop._ import Prop._
import java.io.Writer import java.io.Writer
object LogWriterTest extends Properties("Log Writer") object LogWriterTest extends Properties("Log Writer") {
{ final val MaxLines = 100
final val MaxLines = 100 final val MaxSegments = 10
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. */ * Each line, determined by the specified newline separator, must be logged at the correct logging level. */
property("properly logged") = forAll { (output: Output, newLine: NewLine) => property("properly logged") = forAll { (output: Output, newLine: NewLine) =>
import output.{lines, level} import output.{ lines, level }
val log = new RecordingLogger val log = new RecordingLogger
val writer = new LoggerWriter(log, Some(level), newLine.str) val writer = new LoggerWriter(log, Some(level), newLine.str)
logLines(writer, lines, newLine.str) logLines(writer, lines, newLine.str)
val events = log.getEvents val events = log.getEvents
("Recorded:\n" + events.map(show).mkString("\n")) |: ("Recorded:\n" + events.map(show).mkString("\n")) |:
check( toLines(lines), events, level) 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*/ * Displays a LogEvent in a useful format for debugging. In particular, we are only interested in `Log` types
def show(event: LogEvent): String = * and non-printable characters should be escaped
event match */
{ def show(event: LogEvent): String =
case l: Log => "Log('" + Escape(l.msg) + "', " + l.level + ")" event match {
case _ => "Not Log" 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)*/ * Writes the given lines to the Writer. `lines` is taken to be a list of lines, which are
def logLines(writer: Writer, lines: List[List[ToLog]], newLine: String) * represented as separately written segments (ToLog instances). ToLog.`byCharacter`
{ * indicates whether to write the segment by character (true) or all at once (false)
for(line <- lines; section <- line) */
{ def logLines(writer: Writer, lines: List[List[ToLog]], newLine: String) {
val content = section.content for (line <- lines; section <- line) {
val normalized = Escape.newline(content, newLine) val content = section.content
if(section.byCharacter) val normalized = Escape.newline(content, newLine)
normalized.foreach { c => writer.write(c.toInt) } if (section.byCharacter)
else normalized.foreach { c => writer.write(c.toInt) }
writer.write(normalized) else
} writer.write(normalized)
writer.flush() }
} 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] = /** Converts the given lines in segments to lines as Strings for checking the results of the test.*/
lines.map(_.map(_.contentOnly).mkString) def toLines(lines: List[List[ToLog]]): List[String] =
/** Checks that the expected `lines` were recorded as `events` at level `Lvl`.*/ lines.map(_.map(_.contentOnly).mkString)
def check(lines: List[String], events: List[LogEvent], Lvl: Level.Value): Boolean = /** Checks that the expected `lines` were recorded as `events` at level `Lvl`.*/
(lines zip events) forall { def check(lines: List[String], events: List[LogEvent], Lvl: Level.Value): Boolean =
case (line, log : Log) => log.level == Lvl && line == log.msg (lines zip events) forall {
case _ => false case (line, log: Log) => log.level == Lvl && line == log.msg
} case _ => false
}
/* The following are implicit generators to build up a write sequence.
/* The following are implicit generators to build up a write sequence.
* ToLog represents a written segment. NewLine represents one of the possible * ToLog represents a written segment. NewLine represents one of the possible
* newline separators. A List[ToLog] represents a full line and always includes a * 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 * final ToLog with a trailing '\n'. Newline characters are otherwise not present in
* the `content` of a ToLog instance.*/ * 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] = implicit lazy val arbOut: Arbitrary[Output] = Arbitrary(genOutput)
for(c <- content.arbitrary; by <- byChar.arbitrary) yield implicit lazy val arbLog: Arbitrary[ToLog] = Arbitrary(genLog)
{ implicit lazy val arbLine: Arbitrary[List[ToLog]] = Arbitrary(genLine)
assert(c != null) implicit lazy val arbNewLine: Arbitrary[NewLine] = Arbitrary(genNewLine)
new ToLog(removeNewlines(c), by) implicit lazy val arbLevel: Arbitrary[Level.Value] = Arbitrary(genLevel)
}
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]] = implicit def genLine(implicit logG: Gen[ToLog]): Gen[List[ToLog]] =
Gen.choose(0, max) flatMap { sz => listOfN(sz, content.arbitrary) } 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*/ /* Helper classes*/
final class Output(val lines: List[List[ToLog]], val level: Level.Value) extends NotNull final class Output(val lines: List[List[ToLog]], val level: Level.Value) extends NotNull {
{ override def toString =
override def toString = "Level: " + level + "\n" + lines.map(_.mkString).mkString("\n")
"Level: " + level + "\n" + lines.map(_.mkString).mkString("\n")
} }
final class NewLine(val str: String) extends NotNull final class NewLine(val str: String) extends NotNull {
{ override def toString = Escape(str)
override def toString = Escape(str)
} }
final class ToLog(val content: String, val byCharacter: Boolean) extends NotNull final class ToLog(val content: String, val byCharacter: Boolean) extends NotNull {
{ def contentOnly = Escape.newline(content, "")
def contentOnly = Escape.newline(content, "") override def toString = if (content.isEmpty) "" else "ToLog('" + Escape(contentOnly) + "', " + byCharacter + ")"
override def toString = if(content.isEmpty) "" else "ToLog('" + Escape(contentOnly) + "', " + byCharacter + ")"
} }
/** Defines some utility methods for escaping unprintable characters.*/ /** Defines some utility methods for escaping unprintable characters.*/
object Escape object Escape {
{ /** Escapes characters with code less than 20 by printing them as unicode escapes.*/
/** Escapes characters with code less than 20 by printing them as unicode escapes.*/ def apply(s: String): String =
def apply(s: String): String = {
{ val builder = new StringBuilder(s.length)
val builder = new StringBuilder(s.length) for (c <- s) {
for(c <- s) def escaped = pad(c.toInt.toHexString.toUpperCase, 4, '0')
{ if (c < 20) builder.append("\\u").append(escaped) else builder.append(c)
def escaped = pad(c.toInt.toHexString.toUpperCase, 4, '0') }
if(c < 20) builder.append("\\u").append(escaped) else builder.append(c) builder.toString
} }
builder.toString def pad(s: String, minLength: Int, extra: Char) =
} {
def pad(s: String, minLength: Int, extra: Char) = val diff = minLength - s.length
{ if (diff <= 0) s else List.make(diff, extra).mkString("", "", s)
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 =
/** Replaces a \n character at the end of a string `s` with `nl`.*/ if (s.endsWith("\n")) s.substring(0, s.length - 1) + nl else s
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.*/ /** Records logging events for later retrieval.*/
final class RecordingLogger extends BasicLogger final class RecordingLogger extends BasicLogger {
{ private var events: List[LogEvent] = Nil
private var events: List[LogEvent] = Nil
def getEvents = events.reverse
def getEvents = events.reverse
override def ansiCodesSupported = true
override def ansiCodesSupported = true def trace(t: => Throwable) { events ::= new Trace(t) }
def trace(t: => Throwable) { events ::= new Trace(t) } def log(level: Level.Value, message: => String) { events ::= new Log(level, message) }
def log(level: Level.Value, message: => String) { events ::= new Log(level, message) } def success(message: => String) { events ::= new Success(message) }
def success(message: => String) { events ::= new Success(message) } def logAll(es: Seq[LogEvent]) { events :::= es.toList }
def logAll(es: Seq[LogEvent]) { events :::= es.toList } def control(event: ControlEvent.Value, message: => String) { events ::= new ControlEvent(event, message) }
def control(event: ControlEvent.Value, message: => String) { events ::= new ControlEvent(event, message) }
} }

View File

@ -1,11 +1,10 @@
package sbt package sbt
object TestLogger object TestLogger {
{ def apply[T](f: Logger => T): T =
def apply[T](f: Logger => T): T = {
{ val log = new BufferedLogger(ConsoleLogger())
val log = new BufferedLogger(ConsoleLogger()) log.setLevel(Level.Debug)
log.setLevel(Level.Debug) log.bufferQuietly(f(log))
log.bufferQuietly(f(log)) }
}
} }

View File

@ -1,117 +1,115 @@
package sbt package sbt
package logic package logic
import org.scalacheck._ import org.scalacheck._
import Prop.secure import Prop.secure
import Logic.{LogicException, Matched} import Logic.{ LogicException, Matched }
object LogicTest extends Properties("Logic") object LogicTest extends Properties("Logic") {
{ import TestClauses._
import TestClauses._
property("Handles trivial resolution.") = secure( expect(trivial, Set(A) ) ) property("Handles trivial resolution.") = secure(expect(trivial, Set(A)))
property("Handles less trivial resolution.") = secure( expect(lessTrivial, Set(B,A,D)) ) 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 cycles without negation") = secure(expect(cycles, Set(F, A, B)))
property("Handles basic exclusion.") = secure( expect(excludedPos, Set()) ) property("Handles basic exclusion.") = secure(expect(excludedPos, Set()))
property("Handles exclusion of head proved by negation.") = secure( expect(excludedNeg, 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 // 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("Properly orders results.") = secure(expect(ordering, Set(B, A, C, E, F)))
property("Detects cyclic negation") = secure( property("Detects cyclic negation") = secure(
Logic.reduceAll(badClauses, Set()) match { Logic.reduceAll(badClauses, Set()) match {
case Right(res) => false case Right(res) => false
case Left(err: Logic.CyclicNegation) => true case Left(err: Logic.CyclicNegation) => true
case Left(err) => error(s"Expected cyclic error, got: $err") case Left(err) => error(s"Expected cyclic error, got: $err")
} }
) )
def expect(result: Either[LogicException, Matched], expected: Set[Atom]) = result match { def expect(result: Either[LogicException, Matched], expected: Set[Atom]) = result match {
case Left(err) => false case Left(err) => false
case Right(res) => case Right(res) =>
val actual = res.provenSet val actual = res.provenSet
(actual == expected) || error(s"Expected to prove $expected, but actually proved $actual") (actual == expected) || error(s"Expected to prove $expected, but actually proved $actual")
} }
} }
object TestClauses object TestClauses {
{
val A = Atom("A") val A = Atom("A")
val B = Atom("B") val B = Atom("B")
val C = Atom("C") val C = Atom("C")
val D = Atom("D") val D = Atom("D")
val E = Atom("E") val E = Atom("E")
val F = Atom("F") val F = Atom("F")
val G = Atom("G") val G = Atom("G")
val clauses = val clauses =
A.proves(B) :: A.proves(B) ::
A.proves(F) :: A.proves(F) ::
B.proves(F) :: B.proves(F) ::
F.proves(A) :: F.proves(A) ::
(!C).proves(F) :: (!C).proves(F) ::
D.proves(C) :: D.proves(C) ::
C.proves(D) :: C.proves(D) ::
Nil Nil
val cycles = Logic.reduceAll(clauses, Set()) val cycles = Logic.reduceAll(clauses, Set())
val badClauses = val badClauses =
A.proves(D) :: A.proves(D) ::
clauses clauses
val excludedNeg = { val excludedNeg = {
val cs = val cs =
(!A).proves(B) :: (!A).proves(B) ::
Nil Nil
val init = val init =
(!A) :: (!A) ::
(!B) :: (!B) ::
Nil Nil
Logic.reduceAll(cs, init.toSet) Logic.reduceAll(cs, init.toSet)
} }
val excludedPos = { val excludedPos = {
val cs = val cs =
A.proves(B) :: A.proves(B) ::
Nil Nil
val init = val init =
A :: A ::
(!B) :: (!B) ::
Nil Nil
Logic.reduceAll(cs, init.toSet) Logic.reduceAll(cs, init.toSet)
} }
val trivial = { val trivial = {
val cs = val cs =
Formula.True.proves(A) :: Formula.True.proves(A) ::
Nil Nil
Logic.reduceAll(cs, Set.empty) Logic.reduceAll(cs, Set.empty)
} }
val lessTrivial = { val lessTrivial = {
val cs = val cs =
Formula.True.proves(A) :: Formula.True.proves(A) ::
Formula.True.proves(B) :: Formula.True.proves(B) ::
(A && B && (!C)).proves(D) :: (A && B && (!C)).proves(D) ::
Nil Nil
Logic.reduceAll(cs, Set()) Logic.reduceAll(cs, Set())
} }
val ordering = { val ordering = {
val cs = val cs =
E.proves(F) :: E.proves(F) ::
(C && !D).proves(E) :: (C && !D).proves(E) ::
(A && B).proves(C) :: (A && B).proves(C) ::
Nil Nil
Logic.reduceAll(cs, Set(A,B)) Logic.reduceAll(cs, Set(A, B))
} }
def all { def all {
println(s"Cycles: $cycles") println(s"Cycles: $cycles")
println(s"xNeg: $excludedNeg") println(s"xNeg: $excludedNeg")
println(s"xPos: $excludedPos") println(s"xPos: $excludedPos")
println(s"trivial: $trivial") println(s"trivial: $trivial")
println(s"lessTrivial: $lessTrivial") println(s"lessTrivial: $lessTrivial")
println(s"ordering: $ordering") println(s"ordering: $ordering")
} }
} }

View File

@ -1,133 +1,131 @@
package sbt package sbt
import java.io.File import java.io.File
import org.scalacheck.{Arbitrary, Gen, Prop, Properties} import org.scalacheck.{ Arbitrary, Gen, Prop, Properties }
import Prop._ import Prop._
import Process._ import Process._
object ProcessSpecification extends Properties("Process I/O") object ProcessSpecification extends Properties("Process I/O") {
{ implicit val exitCodeArb: Arbitrary[Array[Byte]] = Arbitrary(
implicit val exitCodeArb: Arbitrary[Array[Byte]] = Arbitrary( for (
for(size <- Gen.choose(0, 10); size <- Gen.choose(0, 10);
l <- Gen.listOfN[Byte](size, Arbitrary.arbByte.arbitrary)) l <- Gen.listOfN[Byte](size, Arbitrary.arbByte.arbitrary)
yield ) yield l.toArray
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)(_ #|| _)(_ || _)) property("#|| correct") = forAll( (exitCodes: Array[Byte]) => checkBinary(exitCodes)(_ #|| _)(_ || _))
property("### correct") = forAll( (exitCodes: Array[Byte]) => checkBinary(exitCodes)(_ ### _)( (x,latest) => latest))*/ property("### correct") = forAll( (exitCodes: Array[Byte]) => checkBinary(exitCodes)(_ ### _)( (x,latest) => latest))*/
property("Pipe to output file") = forAll( (data: Array[Byte]) => checkFileOut(data)) property("Pipe to output file") = forAll((data: Array[Byte]) => checkFileOut(data))
property("Pipe from input file") = forAll( (data: Array[Byte]) => checkFileIn(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") = 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 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 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 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) = private def checkBinary(codes: Array[Byte])(reduceProcesses: (ProcessBuilder, ProcessBuilder) => ProcessBuilder)(reduceExit: (Boolean, Boolean) => Boolean) =
{ {
(codes.length > 1) ==> (codes.length > 1) ==>
{ {
val unsignedCodes = codes.map(unsigned) val unsignedCodes = codes.map(unsigned)
val exitCode = unsignedCodes.map(code => Process(process("sbt.exit " + code))).reduceLeft(reduceProcesses) ! val exitCode = unsignedCodes.map(code => Process(process("sbt.exit " + code))).reduceLeft(reduceProcesses) !
val expectedExitCode = unsignedCodes.map(toBoolean).reduceLeft(reduceExit) val expectedExitCode = unsignedCodes.map(toBoolean).reduceLeft(reduceExit)
toBoolean(exitCode) == expectedExitCode toBoolean(exitCode) == expectedExitCode
} }
} }
private def toBoolean(exitCode: Int) = exitCode == 0 private def toBoolean(exitCode: Int) = exitCode == 0
private def checkExit(code: Byte) = private def checkExit(code: Byte) =
{ {
val exitCode = unsigned(code) val exitCode = unsigned(code)
(process("sbt.exit " + exitCode) !) == exitCode (process("sbt.exit " + exitCode) !) == exitCode
} }
private def checkFileOut(data: Array[Byte]) = private def checkFileOut(data: Array[Byte]) =
{ {
withData(data) { (temporaryFile, temporaryFile2) => withData(data) { (temporaryFile, temporaryFile2) =>
val catCommand = process("sbt.cat " + temporaryFile.getAbsolutePath) val catCommand = process("sbt.cat " + temporaryFile.getAbsolutePath)
catCommand #> temporaryFile2 catCommand #> temporaryFile2
} }
} }
private def checkFileIn(data: Array[Byte]) = private def checkFileIn(data: Array[Byte]) =
{ {
withData(data) { (temporaryFile, temporaryFile2) => withData(data) { (temporaryFile, temporaryFile2) =>
val catCommand = process("sbt.cat") val catCommand = process("sbt.cat")
temporaryFile #> catCommand #> temporaryFile2 temporaryFile #> catCommand #> temporaryFile2
} }
} }
private def checkPipe(data: Array[Byte]) = private def checkPipe(data: Array[Byte]) =
{ {
withData(data) { (temporaryFile, temporaryFile2) => withData(data) { (temporaryFile, temporaryFile2) =>
val catCommand = process("sbt.cat") val catCommand = process("sbt.cat")
temporaryFile #> catCommand #| catCommand #> temporaryFile2 temporaryFile #> catCommand #| catCommand #> temporaryFile2
} }
} }
private def checkPipeExit(data: Array[Byte], code: Byte) = private def checkPipeExit(data: Array[Byte], code: Byte) =
withTempFiles { (a,b) => withTempFiles { (a, b) =>
IO.write(a, data) IO.write(a, data)
val catCommand = process("sbt.cat") val catCommand = process("sbt.cat")
val exitCommand = process(s"sbt.exit $code") val exitCommand = process(s"sbt.exit $code")
val exit = (a #> exitCommand #| catCommand #> b).! val exit = (a #> exitCommand #| catCommand #> b).!
(s"Exit code: $exit") |: (s"Exit code: $exit") |:
(s"Output file length: ${b.length}") |: (s"Output file length: ${b.length}") |:
(exit == 0) && (exit == 0) &&
(b.length == 0) (b.length == 0)
} }
private def checkFileOutExit(data: Array[Byte], exitCode: Byte) = private def checkFileOutExit(data: Array[Byte], exitCode: Byte) =
withTempFiles { (a,b) => withTempFiles { (a, b) =>
IO.write(a, data) IO.write(a, data)
val code = unsigned(exitCode) val code = unsigned(exitCode)
val command = process(s"sbt.exit $code") val command = process(s"sbt.exit $code")
val exit = (a #> command #> b).! val exit = (a #> command #> b).!
(s"Exit code: $exit, expected: $code") |: (s"Exit code: $exit, expected: $code") |:
(s"Output file length: ${b.length}") |: (s"Output file length: ${b.length}") |:
(exit == code) && (exit == code) &&
(b.length == 0) (b.length == 0)
} }
private def checkFileInExit(data: Array[Byte], exitCode: Byte) = private def checkFileInExit(data: Array[Byte], exitCode: Byte) =
withTempFiles { (a,b) => withTempFiles { (a, b) =>
IO.write(a, data) IO.write(a, data)
val code = unsigned(exitCode) val code = unsigned(exitCode)
val command = process(s"sbt.exit $code") val command = process(s"sbt.exit $code")
val exit = (a #> command).! val exit = (a #> command).!
(s"Exit code: $exit, expected: $code") |: (s"Exit code: $exit, expected: $code") |:
(exit == code) (exit == code)
} }
private def temp() = File.createTempFile("sbt", "") private def temp() = File.createTempFile("sbt", "")
private def withData(data: Array[Byte])(f: (File, File) => ProcessBuilder) = private def withData(data: Array[Byte])(f: (File, File) => ProcessBuilder) =
withTempFiles { (a, b) => withTempFiles { (a, b) =>
IO.write(a, data) IO.write(a, data)
val process = f(a, b) val process = f(a, b)
( process ! ) == 0 && sameFiles(a, b) (process !) == 0 && sameFiles(a, b)
} }
private def sameFiles(a: File, b: File) = private def sameFiles(a: File, b: File) =
IO.readBytes(a) sameElements IO.readBytes(b) IO.readBytes(a) sameElements IO.readBytes(b)
private def withTempFiles[T](f: (File, File) => T): T = private def withTempFiles[T](f: (File, File) => T): T =
{ {
val temporaryFile1 = temp() val temporaryFile1 = temp()
val temporaryFile2 = temp() val temporaryFile2 = temp()
try f(temporaryFile1, temporaryFile2) try f(temporaryFile1, temporaryFile2)
finally finally {
{ temporaryFile1.delete()
temporaryFile1.delete() temporaryFile2.delete()
temporaryFile2.delete() }
} }
} private def unsigned(b: Int): Int = ((b: Int) + 256) % 256
private def unsigned(b: Int): Int = ((b: Int) +256) % 256 private def unsigned(b: Byte): Int = unsigned(b: Int)
private def unsigned(b: Byte): Int = unsigned(b: Int) private def process(command: String) =
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 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) val thisClasspath = List(getSource[Product], getSource[IO.type], getSource[SourceTag]).mkString(File.pathSeparator)
"java -cp " + thisClasspath + " " + command "java -cp " + thisClasspath + " " + command
} }
private def getSource[T : Manifest]: String = private def getSource[T: Manifest]: String =
IO.classLocationFile[T].getAbsolutePath IO.classLocationFile[T].getAbsolutePath
} }
private trait SourceTag private trait SourceTag

View File

@ -1,56 +1,47 @@
package sbt package sbt
import java.io.{File, FileNotFoundException, IOException} import java.io.{ File, FileNotFoundException, IOException }
object exit object exit {
{ def main(args: Array[String]) {
def main(args: Array[String]) System.exit(java.lang.Integer.parseInt(args(0)))
{ }
System.exit(java.lang.Integer.parseInt(args(0)))
}
} }
object cat object cat {
{ def main(args: Array[String]) {
def main(args: Array[String]) try {
{ if (args.length == 0)
try { IO.transfer(System.in, System.out)
if(args.length == 0) else
IO.transfer(System.in, System.out) catFiles(args.toList)
else System.exit(0)
catFiles(args.toList) } catch {
System.exit(0) case e =>
} catch { e.printStackTrace()
case e => System.err.println("Error: " + e.toString)
e.printStackTrace() System.exit(1)
System.err.println("Error: " + e.toString) }
System.exit(1) }
} private def catFiles(filenames: List[String]): Option[String] =
} {
private def catFiles(filenames: List[String]): Option[String] = filenames match {
{ case head :: tail =>
filenames match val file = new File(head)
{ if (file.isDirectory)
case head :: tail => throw new IOException("Is directory: " + file)
val file = new File(head) else if (file.exists) {
if(file.isDirectory) Using.fileInputStream(file) { stream =>
throw new IOException("Is directory: " + file) IO.transfer(stream, System.out)
else if(file.exists) }
{ catFiles(tail)
Using.fileInputStream(file) { stream => } else
IO.transfer(stream, System.out) throw new FileNotFoundException("No such file or directory: " + file)
} case Nil => None
catFiles(tail) }
} }
else
throw new FileNotFoundException("No such file or directory: " + file)
case Nil => None
}
}
} }
object echo object echo {
{ def main(args: Array[String]) {
def main(args: Array[String]) System.out.println(args.mkString(" "))
{ }
System.out.println(args.mkString(" "))
}
} }

View File

@ -6,79 +6,79 @@ package sbt
import org.scalacheck._ import org.scalacheck._
import Prop._ import Prop._
object RelationTest extends Properties("Relation") object RelationTest extends Properties("Relation") {
{ property("Added entry check") = forAll { (pairs: List[(Int, Double)]) =>
property("Added entry check") = forAll { (pairs: List[(Int, Double)]) => val r = Relation.empty[Int, Double] ++ pairs
val r = Relation.empty[Int, Double] ++ pairs check(r, pairs)
check(r, pairs) }
} def check(r: Relation[Int, Double], pairs: Seq[(Int, Double)]) =
def check(r: Relation[Int, Double], pairs: Seq[(Int, Double)]) = {
{ val _1s = pairs.map(_._1).toSet
val _1s = pairs.map(_._1).toSet val _2s = pairs.map(_._2).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) ) )
}
}
property("Groups correctly") = forAll { (entries: List[(Int, Double)], randomInt: Int) => r._1s == _1s && r.forwardMap.keySet == _1s &&
val splitInto = math.abs(randomInt) % 10 + 1 // Split into 1-10 groups. r._2s == _2s && r.reverseMap.keySet == _2s &&
val rel = Relation.empty[Int, Double] ++ entries pairs.forall {
val grouped = rel groupBy (_._1 % splitInto) case (a, b) =>
all(grouped.toSeq) { (r.forward(a) contains b) &&
case (k, rel_k) => rel_k._1s forall { _ % splitInto == k } (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)]) => property("Computes size correctly") = forAll { (entries: List[(Int, Double)]) =>
val rel = Relation.empty[Int, Double] ++ entries 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 val computed = rel.size
"Expected size: %d. Computed size: %d.".format(expected, computed) |: expected == computed "Expected size: %d. Computed size: %d.".format(expected, computed) |: expected == computed
} }
def all[T](s: Seq[T])(p: T => Prop): Prop = def all[T](s: Seq[T])(p: T => Prop): Prop =
if(s.isEmpty) true else s.map(p).reduceLeft(_ && _) if (s.isEmpty) true else s.map(p).reduceLeft(_ && _)
} }
object EmptyRelationTest extends Properties("Empty relation") object EmptyRelationTest extends Properties("Empty relation") {
{ lazy val e = Relation.empty[Int, Double]
lazy val e = Relation.empty[Int, Double]
property("Forward empty") = forAll { (i: Int) => e.forward(i).isEmpty } property("Forward empty") = forAll { (i: Int) => e.forward(i).isEmpty }
property("Reverse empty") = forAll { (i: Double) => e.reverse(i).isEmpty } property("Reverse empty") = forAll { (i: Double) => e.reverse(i).isEmpty }
property("Forward map empty") = e.forwardMap.isEmpty property("Forward map empty") = e.forwardMap.isEmpty
property("Reverse map empty") = e.reverseMap.isEmpty property("Reverse map empty") = e.reverseMap.isEmpty
property("_1 empty") = e._1s.isEmpty property("_1 empty") = e._1s.isEmpty
property("_2 empty") = e._2s.isEmpty property("_2 empty") = e._2s.isEmpty
} }