package xsbt.boot

import org.scalacheck._
import Prop._
import java.io.File
import sbt.IO.withTemporaryDirectory

/** These mainly test that things work in the uncontested case and that no OverlappingFileLockExceptions occur.
* There is no real locking testing, just the coordination of locking.*/
object LocksTest extends Properties("Locks")
{
	property("Lock in nonexisting directory") = spec {
		withTemporaryDirectory { dir =>
			val lockFile = new File(dir, "doesntexist/lock")
			Locks(lockFile, callTrue)
		}
	}
		
	property("Uncontested re-entrant lock") =  spec {
		withTemporaryDirectory { dir =>
			val lockFile = new File(dir, "lock")
			Locks(lockFile,  callLocked(lockFile)) &&
			Locks(lockFile,  callLocked(lockFile))
		}
	}
		
	property("Uncontested double lock") = spec {
		withTemporaryDirectory { dir =>
			val lockFileA = new File(dir, "lockA")
			val lockFileB = new File(dir, "lockB")
			Locks(lockFileA,  callLocked(lockFileB)) &&
			Locks(lockFileB,  callLocked(lockFileA))
		}
	}
		
	property("Contested single lock") = spec {
		withTemporaryDirectory { dir =>
			val lockFile = new File(dir, "lock")
			forkFold(2000){i => Locks(lockFile, callTrue) }
		}
	}

	private def spec(f: => Boolean): Prop = Prop { _ => Result(if(f) True else False) }
		
	private def call[T](impl: => T) = new java.util.concurrent.Callable[T] { def call = impl }
	private def callLocked(lockFile: File) = call { Locks(lockFile, callTrue) }
	private lazy val callTrue =  call { true }
	
	private def forkFold(n: Int)(impl: Int => Boolean): Boolean =
		(true /: forkWait(n)(impl))(_ && _)
	private def forkWait(n: Int)(impl: Int  => Boolean): Iterable[Boolean] =
	{
		import scala.concurrent.ops.future
		val futures = (0 until n).map { i => future { impl(i) } }
		futures.toList.map(_())
	}
}