Added tests for FileExamples. Improved the file-searching in FileExamples.

This commit is contained in:
Matej Urbas 2014-04-16 08:36:27 +01:00
parent 757fe4228d
commit cbfd3f1c08
2 changed files with 108 additions and 15 deletions

View File

@ -1,6 +1,7 @@
package sbt.complete
import java.io.File
import sbt.IO._
/**
* These sources of examples are used in parsers for user input completion. An example of such a source is the
@ -27,12 +28,15 @@ trait ExampleSource
* A convenience example source that wraps any collection of strings into a source of examples.
* @param examples the examples that will be displayed to the user when they press the TAB key.
*/
sealed case class FixedSetExamples(examples: Iterable[String]) extends ExampleSource {
sealed case class FixedSetExamples(examples: Iterable[String]) extends ExampleSource
{
override def withAddedPrefix(addedPrefix: String): ExampleSource = FixedSetExamples(examplesWithRemovedPrefix(addedPrefix))
override def apply(): Iterable[String] = examples
private def examplesWithRemovedPrefix(prefix: String) = examples.collect { case example if example startsWith prefix => example substring prefix.length }
private def examplesWithRemovedPrefix(prefix: String) = examples.collect {
case example if example startsWith prefix => example substring prefix.length
}
}
/**
@ -40,22 +44,19 @@ sealed case class FixedSetExamples(examples: Iterable[String]) extends ExampleSo
* @param base the directory within which this class will search for completion examples.
* @param prefix the part of the path already written by the user.
*/
class FileExamples(base: File, prefix: String = "") extends ExampleSource {
private val relativizedPrefix: String = "." + File.separator + prefix
override def apply(): Iterable[String] = files(base).map(_.toString.substring(relativizedPrefix.length))
class FileExamples(base: File, prefix: String = "") extends ExampleSource
{
override def apply(): Stream[String] = files(base).map(_ substring prefix.length)
override def withAddedPrefix(addedPrefix: String): FileExamples = new FileExamples(base, prefix + addedPrefix)
protected def fileStartsWithPrefix(path: File): Boolean = path.toString.startsWith(relativizedPrefix)
protected def directoryStartsWithPrefix(path: File): Boolean = {
val pathString = path.toString
pathString.startsWith(relativizedPrefix) || relativizedPrefix.startsWith(pathString)
protected def files(directory: File): Stream[String] = {
val childPaths = directory.listFiles().toStream
val prefixedDirectChildPaths = childPaths.map(relativize(base, _).get).filter(_ startsWith prefix)
val dirsToRecurseInto = childPaths.filter(_.isDirectory).map(relativize(base, _).get).filter(dirStartsWithPrefix)
prefixedDirectChildPaths append dirsToRecurseInto.flatMap(dir => files(new File(base, dir)))
}
protected def files(directory: File): Iterable[File] = {
val (subDirectories, filesOnly) = directory.listFiles().toStream.partition(_.isDirectory)
filesOnly.filter(fileStartsWithPrefix) ++ subDirectories.filter(directoryStartsWithPrefix).flatMap(files)
}
private def dirStartsWithPrefix(relativizedPath: String): Boolean =
(relativizedPath startsWith prefix) || (prefix startsWith relativizedPath)
}

View File

@ -0,0 +1,92 @@
package sbt.complete
import org.specs2.mutable.Specification
import org.specs2.specification.Scope
import sbt.IO.withTemporaryDirectory
import java.io.File
import sbt.IO._
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 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 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)
}
}
"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("")
}
}
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 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
}
}
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())
// 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, _))
}
}