/*
* Copyright (c) 2014 Contributor. All rights reserved.
*/
import java.io.{File => JFile, FileInputStream, FileOutputStream}
import java.util.zip.{ZipEntry, ZipOutputStream}
import scala.reflect.io.{Directory, File}
import scala.tools.nsc.util.ClassPath.RootPackage
import scala.tools.nsc.classpath.PackageNameUtils
import scala.tools.nsc.io.Jar
/**
* Generates directories, jars and zip files containing sources and classes
* (the result of a compilation which is executed here)
* and use them as a class- and sourcepath during compilation and running
* created application. At the end everything is cleaned up.
*
* It can test also current, recursive classpath. Just right now we force
* flat classpath to test it also when the recursive one would be set as a default.
*/
object Test {
private implicit class JFileOps(file: JFile) {
def createDir(newDirName: String) = {
val newDir = new JFile(file, newDirName)
newDir.mkdir()
newDir
}
def createSrcFile(newFileName: String) = createFile(newFileName + ".scala")
def createFile(fullFileName: String) = {
val newFile = new JFile(file, fullFileName)
newFile.createNewFile()
newFile
}
def writeAll(text: String): Unit = File(file) writeAll text
def moveContentToZip(zipName: String): Unit = {
val newZip = zipsDir createFile s"$zipName.zip"
val outputStream = new ZipOutputStream(new FileOutputStream(newZip))
def addFileToZip(dirPrefix: String = "")(fileToAdd: JFile): Unit =
if (fileToAdd.isDirectory) {
val dirEntryName = fileToAdd.getName + "/"
outputStream.putNextEntry(new ZipEntry(dirEntryName))
fileToAdd.listFiles() foreach addFileToZip(dirEntryName)
} else {
val inputStream = new FileInputStream(fileToAdd)
outputStream.putNextEntry(new ZipEntry(dirPrefix + fileToAdd.getName))
val buffer = new Array[Byte](1024)
var count = inputStream.read(buffer)
while (count > 0) {
outputStream.write(buffer, 0, count)
count = inputStream.read(buffer)
}
inputStream.close()
}
file.listFiles() foreach addFileToZip()
outputStream.close()
cleanDir(file)
}
def moveContentToJar(jarName: String): Unit = {
val newJar = jarsDir createFile s"$jarName.jar"
Jar.create(file = File(newJar), sourceDir = Directory(file), mainClass = "won't be used")
cleanDir(file)
}
def path: String = file.getAbsolutePath
}
private case class DirRep(name: String, nestedDirs: Seq[DirRep] = Nil, sourceFiles: Seq[String] = Nil)
private val compiler = new scala.tools.nsc.MainClass
private val appRunner = new scala.tools.nsc.MainGenericRunner
private val javaClassPath = sys.props("java.class.path")
// creates a test dir in a temporary dir containing compiled files of this test
// root dir will be automatically deleted after the end of test
private val rootDir = new JFile(sys.props("partest.output"))
private val testDir = rootDir createDir s"cp-tests-${System.currentTimeMillis()}"
private val jarsDir = testDir createDir "jars"
private val zipsDir = testDir createDir "zips"
private val srcDir = testDir createDir "src"
private val binDir = testDir createDir "bin"
private val outDir = testDir createDir "out"
def main(args: Array[String]): Unit = {
createClassesZipInZipsDir()
createClassesJarInJarsDir()
createClassesInBinDir()
createSourcesZipInZipsDir()
createSourcesJarInJarsDir()
createSourcesInSrcDir()
compileFinalApp()
runApp()
// at the end all created files will be deleted automatically
}
private def createClassesZipInZipsDir(): Unit = {
val baseFileName = "ZipBin"
createStandardSrcHierarchy(baseFileName)
compileSrc(baseFileName)
outDir moveContentToZip "Bin"
cleanDir(srcDir)
}
private def createClassesJarInJarsDir(): Unit = {
val baseFileName = "JarBin"
createStandardSrcHierarchy(baseFileName)
compileSrc(baseFileName)
outDir moveContentToJar "Bin"
cleanDir(srcDir)
}
private def createClassesInBinDir(): Unit = {
val baseFileName = "DirBin"
createStandardSrcHierarchy(baseFileName)
compileSrc(baseFileName, destination = binDir)
cleanDir(srcDir)
}
private def createSourcesZipInZipsDir(): Unit = {
createStandardSrcHierarchy(baseFileName = "ZipSrc")
srcDir moveContentToZip "Src"
}
private def createSourcesJarInJarsDir(): Unit = {
createStandardSrcHierarchy(baseFileName = "JarSrc")
srcDir moveContentToJar "Src"
}
private def createSourcesInSrcDir(): Unit = {
createStandardSrcHierarchy(baseFileName = "DirSrc")
val appFile = srcDir createSrcFile "Main"
appFile writeAll s"""import nested._
| object Main extends App {
| println(new ZipBin)
| println(new JarBin)
| println(new DirBin)
| println(new ZipSrc)
| println(new JarSrc)
| println(new DirSrc)
|
| println(new NestedZipBin)
| println(new NestedJarBin)
| println(new NestedDirBin)
| println(new NestedZipSrc)
| println(new NestedJarSrc)
| println(new NestedDirSrc)
| }
""".stripMargin
}
private def compileFinalApp(): Unit = {
val classPath = mkPath(javaClassPath, binDir.path, zipsDir.path + "/Bin.zip", jarsDir.path + "/Bin.jar")
val sourcePath = mkPath(srcDir.path, zipsDir.path + "/Src.zip", jarsDir.path + "/Src.jar")
compiler.process(Array("-cp", classPath, "-sourcepath", sourcePath,
"-d", outDir.path, s"${srcDir.path}/Main.scala"))
}
private def runApp(): Unit = {
val classPath = mkPath(javaClassPath, outDir.path, binDir.path, zipsDir.path + "/Bin.zip", jarsDir.path + "/Bin.jar")
appRunner.process(Array("-cp", classPath, "Main"))
}
private def createStandardSrcHierarchy(baseFileName: String): Unit =
createSources(RootPackage, srcDir,
DirRep("",
nestedDirs = Seq(DirRep("nested", sourceFiles = Seq("Nested" + baseFileName))),
sourceFiles = Seq(baseFileName)
)
)
private def createSources(pkg: String, dirFile: JFile, dirRep: DirRep): Unit = {
dirRep.nestedDirs foreach { rep =>
val nestedDir = dirFile createDir rep.name
val nestedPkg = PackageNameUtils.packagePrefix(pkg) + rep.name
createSources(nestedPkg, nestedDir, rep)
}
val pkgHeader = if (pkg == RootPackage) "" else s"package $pkg\n\n"
dirRep.sourceFiles foreach { srcName =>
val text = s"""${pkgHeader}case class $srcName(x: String = "")"""
val srcFile = dirFile createSrcFile srcName
srcFile writeAll text
}
}
private def compileSrc(baseFileName: String, destination: JFile = outDir): Unit = {
val srcDirPath = srcDir.path
compiler.process(Array("-cp", javaClassPath, "-d", destination.path,
s"$srcDirPath/$baseFileName.scala", s"$srcDirPath/nested/Nested$baseFileName.scala"))
}
private def cleanDir(dir: JFile): Unit =
dir.listFiles().foreach { file =>
if (file.isDirectory) cleanDir(file)
file.delete()
}
private def mkPath(pathEntries: String*) = pathEntries.mkString(File.pathSeparator)
}