Skip to content

Instantly share code, notes, and snippets.

@charleso
Last active June 30, 2020 03:19
Show Gist options
  • Save charleso/51b6950ecba6c7fd30b49449582d8f48 to your computer and use it in GitHub Desktop.
Save charleso/51b6950ecba6c7fd30b49449582d8f48 to your computer and use it in GitHub Desktop.
Generator of random file trees for Scalacheck
package com.example
import java.nio.file._
import org.scalacheck._, Gen._, Prop._
import scala.reflect.io.Directory
sealed trait FileTree
object FileTree {
case class File(contents: List[Byte]) extends FileTree
case class Dir(children: Map[String, FileTree]) extends FileTree
}
object ArbitraryFileTreeNew extends Properties("FileTree") {
def genFileTreeDir: Gen[FileTree] =
listOf(for {
name <- nonEmptyListOf(alphaLowerChar).map(_.mkString)
ft <- Gen.size.flatMap(s => Gen.resize(s / 2, genFileTree))
} yield name -> ft).map(_.toMap).map(FileTree.Dir(_))
def genFileTree: Gen[FileTree] =
Gen.size.flatMap(s =>
frequency(
(s / 20) -> genFileTreeDir
, 10 -> listOf(Arbitrary.arbByte.arbitrary).map(FileTree.File(_))
)
)
def createFileTree(path: Path, tree: FileTree): Unit =
tree match {
case FileTree.File(content) =>
Files.write(path, content.toArray)
()
case FileTree.Dir(children) =>
Files.createDirectories(path)
children.foreach { case (nam, child) =>
createFileTree(path.resolve(nam), child)
}
}
def withFiles[A](files: FileTree)(f: Path => A): A = {
val dir = Files.createTempDirectory("test")
try {
createFileTree(dir, files)
f(dir)
} finally {
new Directory(dir.toFile).deleteRecursively()
()
}
}
property("test1") = {
forAll(genFileTreeDir) { fs =>
withFiles(fs) { dir =>
// TODO Use dir here
true
}
}
}
}
package com.example
import java.nio.file._
import org.scalacheck._, Gen._, Prop._
import scala.reflect.io.Directory
/**
* Similar to https://gist.github.com/odisseus/4e1e29782dc5f2e94c50e58b0676e623.
* The difference in this example is that we don't do any side-effects in Gen.
*/
object ArbitraryFileTree extends Properties("FileTree") {
def genFile: Gen[(Path, List[Byte])] =
for {
// Avoid generating files that are too deep
// FIXME Ideally we should trim in a better way than this
depth <- choose(0, 5)
dir <- listOfN(
depth
// FIXME Append the unlikely prefix of '_d' so that we don't have to stuff around
// later having to worry about clashes between a file and directory with the same name
// There are better ways to do this
, nonEmptyListOf(alphaLowerChar).map(_.mkString).map("_d" + _)
).map(_.mkString("/"))
filename <- nonEmptyListOf(alphaLowerChar).map(_.mkString)
content <- listOf(Arbitrary.arbByte.arbitrary)
} yield Paths.get(dir).resolve(filename) -> content
def genFiles: Gen[Map[Path, List[Byte]]] =
// Note: sometimes we'll generate the same file path but we don't care
listOf(genFile).map(_.toMap)
def withFiles[A](files: Map[Path, List[Byte]])(f: Path => A): A = {
val dir = Files.createTempDirectory("test")
try {
files.foreach { case (path, content) =>
Files.createDirectories(dir.resolve(path).getParent)
Files.write(dir.resolve(path), content.toArray)
}
f(dir)
} finally {
new Directory(dir.toFile).deleteRecursively()
()
}
}
property("test1") = {
forAll(genFiles) { fs =>
withFiles(fs) { dir =>
// TODO Use dir here
true
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment